mcpField
D
dotnet-containers

dotnet-containers

github.com/wshaddixクラウド・DevOps8687 回インストール35 週間前に更新
リポジトリ

dotnet-containers について

.NETアプリをコンテナ化するための実践ガイドです。Dockerfileやdotnet publishによるイメージ作成、イメージとレイヤーの選択、非root実行、ヘルスチェックを扱います。

主な機能

  1. 01マルチステージDockerfileのパターン
  2. 02dotnet publishによるコンテナイメージ作成
  3. 03イメージ選択とレイヤーキャッシュ
  4. 04非rootコンテナの設定
  5. 05コンテナのヘルスチェック指針

活用シーン

  1. 01.NET APIのコンテナ化
  2. 02レイヤーキャッシュによる再ビルドの削減
  3. 03非root権限での.NETコンテナ実行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-containers

インストール後、Claude Code で/dotnet-containersを入力して読み込まれたか確認します。

詳細

dotnet-containers

.NET アプリケーションをコンテナー化するためのベストプラクティスです。マルチステージ Dockerfile のパターン、dotnet publish のコンテナーイメージ機能(.NET 8 以降)、ルートレスコンテナーの構成、最適化されたレイヤーキャッシュ、コンテナーのヘルスチェックを扱います。

対象外: DI コンテナーの仕組みとサービスの有効期間については、[skill:dotnet-csharp-dependency-injection] を参照してください。Kubernetes のデプロイマニフェストと Docker Compose によるオーケストレーションについては、[skill:dotnet-container-deployment] で扱っています。イメージのビルドとプッシュに関する CI/CD パイプラインとの統合については、[skill:dotnet-gha-publish] と [skill:dotnet-ado-publish] を参照してください。コンテナー化されたアプリケーションのテストについては、Testcontainers のパターンを扱う [skill:dotnet-integration-testing] を参照してください。

相互参照:ヘルスチェックのパターンについては [skill:dotnet-observability]、Kubernetes へのコンテナーのデプロイと Compose を使ったローカル開発については [skill:dotnet-container-deployment]、ビルド出力を一元化するレイアウトを使用する場合の Dockerfile パスの調整については [skill:dotnet-artifacts-output] を参照してください。


マルチステージ Dockerfile

マルチステージビルドでは、ビルド環境とランタイム環境を分離し、最小限の最終イメージを作成します。

標準的なマルチステージパターン
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# Copy project files first for layer caching
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
RUN dotnet restore "src/MyApi/MyApi.csproj"

# Copy everything else and build
COPY . .
WORKDIR "/src/src/MyApi"
RUN dotnet publish -c Release -o /app/publish --no-restore

# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
EXPOSE 8080

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
レイヤーキャッシュの戦略

変更頻度の低いものから高いものの順に COPY 命令を記述します。

  1. プロジェクトファイルと props -- 依存関係が変更されたときのみ変更されます
  2. dotnet restore -- プロジェクトファイルが変更されるまでキャッシュされます
  3. ソースコード -- ビルドのたびに変更されます
  4. dotnet publish -- ソースまたは復元レイヤーが変更された場合にのみ実行されます
# Good: restore layer is cached when only source changes
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
RUN dotnet restore
COPY . .
RUN dotnet publish

# Bad: restore runs on every source change
COPY . .
RUN dotnet restore
RUN dotnet publish
ソリューション全体の復元

複数プロジェクトのソリューションでは、すべての .csproj ファイルとソリューションファイルをコピーして、1 回の復元で済むようにします。

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# Copy solution and all project files for restore caching
COPY ["MyApp.sln", "."]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["src/MyApi.Infrastructure/MyApi.Infrastructure.csproj", "src/MyApi.Infrastructure/"]
RUN dotnet restore

COPY . .
RUN dotnet publish "src/MyApi/MyApi.csproj" -c Release -o /app/publish --no-restore

dotnet publish コンテナーイメージ(.NET 8 以降)

.NET 8 以降では、Dockerfile を使わずに dotnet publish から OCI コンテナーイメージを直接作成できます。これは Microsoft.NET.Build.Containers SDK(.NET SDK に含まれています)を使用します。

基本的な使い方
# Publish as a container image to local Docker daemon
dotnet publish --os linux --arch x64 /t:PublishContainer

# Publish to a remote registry
dotnet publish --os linux --arch x64 /t:PublishContainer \
  -p:ContainerRegistry=ghcr.io \
  -p:ContainerRepository=myorg/myapi
MSBuild の構成

.csproj でコンテナーのプロパティを構成します。

<PropertyGroup>
  <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0</ContainerBaseImage>
  <ContainerImageName>myapi</ContainerImageName>
  <ContainerImageTag>$(Version)</ContainerImageTag>
</PropertyGroup>

<ItemGroup>
  <ContainerPort Include="8080" Type="tcp" />
</ItemGroup>
高度な構成
<PropertyGroup>
  
  <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled</ContainerBaseImage>

  
  <ContainerUser>app</ContainerUser>
</PropertyGroup>

<ItemGroup>
  
  <ContainerEnvironmentVariable Include="ASPNETCORE_URLS" Value="http://+:8080" />
  <ContainerEnvironmentVariable Include="DOTNET_RUNNING_IN_CONTAINER" Value="true" />

  
  <ContainerLabel Include="org.opencontainers.image.source" Value="https://github.com/myorg/myapi" />
</ItemGroup>
dotnet publish と Dockerfile の使い分け
シナリオ推奨
シンプルな単一プロジェクトの APIdotnet publish /t:PublishContainer -- 定型コードが少なくて済みます
ネイティブ依存関係を含むマルチステージビルドDockerfile -- ビルド環境を完全に制御できます
OS パッケージ(例:libgdiplus)のインストールが必要Dockerfile -- SDK による publish では RUN apt-get install を利用できません
複雑なビルド手順を伴う CI/CDDockerfile -- 明示的で再現性があります
ローカルでのコンテナーの簡易テストdotnet publish /t:PublishContainer -- 最も迅速に反復できます

ベースイメージの選択

公式 .NET コンテナーイメージ
イメージ用途サイズ
mcr.microsoft.com/dotnet/aspnet:10.0ASP.NET Core アプリ(Ubuntu)~220 MB
mcr.microsoft.com/dotnet/aspnet:10.0-alpineASP.NET Core アプリ(Alpine、より小型)~110 MB
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseledディストロレス(シェルなし、パッケージマネージャーなし)~110 MB
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extraChiseled + グローバリゼーション + タイムゾーン~130 MB
mcr.microsoft.com/dotnet/runtime:10.0コンソールアプリ、ワーカーサービス~190 MB
mcr.microsoft.com/dotnet/runtime-deps:10.0自己完結型/AOT アプリ(ランタイム不要)~30 MB
ベースイメージの選び方
  • デフォルト: Web アプリには aspnet、ワーカーサービスには runtime を使用します
  • 最小サイズ: chiseled バリアントを使用します(シェルなし、root ユーザーなし、パッケージマネージャーなし)
  • グローバリゼーションが必要: アプリでカルチャ固有の書式設定やタイムゾーンを使用する場合は、chiseled-extra を使用します
  • 自己完結型または AOT: ランタイムがアプリにバンドルされるため、runtime-deps を使用します
  • Alpine: Ubuntu より小型ですが、musl libc を使用します。ネイティブ依存関係との互換性をテストしてください

ルートレスコンテナー

コンテナーを非 root ユーザーで実行すると、攻撃対象領域を減らせます。.NET 8 以降の chiseled イメージは、デフォルトで非 root ユーザーとして実行されます。

標準イメージで非 root として実行する
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app

# Create non-root user and switch to it
RUN adduser --disabled-password --gecos "" --uid 1001 appuser
USER appuser

COPY --from=build --chown=appuser:appuser /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
chiseled イメージで非 root として実行する

chiseled イメージには、設定済みの app ユーザー(UID 1654)が含まれています。追加の設定は不要です。

FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app
# Already runs as non-root 'app' user (UID 1654)

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
ポートの設定

非 root ユーザーは 1024 未満のポートにバインドできません。ASP.NET Core はコンテナー内でデフォルトのポート 8080 を使用します(ASPNETCORE_HTTP_PORTS で設定)。

# Default in .NET 8+ container images -- no explicit config needed
# ASPNETCORE_HTTP_PORTS=8080

# If you need a different port:
ENV ASPNETCORE_HTTP_PORTS=5000
EXPOSE 5000

コンテナーのヘルスチェック

ヘルスチェックを使用すると、コンテナーランタイムでアプリケーションの準備状況を監視できます。アプリケーションレベルのヘルスチェックエンドポイント([skill:dotnet-observability] を参照)は、Docker と Kubernetes のプローブで使用されます。

Docker HEALTHCHECK
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app

# Health check using curl (not available in chiseled images)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:8080/health/live || exit 1

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]

chiseled イメージ(curl なし)の場合は、専用のヘルスチェックバイナリを使用するか、オーケストレーター側のプローブ(Kubernetes の httpGet、Docker Compose の test)を利用してください。

FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app

# No HEALTHCHECK directive -- use orchestrator probes instead
# See [skill:dotnet-container-deployment] for Kubernetes probe configuration

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
ヘルスチェックエンドポイント

アプリケーションにヘルスチェックエンドポイントを登録してください(詳しい手順は [skill:dotnet-observability] を参照)。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks()
    .AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["live"])
    .AddNpgSql(
        builder.Configuration.GetConnectionString("DefaultConnection")!,
        name: "database",
        tags: ["ready"]);

var app = builder.Build();

app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("live")
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

コンテナーの最適化

.dockerignore

ビルドコンテキストから不要なファイルを除外するため、必ず .dockerignore を含めてください。

**/.git
**/.vs
**/.vscode
**/bin
**/obj
**/node_modules
**/*.user
**/*.suo
**/Dockerfile*
**/docker-compose*
**/.dockerignore
**/README.md
**/LICENSE
グローバリゼーションとタイムゾーン

アプリでグローバリゼーション(カルチャー固有の書式設定、タイムゾーン)のサポートが必要な場合は、ICU を設定してください。

# Option 1: Use the chiseled-extra image (includes ICU + tzdata)
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra

# Option 2: Disable globalization for smaller images (if not needed)
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
メモリ制限

.NET がコンテナーのメモリ制限を尊重するように設定してください。

# .NET automatically detects container memory limits and adjusts GC heap size.
# Override only if needed:
ENV DOTNET_GCHeapHardLimit=0x10000000  # 256 MB hard limit

.NET は cgroup のメモリ制限を自動的に読み取ります。GC はヒープサイズを調整し、コンテナーのメモリ予算内に収まるようにします。特別な理由がない限り、DOTNET_GCHeapHardLimit の設定は避けてください。

ReadOnlyRootFilesystem

多層防御のため、ルートファイルシステムを読み取り専用にして実行してください。一時ファイル用に書き込み可能なパスを確保してください。

ENV DOTNET_EnableDiagnostics=0
# Or mount a tmpfs at /tmp for diagnostics support

主な原則

  • マルチステージビルドを使用する -- ビルドツールを最終イメージに含めないようにします
  • レイヤーキャッシュを考慮して COPY の順序を決める -- ソースコードより前にプロジェクトファイルをコピーして restore を実行します
  • 本番環境では chiseled イメージを優先する -- シェルなし、root なしで、攻撃対象領域を最小限に抑えます
  • シンプルなプロジェクトでは dotnet publish /t:PublishContainer を使用する -- Dockerfile の定型記述を省略できます
  • 非 root ユーザーとして実行する -- USER ディレクティブまたは chiseled イメージ(デフォルトで非 root)を使用します
  • ヘルスチェックエンドポイントを設定する -- オーケストレーターがアプリケーションの状態を監視できるようにします([skill:dotnet-observability] を参照)
  • .dockerignore を含める -- ビルドコンテキストを小さく保ち、シークレットを除外します

エージェントが注意すべき点

  1. 最終イメージに mcr.microsoft.com/dotnet/sdk を使用しないでください -- SDK イメージは 800 MB を超え、ビルドツールも含まれています。最終ステージには必ず aspnet、runtime、または runtime-deps を使用してください。
  2. イメージタグをパッチバージョンに固定しないでください(例:10.0.1)-- セキュリティパッチを受け取るには 10.0 を使用してください。特定の互換性要件がある場合に限り、パッチバージョンに固定してください。
  3. chiseled イメージで HEALTHCHECK を使用しないでください -- chiseled イメージには curl もシェルもありません。代わりに、オーケストレーター側のプローブ(Kubernetes の httpGet、Docker Compose の test)を使用してください。
  4. dotnet restore の後に dotnet publish を実行する際は、--no-restore を忘れないでください -- これを指定しないと restore が再度実行され、レイヤーキャッシュが機能しなくなります。
  5. 非 root コンテナーで 1024 未満のポートにバインドしないでください -- .NET はコンテナーイメージでデフォルトのポート 8080 を使用します。ASPNETCORE_HTTP_PORTS を上書きする場合は、ポートが 1024 以上であることを確認してください。
  6. .dockerignore を省略しないでください -- 省略すると、ビルドコンテキストに .git、bin/obj、さらにシークレットが含まれる可能性があり、ビルド時間とイメージサイズが増加します。

参考資料

コメント