教學課程:容器化 .NET 應用程式

在此教學課程中,您將了解如何使用 Docker 來將 .NET 應用程式容器化。 容器有許多功能和優點,例如不可變的基礎結構、提供可攜式結構,以及啟用可擴縮性。 映像可用來為您的本機開發環境、私人雲端,或公用雲端建立容器。

在本教學課程中,您已:

  • 建立及發佈簡單的 .NET 應用程式
  • 建立及設定適用於 .NET 的 Dockerfile
  • 建置 Docker 映像
  • 建立及執行 Docker 容器

您將了解 .NET 應用程式的 Docker 容器建置及部署工作。 「Docker 平台」會使用「Docker 引擎」快速建置應用程式,並將其封裝為「Docker 映像」。 這些映像是以 Dockerfile 格式所撰寫,可在分層式容器中部署及執行。

注意

本教學課程不適用於 ASP.NET Core 應用程式。 如果您使用 ASP.NET Core,請參閱了解如何容器化 ASP.NET Core 應用程式教學課程。

必要條件

安裝下列先決條件:

  • .NET 8+ SDK
    如果您已安裝 .NET,則請使用 dotnet --info 命令來判斷您使用的是哪一個 SDK。
  • Docker Community Edition
  • Dockerfile 和 .NET 範例應用程式的暫存工作資料夾。 在本教學課程中,docker-working 名稱會作為工作資料夾使用。
  • .NET 7+ SDK
    如果您已安裝 .NET,則請使用 dotnet --info 命令來判斷您使用的是哪一個 SDK。
  • Docker Community Edition
  • Dockerfile 和 .NET 範例應用程式的暫存工作資料夾。 在本教學課程中,docker-working 名稱會作為工作資料夾使用。

建立 .NET 應用程式

您需要 Docker 容器執行的 .NET 應用程式。 開啟您的終端機,建立工作資料夾 (如果沒有),並進入該資料夾。 在工作資料夾中執行下列命令,在名為 App 的子目錄中建立新專案:

dotnet new console -o App -n DotNet.Docker

您的資料夾樹狀目錄會如下所示:

📁 docker-working
    └──📂 App
        ├──DotNet.Docker.csproj
        ├──Program.cs
        └──📂 obj
            ├── DotNet.Docker.csproj.nuget.dgspec.json
            ├── DotNet.Docker.csproj.nuget.g.props
            ├── DotNet.Docker.csproj.nuget.g.targets
            ├── project.assets.json
            └── project.nuget.cache

dotnet new 命令會建立名為 App 的新資料夾,並產生 "Hello World" 主控台應用程式。 從終端機會話變更目錄並瀏覽至 App 資料夾。 使用 dotnet run 命令來啟動應用程式。 應用程式會執行,並在命令下方列印 Hello World!

cd App
dotnet run
Hello World!

預設範本所建立的應用程式會列印到終端機,然後立即終止。 針對此教學課程,您會使用無限期執行迴圈的應用程式。 在文字編輯器中開啟 Program.cs 檔案。

提示

如果您使用 Visual Studio Code,請從上一個終端工作階段輸入下列命令:

code .

這會開啟包含專案 Visual Studio Code 的 App 資料夾。

Program.cs 看起來應該像下列 C# 程式碼:

Console.WriteLine("Hello World!");

使用下列每秒計算數字的程式碼來取代檔案:

var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
    Console.WriteLine($"Counter: {++counter}");
    await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
    Console.WriteLine($"Counter: {++counter}");
    await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}

儲存檔案,然後使用 dotnet run 再次測試程式。 請記住此應用程式會無限期執行。 使用取消命令 Ctrl + C 來停止它。 以下是一個範例輸出:

dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C

如果您在命令列上傳遞一個數字給應用程式,它將只會計算到該數量,然後結束。 搭配 dotnet run -- 5 試用它以計算到五。

重要

-- 之後的任何參數都不會傳遞至 dotnet run 命令,而會改為傳遞至您的應用程式。

發佈 .NET 應用程式

將 .NET 應用程式新增至 Docker 映像之前,必須先發佈。 最好讓容器執行已發佈的應用程式版本。 若要發佈應用程式,請執行下列命令:

dotnet publish -c Release

此命令會將您的應用程式編譯至 publish 資料夾。 從工作資料夾通往 publish 資料夾的路徑應該是 .\App\bin\Release\net8.0\publish\

此命令會將您的應用程式編譯至 publish 資料夾。 從工作資料夾通往 publish 資料夾的路徑應該是 .\App\bin\Release\net7.0\publish\

App 資料夾取得發佈資料夾的目錄清單,以確認已建立 DotNet.Docker.dll 檔案。

dir .\bin\Release\net8.0\publish\

    Directory: C:\Users\default\App\bin\Release\net8.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/22/2023  9:17 AM            431 DotNet.Docker.deps.json
-a---           9/22/2023  9:17 AM           6144 DotNet.Docker.dll
-a---           9/22/2023  9:17 AM         157696 DotNet.Docker.exe
-a---           9/22/2023  9:17 AM          11688 DotNet.Docker.pdb
-a---           9/22/2023  9:17 AM            353 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net7.0\publish\

    Directory: C:\Users\default\App\bin\Release\net7.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           2/13/2023  1:52 PM            431 DotNet.Docker.deps.json
-a---           2/13/2023  1:52 PM           6144 DotNet.Docker.dll
-a---           2/13/2023  1:52 PM         153600 DotNet.Docker.exe
-a---           2/13/2023  1:52 PM          11052 DotNet.Docker.pdb
-a---           2/13/2023  1:52 PM            253 DotNet.Docker.runtimeconfig.json

建立 Dockerfile

docker build 命令會使用 Dockerfile 檔案來建立容器映像。 此檔案是名為 Dockerfile 且沒有副檔名的文字檔案。

在包含 .csproj 的目錄中建立名為 Dockerfile 的檔案,然後在文字編輯器中開啟它。 本教學課程使用 ASP.NET Core 執行階段映像 (其中包含 .NET 執行階段映像),並與 .NET 主控台應用程式相對應。

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

注意

雖然可能已經使用映像 mcr.microsoft.com/dotnet/runtime:8.0,但在這裡刻意使用 ASP.NET Core 執行階段映像。

提示

Dockerfile 會使用多階段組建,藉由分層建置並只保留必要的成品,最佳化映像的最終大小。 如需詳細資訊,請參閱 Docker Docs:多階段組建

FROM 關鍵字需要完整的 Docker 容器映像名稱。 Microsoft Container Registry (MCR,mcr.microsoft.com) 是 Docker Hub 的一個分支,可裝載可公開存取的容器。 dotnet 區段是容器存放庫,而 sdkaspnet 區段則是容器映像名稱。 映像會以 8.0 標記,用於版本設定。 因此,mcr.microsoft.com/dotnet/aspnet:8.0 是 .NET 8.0 執行階段。 確定您所提取的執行階段版本符合您 SDK 以其為目標的執行階段。 例如,在上一節中建立的應用程式會使用 8.0 SDK 和 Dockerfile 中參考的基底映像加上 8.0 標記。

重要

使用 Windows 型容器映像時,您必須指定映像標記,而不僅僅是 8.0,例如,mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809 而不是 mcr.microsoft.com/dotnet/aspnet:8.0。 根據您使用的 Nano Server 或 Windows Server Core,以及該作業系統的版本來選取映像名稱。 您可以在 NET 的 Docker Hub 頁面上找到所有支援標記的完整清單。

儲存 Dockerfile 檔案。 工作資料夾的目錄結構應如下所示。 部分更下層的檔案和資料夾已省略,以節省文章空間:

📁 docker-working
    └──📂 App
        ├── Dockerfile
        ├── DotNet.Docker.csproj
        ├── Program.cs
        ├──📂 bin
        │   └──📂 Release
        │       └──📂 net8.0
        │           └──📂 publish
        │               ├── DotNet.Docker.deps.json
        │               ├── DotNet.Docker.exe
        │               ├── DotNet.Docker.dll
        │               ├── DotNet.Docker.pdb
        │               └── DotNet.Docker.runtimeconfig.json
        └──📁 obj
            └──...
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

注意

雖然可能已經使用 mcr.microsoft.com/dotnet/runtime:7.0 映像,但在這裡刻意使用 ASP.NET Core 執行階段映像。

提示

Dockerfile 會使用多階段組建,藉由分層建置並只保留必要的成品,最佳化映像的最終大小。 如需詳細資訊,請參閱 Docker Docs:多階段組建

FROM 關鍵字需要完整的 Docker 容器映像名稱。 Microsoft Container Registry (MCR,mcr.microsoft.com) 是 Docker Hub 的一個分支,可裝載可公開存取的容器。 dotnet 區段是容器存放庫,而 sdkaspnet 區段則是容器映像名稱。 映像會以 7.0 標記,用於版本設定。 因此,mcr.microsoft.com/dotnet/aspnet:7.0 是 .NET 7.0 執行階段。 確定您所提取的執行階段版本符合您 SDK 以其為目標的執行階段。 例如,在上一節中建立的應用程式會使用 7.0 SDK 和 Dockerfile 中參考的基底映像加上 7.0 標記。

儲存 Dockerfile 檔案。 工作資料夾的目錄結構應如下所示。 部分更下層的檔案和資料夾已省略,以節省文章空間:

📁 docker-working
    └──📂 App
        ├── Dockerfile
        ├── DotNet.Docker.csproj
        ├── Program.cs
        ├──📂 bin
        │   └──📂 Release
        │       └──📂 net7.0
        │           └──📂 publish
        │               ├── DotNet.Docker.deps.json
        │               ├── DotNet.Docker.exe
        │               ├── DotNet.Docker.dll
        │               ├── DotNet.Docker.pdb
        │               └── DotNet.Docker.runtimeconfig.json
        └──📁 obj
            └──...

從終端機執行下列命令:

docker build -t counter-image -f Dockerfile .

Docker 將會處理 Dockerfile 中的每一行。 命令中的 .docker build 設定映像的建置內容。 參數 -fDockerfile 的路徑。 此命令會建置映像,並建立名為 counter-image 的本機存放庫以指向該映像。 當此命令完成之後,執行 docker images 以查看已安裝的映像清單:

docker images
REPOSITORY                         TAG       IMAGE ID       CREATED          SIZE
counter-image                      latest    2f15637dc1f6   10 minutes ago   217MB

counter-image 存放庫是映像的名稱。 latest 標記是用來識別映像的標記。 2f15637dc1f6 是映像識別碼。 10 minutes ago 是映像的建立時間。 217MB 是映像的大小。 Dockerfile 的最後一個步驟是從映像建立容器並執行應用程式、將已發佈的應用程式複製到容器,以及定義進入點。

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
docker images
REPOSITORY                         TAG       IMAGE ID       CREATED          SIZE
counter-image                      latest    2f15637dc1f6   10 minutes ago   208MB

counter-image 存放庫是映像的名稱。 latest 標記是用來識別映像的標記。 2f15637dc1f6 是映像識別碼。 10 minutes ago 是映像的建立時間。 208MB 是映像的大小。 Dockerfile 的最後一個步驟是從映像建立容器並執行應用程式、將已發佈的應用程式複製到容器,以及定義進入點。

FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

COPY 命令會指示 Docker,將您電腦上指定的資料夾複製到容器中的資料夾。 在此範例中,會將 publish 資料夾複製到容器中名為 App/out 的資料夾。

WORKDIR 命令會將容器內的目前目錄變更為 App

下一個命令 ENTRYPOINT 會指示 Docker 將容器設定為以可執行檔的形式執行。 當容器啟動時,ENTRYPOINT 命令就會執行。 當此命令結束時,容器將會自動停止。

提示

在 .NET 8 之前,設定為以唯讀身分執行的容器可能會失敗,並顯示 Failed to create CoreCLR, HRESULT: 0x8007000E。 若要處理這個問題,請將 DOTNET_EnableDiagnostics 環境變數指定為 0 (在 ENTRYPOINT 步驟之前):

ENV DOTNET_EnableDiagnostics=0

如需多項 .NET 環境變數的詳細資訊,請參閱 .NET 環境變數

注意

.NET 6 會針對設定 .NET 執行階段行為的環境變數,透過前置詞 DOTNET_ (而非 COMPlus_) 進行標準化。 不過,COMPlus_ 前置詞將繼續運作。 如果使用舊版的 .NET 執行階段,則您仍應對環境變數使用 COMPlus_ 前置詞。

建立容器

您現在已有包含應用程式的映像,您可以建立一個容器。 您可以兩種方式建立容器。 首先,建立已停止的新容器。

docker create --name core-counter counter-image

docker create 命令會根據 counter-image 映像建立容器。 該命令的輸出會顯示已建立容器的容器識別碼 (您的映像識別碼將會不同):

d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf

若要查看「所有」容器的清單,請使用 docker ps -a 命令:

docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS    PORTS     NAMES
d0be06126f7d   counter-image   "dotnet DotNet.Docke…"   12 seconds ago   Created             core-counter

管理容器

容器是以特定名稱 core-counter 建立,此名稱是用來管理容器。 下列範例會使用 docker start 命令來啟動容器,然後使用 docker ps 命令只顯示正在執行的容器:

docker start core-counter
core-counter

docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS     NAMES
cf01364df453   counter-image   "dotnet DotNet.Docke…"   53 seconds ago   Up 10 seconds             core-counter

同樣地,docker stop 命令會停止容器。 下列範例會使用 docker stop 命令來停止容器,然後使用 docker ps 命令顯示沒有任何容器正在執行:

docker stop core-counter
core-counter

docker ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

連線到容器

當容器正在執行之後,您可以連線到它以查看輸出。 使用 docker startdocker attach 命令來啟動容器,並查看輸出資料流。 在此範例中,會使用 Ctrl + C 按鍵輸入來將執行中的容器中斷連結。 除非另有指定,否則此按鍵輸入會結束容器中的程序,這將停止容器。 --sig-proxy=false 參數可確保 CTRL+C 不會停止容器中的程序。

當您從容器中斷連結之後,請重新連結以確認它仍在執行且正在進行計算。

docker start core-counter
core-counter

docker attach --sig-proxy=false core-counter
Counter: 7
Counter: 8
Counter: 9
^C

docker attach --sig-proxy=false core-counter
Counter: 17
Counter: 18
Counter: 19
^C

刪除容器

在本文中,您不想讓容器閒置且不執行任何動作。 刪除您先前建立的容器。 如果容器正在執行,請停止它。

docker stop core-counter

下列範例會列出所有容器。 它接著會使用 docker rm 命令來刪除容器,然後第二次檢查任何執行中的容器。

docker ps -a
CONTAINER ID    IMAGE            COMMAND                   CREATED          STATUS                        PORTS    NAMES
2f6424a7ddce    counter-image    "dotnet DotNet.Dock…"    7 minutes ago    Exited (143) 20 seconds ago            core-counter

docker rm core-counter
core-counter

docker ps -a
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

單一執行

Docker 提供 docker run 命令來建立容器,並以單一命令執行。 使用此命令,就不需依序執行 docker createdocker start。 您也可以設定此命令,在容器停止時自動刪除容器。 例如,使用 docker run -it --rm 來執行兩個動作,首先,自動使用目前的終端機連線到容器,然後在容器完成時將其移除:

docker run -it --rm counter-image
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
^C

容器也會將參數傳遞至 .NET 應用程式的執行。 若要指示 .NET 應用程式只計算到 3,請傳入 3。

docker run -it --rm counter-image 3
Counter: 1
Counter: 2
Counter: 3

搭配 docker run -it,則 Ctrl+C 命令會停止正在容器中執行的程序,接著停止容器。 由於已提供 --rm 參數,因此會在程序停止時自動刪除容器。 確認它不存在:

docker ps -a
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

變更 ENTRYPOINT

docker run 命令也可讓您從 Dockerfile 修改 ENTRYPOINT 命令並執行其他動作,但只適用於該容器。 例如,使用下列命令來執行 bashcmd.exe。 視需要編輯命令。

在此範例中,ENTRYPOINT 會變更為 cmd.exe。 按下 Ctrl + C 以結束程序並停止容器。

docker run -it --rm --entrypoint "cmd.exe" counter-image

Microsoft Windows [Version 10.0.17763.379]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\>dir
 Volume in drive C has no label.
 Volume Serial Number is 3005-1E84

 Directory of C:\

04/09/2019  08:46 AM    <DIR>          app
03/07/2019  10:25 AM             5,510 License.txt
04/02/2019  01:35 PM    <DIR>          Program Files
04/09/2019  01:06 PM    <DIR>          Users
04/02/2019  01:35 PM    <DIR>          Windows
               1 File(s)          5,510 bytes
               4 Dir(s)  21,246,517,248 bytes free

C:\>^C

基本命令

Docker 有許多不同的命令,可建立、管理及與容器和映像互動。 這些 Docker 命令對於管理容器而言非常重要:

清除資源

在此教學課程中,您建立了容器與映像。 您可以視需要刪除這些資源。 使用下列命令

  1. 列出所有容器

    docker ps -a
    
  2. 停止依其名稱執行的容器。

    docker stop core-counter
    
  3. 刪除容器

    docker rm core-counter
    

接下來,在電腦上刪除您不再需要的任何映像。 刪除 Dockerfile 所建立的映像,然後刪除 Dockerfile 以其為基礎的 .NET 映像。 您可以使用映像識別碼存放庫:標記格式的字串。

docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:7.0

使用 docker images 命令來查看已安裝的映像清單。

提示

映像檔可能很大。 一般而言,會移除您在測試及開發應用程式時所建立的暫存容器。 如果您打算根據已安裝的執行階段建置其他映像,您通常會使用該執行階段來保存基底映像。

下一步