使用 Dockerfile 建置映像檔時,每一行指令都會新增一層 layer ,這都會使映像檔變大。 又或者是我們會在 Dockerfile 裡面執行建置的工作 (這在 CI/CD 很常見),例如執行 dotnet publish 來產生網站的發佈檔,為了可以使用 dotnet publish 產生發佈檔,我們會使用有 dotnet cli SDK 工具的映像檔,例如 Image - .NET Core SDK ,這映像檔相對來說會比較大,但是將網站執行起來只需要 image - ASP.NET Core Runtime 的映像檔案就好,並不需要含有 SDK 工具的映像檔。

而建置映像檔的時候,我們都希望映像檔越小越好,在 Docker 版本 17.05.0-ce 新增了多階段建置 (multi-stage build) 的支援,能夠幫助我們簡單的處理這個問題。

前提概要

這次的範例會使用 dotnet cli 建立一個的 asp.net core mvc 專案範例,並在且 .NET Core SDK 映像檔產生發佈檔案,再複製發佈檔案到 ASP.NET Core Runtime 映像檔,最後將網站執行起來。

建立 asp.net core mvc 範例專案

先執行 dotnet new mvc -n=docker.lab 建立一個 asp.net core mvc 專案,然後在專案的資料夾底下新增一個 Dockerfile ,完成後專案資料夾大概會如下圖。 (我的 dotnet cli 版本是 2.2.204。)

initial_projectinitial_project

撰寫 Dockerfile

接下來請將底下的內容貼到 Dockerfile 。

dockerfile
  • dockerfile
1
2
3
4
5
6
7
8
9
FROM mcr.microsoft.com/dotnet/core-nightly/sdk:2.2 AS Builder
WORKDIR /app
COPY . .
RUN dotnet publish -o=dist --configuration=Release

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
COPY --from=Builder ./app/dist ./app
WORKDIR /app
ENTRYPOINT ["dotnet", "docker.lab.dll"]

第一段(the first stage)的 FromRUN dotnet publish,是使用含有 dotnet sdk 的映像檔,將專案的程式碼複製到容器底面,並且使用 dotnet publish 產生發佈檔案。

這裡一定要加入 WORKDIR /app 否則 dotnet publish 的時候會失敗。 請參考 ISSUE: dotnet publish fails when building in container

第二段(the second stage) 的 FromENTRYPOINT,是使用 .net core runtime 的映像檔,並且複製上一個映像檔產生的發佈檔,最後將網站執行起來。
其中 COPY --from=Builder ./app/dist ./app 我是使用 建立別名 的方式指定複製上一個映像檔的內容。 這裡再提供另一個方法,就是也可以使用 --from=0 取代 --from=Builder 來表示要複製上一個 stage 的內容。

建置映像檔

接著執行 docker build . -t dockerlab 建置映像檔案,執行成功的結果會如下圖。

docker_builddocker_build

然後在執行 docker run --rm -p 81:80 dockerlab 理論上網站就會跑起來了。
run_webrun_web

檢查映像檔的歷史

我們可以檢查映像檔的每一層 layer 紀錄,來確認是否沒有多餘的 layer。 執行 docker history dockerlab 查詢歷史紀錄,檢查的結果如下圖,可以證實,這個映像檔只有做了三件事情,1.複製檔案 2.指定 workdir 3.執行網站,並沒有多餘的執行過程在 layer 裡面。

docker_historydocker_history

小結

用多階段建置映像檔(multi-stage build) 的方式,就可以很輕易地建立出沒有多餘 layer 的映像檔 ,而且在 CI/CD 的情境底下也很好用的,是必學的建置方式!!

延伸閱讀

[Use multi-stage builds]
[Docker Hub - .NET Core SDK]
[Docker Hub - ASP.NET Core Runtime]
[dotnet cli - dotnet restore]
[dotnet cli - dotnet publish]