次の方法で共有


チュートリアル: .NET アプリをコンテナー化する

このチュートリアルでは、Docker を使用して .NET アプリケーションをコンテナー化する方法について説明します。 コンテナーには、不変のインフラストラクチャであること、移植可能なアーキテクチャを提供すること、スケーラビリティを実現するなど、多くの機能と利点があります。 イメージを使用して、ローカル開発環境、プライベート クラウド、またはパブリック クラウド用のコンテナーを作成できます。

このチュートリアルでは、次の操作を行います。

  • 単純な .NET アプリを作成して発行する
  • .NET 用の Dockerfile を作成して構成する
  • Docker イメージをビルドする
  • Docker コンテナーを作成して実行する

.NET アプリケーションの Docker コンテナーのビルドタスクとデプロイ タスクについて説明します。 Docker プラットフォーム では、Docker エンジン を使用して、Docker イメージとしてアプリをすばやくビルドしてパッケージ化します。 これらのイメージは、Dockerfile 形式で書き込まれ、階層化されたコンテナーでデプロイおよび実行されます。

ヒント

Docker または Podman を必要とせずに .NET アプリをコンテナーとして発行することに関心がある場合は、「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 作業 名前を作業フォルダーとして使用します。

.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

コマンドは、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!");

ファイルを、1 秒ごとに数値をカウントする次のコードに置き換えます。

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 を使用して 5 個までカウントしてみてください。

重要

-- 後のパラメーターは、dotnet run コマンドには渡されず、代わりにアプリケーションに渡されます。

.NET アプリを発行する

アプリがイメージの作成に適するようにするには、コンパイルする必要があります。 dotnet publish コマンドは、アプリをビルドして発行する際に最も適しています。 詳細なリファレンスについては、dotnet build コマンドおよび dotnet publish コマンドのドキュメント を参照してください。

dotnet publish -c Release

アドバイス

Docker を必要とせずに .NET アプリをコンテナーとして発行することに関心がある場合は、「dotnet publishを使用して .NET アプリをコンテナー化する を参照してください。

dotnet publish コマンドは、アプリを 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 を作成する

Dockerfile ファイルは、docker build コマンドによってコンテナー イメージを作成するために使用されます。 このファイルは、拡張子のない Dockerfile 名前のテキスト ファイルです。

.csproj を含むディレクトリ Dockerfile という名前のファイルを作成し、テキスト エディターで開きます。 このチュートリアルでは、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 ドキュメント: ダイジェストでイメージをプルする」を参照してください。

ヒント

この Dockerfile では、ビルドを重ねて必要な成果物のみを残すことで、イメージの最終的なサイズを最適化するマルチステージ ビルドを使用します。 詳細については、「Docker Docs: マルチステージ ビルド」を参照してください。

FROM キーワードには、完全修飾 Docker コンテナー イメージ名が必要です。 Microsoft Container Registry (MCR、mcr.microsoft.com) は、パブリックにアクセス可能なコンテナーをホストする Docker Hub のシンジケートです。 dotnet セグメントはコンテナー リポジトリですが、sdk または aspnet セグメントはコンテナー イメージ名です。 イメージには、バージョン管理に使用される 9.0がタグ付けされています。 したがって、mcr.microsoft.com/dotnet/aspnet:9.0 は .NET 9.0 ランタイムです。 必ず、SDK の対象となるランタイムと一致するランタイム バージョンをプルしてください。 たとえば、前のセクションで作成したアプリは .NET 9.0 SDK を使用し、Dockerfile で参照される基本イメージには、9.0でタグ付けされています。

重要

Windows ベースのコンテナー イメージを使用する場合は、mcr.microsoft.com/dotnet/aspnet:9.0ではなく、mcr.microsoft.com/dotnet/aspnet:9.0-nanoserver-1809 など、単に 9.0を超えてイメージ タグを指定する必要があります。 Nano Server と Windows Server Core のどちらを使用しているか、およびその OS のバージョンに基づいてイメージ名を選択します。 サポートされているすべてのタグの完全なリストは、.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 セグメントはコンテナー リポジトリですが、sdk または aspnet セグメントはコンテナー イメージ名です。 イメージには、バージョン管理に使用される 8.0がタグ付けされています。 したがって、mcr.microsoft.com/dotnet/aspnet:8.0 は .NET 8.0 ランタイムです。 必ず、SDK の対象となるランタイムと一致するランタイム バージョンをプルしてください。 たとえば、前のセクションで作成したアプリは .NET 8.0 SDK を使用し、Dockerfile で参照される基本イメージには、8.0でタグ付けされています。

重要

Windows ベースのコンテナー イメージを使用する場合は、mcr.microsoft.com/dotnet/aspnet:8.0ではなく、mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809 など、単に 8.0を超えてイメージ タグを指定する必要があります。 Nano Server と Windows Server Core のどちらを使用しているか、およびその OS のバージョンに基づいてイメージ名を選択します。 .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.Docker.dllのホストとして dotnet を設定します。 ただし、代わりに、アプリの実行可能ファイル自体として ENTRYPOINT を定義し、アプリ ホストとして OS に依存することもできます。

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 レイヤーの publish コンテンツが App/out という名前のフォルダーに出力されるため、これがコピー元になります。 App/out ディレクトリ内のすべての発行済みコンテンツは、現在の作業ディレクトリ (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 ランタイム動作を構成する環境変数の COMPlus_ ではなく、プレフィックス DOTNET_ が標準化されています。 ただし、COMPlus_ プレフィックスは引き続き機能します。 以前のバージョンの .NET ランタイムを使用している場合でも、環境変数には COMPlus_ プレフィックスを使用する必要があります。

コンテナーを作成する

アプリを含むイメージが作成されたので、コンテナーを作成できます。 コンテナーは 2 つの方法で作成できます。 まず、停止している新しいコンテナーを作成します。

docker create --name core-counter counter-image

この docker create コマンドは、カウンター イメージの イメージに基づいてコンテナーを作成します。 docker create コマンドの出力には、コンテナーの CONTAINER ID が表示されます (識別子は異なります)。

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 start コマンドと docker attach コマンドを使用してコンテナを開始し、出力ストリームを確認します。 この例では、Ctrl + C キーストロークを使用して、実行中のコンテナーからデタッチします。 このキー操作では、特に指定しない限り、コンテナー内のプロセスが終了し、コンテナーが停止されます。 パラメーターを使用すると、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 コマンドを使用してコンテナーを削除し、2 回目に実行中のコンテナーを確認します。

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 には、コンテナーを 1 つのコマンドとして作成して実行するための docker run コマンドが用意されています。 このコマンドを使用すると、docker create を実行してから docker startする必要がなくなります。 このコマンドは、コンテナーが停止したときにコンテナーを自動的に削除するように設定することもできます。 たとえば、docker run -it --rm を使用して 2 つのことを行います。最初に、現在のターミナルを自動的に使用してコンテナーに接続し、コンテナーが完了したら削除します。

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 コマンドを変更し、そのコンテナーに対してのみ何かを実行することもできます。 たとえば、次のコマンドを使用して、bash または cmd.exeを実行します。 必要に応じてコマンドを編集します。

この例では、ENTRYPOINTcmd.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 イメージを削除します。 IMAGE ID または 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 コマンドを使用して、インストールされているイメージの一覧を表示します。

ヒント

イメージ ファイルは大きくなる場合があります。 通常は、アプリのテストと開発中に作成した一時コンテナーを削除します。 通常、そのランタイムに基づいて他のイメージをビルドする予定の場合は、ランタイムがインストールされた状態で基本イメージを保持します。

次の手順