如何將 .NET Core App 打包成 Docker Image ?
Docker 除了用在測試外,尚可將整個 app 打包成 Docker image,配合 Docker Compose 與其他 Microservice,可以直接用在 production。
Version
macOS High Sierra 10.13.4
Docker for Mac 18.03-ce-mac65 (24312)
.NET Core 2.0.7
VS Code 1.23.1
建立 Console App
1 | $ dotnet new console -o MyConsole |
使用 dotnet new 建立 .NET Core App。
- new : 建立 project
- console : 建立 console 類型 project
- -o : 以 MyConsole 為專案名稱並建立目錄

- 輸入
dotnet new console -o MyConsole建立MyConsole專案,其專案類型為console app
建立 Dockerfile
由於我們想要將 app 打包成 Docker image,因此要建立自己的 Dockerfile,Docker 才能依照此 Dockerfile 建立 Docker image。
Dockerfile
1 | FROM microsoft/dotnet:runtime-deps |
第 1 行
1 | FROM microsoft/dotnet:runtime-deps |
FROM 為 Dockerfile 指令,表示以哪一個 image 為基礎建立自己的 image。
其中 microsoft/dotnet:runtime-deps 是 Microsoft 專為 .NET Core 準備的 Docker image,此 image 不含 .NET Core runtime,僅包含所需要的 Linux library,是最小包的 image。
第 2 行
1 | WORKDIR /app |
WORKDIR 為 Dockerfile 指令,表示其他 Dockerfile 指令 (如 RUN、CMD、ENTRYPOINT、COPY 與 ADD …等) 的工作目錄。
以上表示 WORKDIR 為 /app,若 /app 目錄不存在會自動建立。
第 4 行
1 | COPY ./bin/Release/netcoreapp2.0/linux-x64/publish ./ |
COPY 為 Dockerfile 指令,表示將 Host OS 的檔案或目錄,複製到 container 內。
第 1 個參數為 Host OS 檔案或目錄,第 2 個參數為 container 檔案或目錄。
以上表示僅將以 SCD 方式發布的 Linux App 複製到 container 內。
第 5 行
1 | ENTRYPOINT ["./MyConsole"] |
ENTRYPOINT 為 Dockerfile 指令,表示 container 一啟動時,該執行什麼指令。
以上表示當 container 一啟動時,將執行 ./MyConsole。

建立 Image
BuildDocker.sh
1 | #!/bin/bash |
由於使用的 Docker image 是 microsoft/dotnet:runtime-deps,這表示我們必須使用 SCD 方式發布,dotnet publish 特別加上 -r linux-x64 使用 SCD,其 runtime 為 linux-x64。
使用 docker build 由 Dockerfile 建立 Docker image。
- -t :
tag,image 的名稱

1 | ~/MyConsole $ chmod +x ./BuildDocker.sh |
使用 chmod 讓 BuildDocker.sh 能夠有 被執行 權限。

1 | ~/MyConsole $ ./BuildDocker.sh |
執行 BuildDocker.sh 建立 Docker image。

1 | ~/MyConsole $ docker images |
確認 myconsole image 已經被產生。

建立 Docker Compose
docker-compose.yml
1 | version: "3" services: net-core: image: myconsole container_name: MyConsole |
第 5 行
1 | image: myconsole |
使用自己剛剛建立的 mycosole image。
第 6 行
1 | container_name: MyConsole |
建立名為 MyConsole 的 container。

執行 Docker Compose
1 | ~/MyConsole $ docker-compose up |
執行 docker-compose up 執行 docker-compose.yml 所定義的 container。

正確顯示 Hello World,表示 .NET Core App 已在 Docker 內執行成功。
結束 Docker Compose
1 | ~/MyProject $ docker-compose down |
執行 docker-compose down 結束並刪除 container。

Summary
在 Microsoft 官方的 Learn Docker Basics with .NET Core 文件中,其 Dockerfile 如下:
1 | FROM microsoft/dotnet:2.0-sdk |
第 1 行
1 | FROM microsoft/dotnet:2.0-sdk |
FROM 為 microsoft/dotnet:2.0-sdk,也就是包含了 .NET Core CLI,所以 image 一定比較大。
第 4 行
1 | # copy csproj and restore as distinct layers |
將所有 source code 都複製進 image,在 docker build 時,在 image 內執行 dotnet restore 與 dotnet publish,由於所有 source code 都進了 image,image 也一定比較大。
本文是 Host OS 以 SCD 方式 publish,僅將所需要的檔案複製進 image,如此 image 會小很多。

實際比較發現,若使用 Microsoft 官方作法,Docker image 為 1.84 GB,但使用本文方式,只有 218 MB,非常適合 production 使用。
Conclusion
- 將 .NET Core console app 打包成 Docker image 後,就可以跨平台執行此 image
- 此範例雖然是 console app,事實上將 ASP.NET Core 打包成 Docker image 也是類似以上流程
- 使用本文的方式,Docker image 的 size 將會有效的縮小
Sample Code
完整的範例可以在我的 GitHub 上找到