スキップしてメイン コンテンツに移動

【Day 17】マルチステージビルド(SDKイメージ vs 軽量Runtimeイメージの分離)

Day 17 アイキャッチ
▲ Day 17: マルチステージビルド(SDKイメージ vs 軽量Runtimeイメージの分離)
🐳
皆さん、こんにちは! Docker/コンテナ実践講座の専任講師です。
前回(Day 16)では、C# / ASP.NET Core Web API をコンテナ化するための「初めてのDockerfile構文(FROM, WORKDIR, COPY, RUN, ENTRYPOINT)」を学びました。
しかし、前回の単純なDockerfileには「イメージサイズが800MB超に肥大化してしまう」「本番環境にコンパイラやソースコードが丸見えで残ってしまう」という重大な課題がありました。
本日学ぶ『マルチステージビルド(Multi-stage builds)』は、この問題を完璧に解決し、現場のDockerイメージ作成において100%必須とされる最重要テクニックです!
開発環境と本番サーバーの分離を30年間実践してきたベテランエンジニアの皆様なら、その合理性に思わず膝を打つはずです。じっくりマスターしていきましょう!

① 導入・本日の達成目標

一般的なアプリケーション開発では、「ビルドに必要な道具(巨大なSDK、コンパイラ、開発用ヘッダー、NuGetキャッシュ)」と「本番で動かすために必要なもの(最小限のランタイムと発行済みDLL群)」は全く異なります。
もしビルド環境をそのまま本番コンテナに含めてしまうと、ディスク容量を圧迫するだけでなく、余計なツール類がサイバー攻撃の足場(攻撃対象領域: Attack Surface)となってしまいます。

本日の講義を終える頃には、以下の3つの目標を完全にクリアできるようになります。

  • マルチステージビルドの概念と、1つのDockerfile内で複数の FROM を使い分ける仕組みを論理的に理解できる。
  • 約800MBのSDKイメージを約100MBのRuntimeイメージへ劇的にスリム化し、セキュリティを大幅に高める理由を説明できる。
  • COPY --from を駆使して純粋な発行バイナリのみを抽出し、Dockerキャッシュを最大限に利かせる実践的なC# Dockerfileを自作できる。

② VM / Windowsサーバー技術者のためのパラダイムシフト

従来のWindows Server運用では、私たちは「ビルドサーバー」と「本番Webサーバー」を厳格に物理的・論理的に分離していました。

SDK環境 vs 実行環境の対比
▲ 【図解②】巨大な.NET SDK(約800MB) vs 極小の.NET Runtime(約100MB)の決定的な対比

TFS(Team Foundation Server)やJenkins専用のビルドマシンにVisual Studioや巨大な.NET SDKをインストールしてコンパイルを行い、出力されたDLL群だけをZIPやファイル共有経由で本番のIISサーバーへと配置(XCOPY / Web Deploy)していました。
「本番サーバーにコンパイラや開発環境を入れてはならない」というのは、安定稼働とセキュリティにおける絶対の掟だったのです。

Dockerのマルチステージビルドは、この「ビルドサーバーと本番サーバーの分離」という大掛かりなインフラ構築を、たった1つのDockerfileの中でスマートに再現する仕組みです!

📊 旧来のビルド/本番分離 と Dockerマルチステージビルド の対比表

比較項目 旧来:個別ビルドサーバー運用 現代:Docker マルチステージビルド
環境の分離方法 物理サーバーや別個のVMを用意して分離 1つのDockerfile内で複数 FROM を定義
成果物の受け渡し ネットワーク共有、ZIP転送、CIアーティファクト保存 COPY --from=build コマンドで瞬時に内部コピー
開発者PCでの再現性 ❌ ビルドサーバー固有のツール依存で手元再現が困難 ⭕ 手元でもCI上でも寸分違わぬ同一ビルドが完了
本番イメージサイズ OS含む数十GB(VHDX / 物理ディスク) 約100MB(ASP.NET Core Runtimeのみ)

③ マルチステージビルドの3ステップと基本構文

マルチステージビルドの全体像は、以下の3つのステップで成り立っています。

マルチステージビルドの3ステップ
▲ 【図解①】マルチステージビルドの3ステップフロー(ビルド ➡ 抽出 ➡ 極小実行環境)

Dockerfile内に FROM 命令を複数回書くと、それぞれの FROM は「全く新しい独立したステージ」を開始します。 重要なのは、「最終的に出力されるDockerイメージは、一番最後の FROM ステージの内容だけになる」という点です!
途中のステージ(ビルド用コンテナ)で生成された巨大なキャッシュや中間ファイルは、すべて自動的に使い捨て(破棄)されます。

🔑 構文の基本ルール:ステージに名前をつける(AS 句)

# ① 第1ステージ: 巨大な.NET SDKを使ってビルドを行う(ステージ名: build)
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
... ビルド・発行コマンド ...

# ② 第2ステージ: 最小限のASP.NET Runtimeを使って本番イメージを作る(ステージ名: final)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# 第1ステージから成果物だけをコピー!
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyWebApi.dll"]

④ 核心:成果物のみを抽出する「COPY --from」の魔法

マルチステージビルドの最大の武器が、COPY --from=<ステージ名> 命令です。

成果物のみを抽出する仕組み
▲ 【図解③】COPY --from による純粋成果物のみの抽出とクリーンな本番コンテナ生成

通常の COPY 命令は「ホストPC(Windows側)のファイル」をコンテナへコピーしますが、--from=build を指定すると、「先行する build ステージのコンテナ内部のファイル」を直接参照して、現在のステージへコピーできます。

これにより、以下のような劇的なメリットが生まれます。

  • ソースコードや.gitが本番に残らない: ソースファイル(.cs)やGit履歴はビルドステージにしか存在せず、本番イメージにはコンパイル済みのDLLと静的ファイルしか入りません。
  • NuGetキャッシュ・objフォルダの完全排除: 膨大な中間生成物はビルドステージと共に破棄されます。
  • 脆弱性(CVE)の激減: コンパイラやパッケージマネージャーなどの不要な実行可能バイナリが一切本番に入らないため、セキュリティスキャナ(Trivyなど)の検知件数が劇的に減少します。

⑤ 実践コード解説:ASP.NET Core 最適化Dockerfile完全版

それでは、現場で実際に利用されている「ビルドキャッシュを極限まで最適化したASP.NET Core Web APIのDockerfile完全版」を見ていきましょう。

📄 Dockerfile(マルチステージ&キャッシュ最適化構成)
# ========================================================
# ステージ1: ビルド環境(.NET 8 SDK / 約800MB)
# ========================================================
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# 【ポイント①】プロジェクトファイル(.csproj)だけを先にコピーして restore を実行
# ソースコードを変更しても、パッケージ追加がなければこの層はキャッシュされ一瞬で完了!
COPY ["MyWebApi.csproj", "./"]
RUN dotnet restore "MyWebApi.csproj"

# 【ポイント②】ソースコード全体をコピーして Release 発行
COPY . .
RUN dotnet publish "MyWebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false

# ========================================================
# ステージ2: 本番実行環境(ASP.NET Core 8 Runtime / 約100MB)
# ========================================================
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
EXPOSE 8080

# 【ポイント③】build ステージの発行成果物だけをクリーンに転送
COPY --from=build /app/publish .

# コンテナ起動コマンド
ENTRYPOINT ["dotnet", "MyWebApi.dll"]

⚡ なぜ COPY を2回に分けるのか?(Dockerキャッシュの極意)

上記のコードで最も重要なテクニックが、「.csproj だけを先にCOPYして dotnet restore を行い、その後にソースコード全体をCOPYする」という2段階の記述です。
Dockerは各行(命令)ごとにキャッシュを作成します。もし最初から COPY . . としてしまうと、C#コードを1文字修正しただけでもキャッシュが無効化され、毎回重い dotnet restore(NuGetダウンロード)が再実行されてしまいます。
プロジェクトファイルを分離することで、「パッケージ構成が変わらない限り、依存ライブラリの復元処理を完全スキップ(0秒キャッシュヒット)」させることが可能になるのです!

⑥ Git連携とどこでも動くビルド(CI/CDの恩恵)

マルチステージビルドを導入すると、開発チームやCI/CDパイプラインに絶大な恩恵がもたらされます。

Git連携とどこでも動くビルド
▲ 【図解④】Git連携とCI/CDパイプライン(ホストPCのSDK不要で再現性100%の自動ビルド)

最も驚くべきメリットは、「開発者のPCやCIサーバーに .NET SDK が一切インストールされていなくても、Git cloneして docker build を叩くだけで完璧なビルドができる」という点です!

💡 現場でよくある悲劇の撲滅:
「AさんのPCは .NET 8.0.100、Bさんは 8.0.204、CIサーバーは 8.0.101 で、マイナーバージョンの違いによってビルドエラーが発生する」という不毛なトラブルを経験したことはありませんか?
マルチステージビルドなら、コンパイラやビルドツールのバージョンがDockerfile内で sdk:8.0 として完全に固定されるため、地球上の誰がいつビルドしても寸分違わぬ同一成果物が保証されます!

⑦ ベテランがハマる落とし穴とベストプラクティス

罠① ソースコードをまとめて先にCOPYしてキャッシュを殺してしまう
前述の通り、COPY . . を最初に行ってしまうと、わずかなコード修正でも毎回NuGetパッケージのリストアが走り、ビルド時間が数分延びてしまいます。必ず「定義ファイル(.csproj)先行コピー ➡ dotnet restore」の順序を守りましょう。

罠② COPY --from のステージ名タイポ
FROM ... AS build と定義したステージ名は、COPY --from=build で大文字小文字を含めて正確に指定する必要があります。存在しないステージ名を指定すると「failed to solve: stage not found」というビルドエラーになります。

罠③ /p:UseAppHost=false の指定を忘れる
Linuxコンテナ向けのASP.NET Core発行では、/p:UseAppHost=false を指定してOS依存の実行ファイル(AppHost)を生成せず、純粋なクロスプラットフォームDLLを出力させるのが定石です。余計なファイルサイズを削り、移植性を高められます。

📌 本日のまとめ & 明日へのステップ
  • マルチステージビルドは、巨大なビルド環境(SDK)と軽量な実行環境(Runtime)を1つのDockerfile内で多段階に分離する技術。
  • FROM ... AS build でビルド作業を行い、COPY --from=build で発行成果物だけを本番ステージへ安全に抽出する。
  • イメージサイズを 800MB超から約100MBへ劇的に削減し、ソースコードや不要ツールの混入を防いでセキュリティを極大化できる。
  • .csproj の先行コピーと dotnet restore の分離により、Dockerレイヤーキャッシュを最大化して爆速ビルドを実現する。
  • ホストPCに .NET SDK が未インストールでも、Git cloneして docker build だけで完全動作するため、チーム開発とCI/CDの再現性が100%になる。
次回【Day 18】予告:
『.dockerignore の完全攻略』〜.git や bin/obj を除外してビルド転送量を劇的削減&ビルド爆速化〜

コメント

このブログの人気の投稿

【Perfume】Perfume 3rd Tour 「JPN」さいたまスーパーアリーナに行って参りました~

Perfume 3rd Tour 「JPN」1月28日、29日の2daysで参戦して参りました。 ファンクラブ会員の選考抽選会に28日、29日の両日で申し込んでいたんですが、両方とも当選していたという嬉しい誤算。 仕事だぁ、なんだかんだと紆余曲折ありまして28日の参戦は危ぶまれていたのですが、なんとかかんとか28日は40分遅刻しながらも参戦、29日はフル参戦する事が出来ました(∩´∀`)∩

Perfume、2025年末で“コールドスリープ(活動休止)”へ──東京ドーム2daysの記録と、公式・各界・ファンの声

  1) 公式発表・基本情報 活動休止(コールドスリープ)を正式発表 2025年9月21日、公式サイト「 Perfumeより 」で、2026年から一度コールドスリープに入ると発表。再始動の意思(“新しいPerfume”として戻る)も明記。 Perfume Official Site 今後の運用告知(配信・上映など) 同21日付の「 今後のPerfumeに関して 」で、9/23(火・祝)東京ドーム公演の LIVESHIP全世界生配信 および 全国ディレイビューイング 決定を案内。特設サイトも併記。 Perfume Official Site +1 公式グッズ **「Perfume Calendar 2026」**発売決定(B2壁掛け/表紙+2カ月1枚×6枚+ポスター)。テーマは“フルーツ”。 Perfume Official Site 2) 東京ドーム2days(9/22・23)要点 公演タイトル: ZO/Z5 Anniversary “ネビュラロマンス” Episode TOKYO DOME 。日程・開場/開演・会場・物販等の実務情報は特設で公開。 Perfume Official Site 9/23公演を世界配信(LIVESHIP) /ディレイビューイング実施。 Perfume Official Site 海外向け報道(英語)も「 indefinite hiatus 」として広く配信。 The Times of India 国内英字メディアも速報。 朝日新聞 3) 現地レポ( メリ爺 )から見える空気 あなたの現地記録(9/24公開)を、ファクトと現場情景の両面で反映: 両日参戦 。22日は アリーナ 、23日は 1階席から俯瞰 。前日の発表を受け、開演前のザワつき→MC後に“いつもの一体感”へ。開演は22日 18:45 頃。オープニングは**P³ツアー東京ドーム(2020/2)**と同趣向で“回収”の演出。 メリ爺のゲーム万歳 MC所感 :解散ではなく前向きな休止で不安を和らげる内容。 かしゆか の涙、 のっち の気丈さ、 あ〜ちゃん の思い出語り──3人の結束と「 また戻る 」約束が強く刻まれた。 メリ爺のゲーム万歳 体感時間 : “3時間弱”級のボリューム ながら“過去一...

ニンテンドースイッチでオンラインゲームを遊ぶには?

モンスターハンターライズが発売になって、家族でモンハン楽しみたい人が増えたみたいですね。 某ブログの検索キーワードを調べると、モンハンを家族でやるにはどうしたらいいのか?を調べている人が沢山いるのがわかります。 ※これでも一部  家族で協力するにはSwitch2台、ソフトも2つ購入する必要があります。 1台のスイッチでは遊べませんし、ソフト1つでも遊べません。 でも、1台のスイッチで別々アカウントがあれば交代で遊ぶこともできますよ。セーブデータもしっかり分かれます。 Switchのオンラインゲームを家族で遊ぶ方法については以下の記事にまとめてますのでよろしければ参考にしてみてくださいね。 家族でモンスターハンターライズDL版のオンラインプレーを楽しむ方法