前回は「私のPCでは動くのに問題」の真因である環境ドリフトと、それを根絶するイミュータブルインフラの概念を学びました。
第4回目の本日は、Dockerの世界を自在に操るための最重要基礎である「3大要素(Dockerfile / Image / Container)」と、コンテナの「ライフサイクル(生成から破棄まで)」を徹底解説します!
① 導入・本日の達成目標
「Dockerのコマンドは色々あるけれど、イメージとコンテナの違いが曖昧…」
「コンテナを止めたら中のデータはどうなるの?削除したらどうやって復元するの?」
長年VMwareやHyper-Vで仮想ハードディスク(VHDX/VMDK)やスナップショットを扱ってきたエンジニアほど、Dockerの「実体の捉え方」で混乱しがちです。
本日の講義をマスターすると、以下の3つの重要概念がスッキリ腑に落ちます。
- Dockerfile・Image・Containerの3段階の変換関係を完璧に整理・理解する。
- コンテナの4段階ライフサイクル(Create ➡ Run ➡ Stop ➡ Rm)と状態遷移を掴む。
- レイヤー構造とビルドキャッシュ、そしてC#のオブジェクト指向(クラスとインスタンス)を通じた直感理解を深める。
② Dockerの3大要素:設計図・金型・実体
Dockerを構成する根幹は、以下の3つの要素です。この3者の関係性は、「ビルド(Build)」と「実行(Run)」という2つのアクションで結ばれています。
ベースOS、必要なランタイム(.NET 8 SDKなど)、コピーするソースコード、実行コマンドなどを手順として記述したプレーンテキストファイルです。Gitリポジトリでコードと一緒にバージョン管理します。
Dockerfileを docker build して生成される、読み取り専用(Read-Only)の不変パッケージです。アプリケーション本体と依存ライブラリが完全に固められており、Docker HubやGitHub Packages(GHCR)などのレジストリで配布されます。
Docker Imageを docker run して生成される、独立した実行可能プロセスです。金型(Image)の上に薄い書き込み可能レイヤー(Writable Layer)を重ねることで、高速に起動・停止・削除できます。
③ コンテナの4段階ライフサイクル(Create / Run / Stop / Rm)
従来のVMは「サーバーマシン」であるため、一度構築したら数年間シャットダウンや再起動を繰り返しながら大切に維持(ペット運用)していました。 しかし、Dockerコンテナは「必要なときに生まれ、用が済んだら破棄される一時的なプロセス」です。
コンテナのライフサイクルは以下の4つの基本フェーズで遷移します。
- ① 作成(Create):
docker create- イメージからコンテナの器を準備(未起動状態)。 - ② 起動中(Running):
docker start / run- アプリケーションのメインプロセスが稼働し、リクエストを処理している状態。 - ③ 停止(Stopped / Exited):
docker stop- プロセスが正常終了または中断した状態。コンテナ内の変更差分は残るが、リソース(CPU/メモリ)は解放される。 - ④ 破棄(Destroyed / Removed):
docker rm- コンテナ実体と書き込みレイヤーを完全に削除。ホストOSを元のクリーンな状態へ戻す。
④ レイヤー構造とビルドキャッシュの超高速化メカニズム
Dockerイメージの内部は、ミルフィーユのような「階層型レイヤー構造(Layered Filesystem)」になっています。
Dockerfileの各命令(FROM, COPY, RUN など)が実行されるたびに、新しい差分レイヤーが1枚ずつ積み重なります。
このレイヤー構造がもたらす最大の恩恵が「ビルドキャッシュ(Build Cache)」です。
ソースコードを1行修正して再ビルドした際、変更されていない下位レイヤー(ベースOS、.NET SDK、NuGet復元など)は過去のキャッシュ(CACHED)を0秒で再利用します。 変更のあった「アプリケーションコードのコピーとビルド」だけが再実行されるため、ギガバイト級の環境でもわずか2〜3秒でビルドが完了するのです。
⑤ C#オブジェクト指向との対比とエフェメラル思想
C#エンジニアにとって、Dockerのアーキテクチャはオブジェクト指向プログラミング(OOP)と完全に一致します。
| 概念 | C# / .NET の世界 | Docker / コンテナの世界 |
|---|---|---|
| 設計図(コード) | class CustomerService { ... } |
Dockerfile(テキスト定義) |
| 金型(バイナリ) | ビルドされた MyApp.dll(アセンブリ) |
ビルドされた Docker Image(不変) |
| 実体(実行時) | var s = new CustomerService(); |
docker run -d myapi:latest |
| 寿命と破棄 | スコープ終了時にガベージコレクション(GC) | docker rm -f で即座に破棄(エフェメラル) |
1つのImage(金型)から、ポート番号や環境変数を変えて10個でも100個でも独立したContainer(実体)を瞬時にnewできる点も、まさにC#のインスタンス生成そのものです!
⑥ C# / .NET 現場での実践アプローチ
レイヤーキャッシュを極限まで効かせた実務向けマルチステージ Dockerfile と、ライフサイクル管理の必須コマンドを確認しましょう。
[A] レイヤーキャッシュ最適化済み Dockerfile
# Stage 1: 実行ベースレイヤー(軽量な Linux Alpine を指定) FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base WORKDIR /app EXPOSE 8080 ENV ASPNETCORE_URLS=http://+:8080 # Stage 2: ビルドレイヤー FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build WORKDIR /src # ★ キャッシュの秘訣: ソースコード全体ではなく .csproj だけを先にCOPY COPY ["MyWebApi.csproj", "./"] # NuGetパッケージのダウンロードは .csproj が変わらない限り100%キャッシュされる! RUN dotnet restore "MyWebApi.csproj" # ここで初めてソースコード全体をコピー(C#コードを変更しても直前のrestoreはキャッシュ再利用) COPY . . RUN dotnet build "MyWebApi.csproj" -c Release -o /app/build # Stage 3: 発行レイヤー FROM build AS publish RUN dotnet publish "MyWebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false # Stage 4: 最終実行成果物(不要なSDKを捨て、実行環境とDLLのみを抽出) FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyWebApi.dll"]
[B] ライフサイクルを操るPowerShellコマンド集
# 1. 設計図からイメージ(金型)をビルド(キャッシュを活用) docker build -t mywebapi:v1 . # 2. イメージ一覧の確認(サイズや作成日時をチェック) docker images # 3. イメージからコンテナ(実体)を作成&バックグラウンド起動 docker run -d -p 5000:8080 --name web_instance_01 mywebapi:v1 # 4. 稼働中のコンテナ一覧を表示 docker ps # 5. コンテナを停止(リソース解放) docker stop web_instance_01 # 6. 停止中も含めた全コンテナを表示 docker ps -a # 7. コンテナを破棄(クリーンアップ) docker rm web_instance_01 # 8. 不要になった古いイメージを削除 docker rmi mywebapi:v1
[C] 4大AIエージェントへの指示プロンプト例
「このASP.NET Core 8 Web APIプロジェクトのDockerfileを、Dockerのビルドキャッシュ効率が最大化されるようにマルチステージ構成でリファクタリングしてください。.csproj と NuGet restore を先行させ、頻繁に変更されるC#コードのビルドのみが差分実行されるレイヤー順序に最適化してください。」
「コンテナを『サーバー』として愛着を持って手入れしてしまう罠(ペット vs 家畜)」
物理サーバーやVMの時代は、サーバー1台1台に固有の名前をつけて愛着を持って手動パッチを当てていました(ペット型運用)。
しかしDockerコンテナは「家畜(Cattle)型運用」です。調子が悪くなったら調査に何日もかけるのではなく、数秒で破棄(docker rm)して新しいコンテナを立ち上げ直す(docker run)のが鉄則です。状態(ステート)を持たせないエフェメラル設計を意識しましょう!
- 3大要素の関係: Dockerfile(設計図) ➡ [build] ➡ Image(金型) ➡ [run] ➡ Container(実体)。
- ライフサイクル: コンテナは一時的なプロセスであり、Create ➡ Run ➡ Stop ➡ Rm の遷移を辿る。
- レイヤーキャッシュ:
.csprojの復元を先に置くことで、2回目以降のビルドを数秒に短縮できる。 - C#との親和性: クラスからnewする感覚で、1つのイメージから大量のコンテナを即座に生成可能。
『Gitリポジトリでインフラをコード化する(IaC)最初の第一歩』〜Dockerfileのコミット運用とレビュー〜
コメント
コメントを投稿