共用方式為


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

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

在本教學課程中,您會:

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

您可以探索 .NET 應用程式的 Docker 容器建置和部署工作。 Docker 平臺 使用 Docker 引擎 快速建置和封裝應用程式為 Docker 映像。 這些映像會以 Dockerfile 格式撰寫,以在分層容器中部署和執行。

提示

如果您想要將 .NET 應用程式發佈為容器,而不需要 Docker 或 Podman,請參閱使用 dotnet publish將 .NET 應用程式容器化

注意

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

先決條件

安裝下列必要條件:

  • .NET 8+ 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再次測試程式。 請記住,此應用程式會無限期執行。 使用 cancel 命令 Ctrl+C 來停止它。 請考慮下列範例輸出:

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

如果您將命令行上的數位傳遞給應用程式,它會將計數限制為該數量,然後結束。 嘗試使用 dotnet run -- 5 從一數到五。

重要

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

發佈 .NET 應用程式

為了讓應用程式適合建立映像,它必須編譯。 dotnet publish 命令最適合此命令,因為它會建置和發佈應用程式。 如需深入參考,請參閱 dotnet builddotnet publish 命令檔。

dotnet publish -c Release

提示

如果您想要將 .NET 應用程式發佈為容器,而不需要 Docker,請參閱使用 dotnet publish將 .NET 應用程式容器化

dotnet publish 命令會將您的應用程式編譯至 publish 資料夾。 工作資料夾中 發佈 資料夾的路徑應 ./App/bin/Release/<TFM>/publish/

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

dir .\bin\Release\net9.0\publish\

    Directory: C:\Users\default\docker-working\App\bin\Release\net9.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          1/6/2025  10:11 AM            431 DotNet.Docker.deps.json
-a----          1/6/2025  10:11 AM           6144 DotNet.Docker.dll
-a----          1/6/2025  10:11 AM         145408 DotNet.Docker.exe
-a----          1/6/2025  10:11 AM          11716 DotNet.Docker.pdb
-a----          1/6/2025  10:11 AM            340 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net8.0\publish\

    Directory: C:\Users\default\docker-working\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

建立 Dockerfile

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

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

FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /App

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

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

注意

此處刻意使用 ASP.NET Core 運行時映像,不過可以改用 mcr.microsoft.com/dotnet/runtime:9.0 映像。

FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /App

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

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

注意

此處特意使用 ASP.NET Core 運行時映像,不過可以改用 mcr.microsoft.com/dotnet/runtime:8.0 映像。

重要

Dockerfile 中的映像標籤後面加入安全雜湊演算法(SHA)是最佳做法。 這可確保映像不會遭到竄改,而且映像與您預期的映像相同。 SHA 是影像的唯一標識碼。 如需詳細資訊,請參閱 Docker Docs:依摘要提取映射。

提示

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

FROM 關鍵詞需要完整且符合資格的 Docker 容器映像名稱。 Microsoft Container Registry (MCR,mcr.microsoft.com) 是 Docker Hub 的一部分,承載可以公開存取的容器。 dotnet 區段是容器存放庫,而 sdkaspnet 區段則是容器映像名稱。 圖像被標記為 9.0,用於版本控制。 因此,mcr.microsoft.com/dotnet/aspnet:9.0 是 .NET 9.0 運行時間。 請確保您下載的執行時間版本與您的 SDK 所針對的執行時間版本一致。 例如,在上一節中建立的應用程式使用 .NET 9.0 SDK,而 Dockerfile 中參考的基底映射會以 9.0標記。

重要

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

儲存 Dockerfile 檔案。 工作資料夾的目錄結構看起來應該如下所示。 一些更深層的檔案和資料夾會省略,以節省文章中的空間:

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

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 所針對的執行時間相匹配。 例如,在上一節中建立的應用程式使用 .NET 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
            └──...

ENTRYPOINT 指令會將 dotnet 設定為 DotNet.Docker.dll的主機。 不過,可以改為將 ENTRYPOINT 定義為應用程式可執行檔本身,並依賴操作系統作為應用程式主機:

ENTRYPOINT ["./DotNet.Docker"]

這會導致應用程式直接執行,而不需要 dotnet,而是依賴應用程式主機和基礎OS。 如需部署跨平臺二進位檔的詳細資訊,請參閱 產生跨平臺二進位

若要從終端機建置容器,請執行下列命令:

docker build -t counter-image -f Dockerfile .

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

REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    1c1f1433e51d   32 seconds ago   223MB
docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    2f15637dc1f6   10 minutes ago   217MB

counter-image 存放庫是映像的名稱。 此外,映像標籤、映像標識碼、大小和建立時間都是輸出的一部分。 Dockerfile 的最後步驟是從映像建立容器並執行應用程式、將已發佈的應用程式複製到容器,以及定義進入點:

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

FROM 命令會指定要使用的基底映像和標記。 WORKDIR 命令會將容器內部的 目前目錄 變更為 App

COPY 命令會指示 Docker 將指定的來源目錄複製到目的地資料夾。 在此範例中,發佈的 內容會從 build 層輸出到名為 App/out的資料夾,因此該資料夾就是用來複製的來源。 App/out 目錄中所有已發佈的內容都會複製到目前的工作目錄中(應用程式)。

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

提示

在 .NET 8 之前,設定為以唯讀身分執行的容器可能會因為 Failed to create CoreCLR, HRESULT: 0x8007000E而失敗。 若要解決此問題,請將 DOTNET_EnableDiagnostics 環境變數指定為 0 (就在 ENTRYPOINT 步驟之前):

ENV DOTNET_EnableDiagnostics=0

如需各種 .NET 環境變數的詳細資訊,請參閱.NET 環境變數。

注意

.NET 6 將標準化使用前置詞 DOTNET_,以替代在設定 .NET 運行時行為的環境變數中使用的 COMPlus_。 不過,COMPlus_ 前綴會繼續運作。 如果您使用舊版的 .NET 執行階段,您仍應該使用環境變數的 COMPlus_ 前綴。

建立容器

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

docker create --name core-counter counter-image

這個 docker create 命令會根據 計數器映像 映射來建立容器。 docker create 命令的輸出會顯示容器的 容器識別碼(您的識別元會不同):

d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf

若要查看所有 容器 清單,請使用 命令:

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 create,然後 docker 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

注意

此範例僅適用於 Windows 容器。 Linux 容器沒有 cmd.exe

基本命令

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

清除資源

在本教學課程中,您已建立容器和映像。 如果您想要,請刪除這些資源。 使用下列命令

  1. 列出所有容器

    docker ps -a
    
  2. 停止正在執行的容器,依名稱識別。

    docker stop core-counter
    
  3. 刪除容器

    docker rm core-counter
    

接下來,刪除計算機上不再需要的任何映像。 刪除 Dockerfile 所建立的映射,然後刪除 Dockerfile 所依據的 .NET 映射。 您可以使用 映射標識碼REPOSITORY:TAG 格式化字串。

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

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

提示

圖像檔可能很大。 一般而言,您會移除您在測試及開發應用程式時建立的暫存容器。 如果您打算根據該運行時間建置其他映像,通常會讓基底映像與運行時間一起安裝。

後續步驟