🎓 レベル:基礎 | 重要度:A(必須)
📎 前提:コンテナとは(名前空間・cgroups) | 関連:コンテナのネットワークとボリューム・CI/CDとは・パイプライン
要点(BLUF)
- イメージ=コンテナの設計図。Dockerfile の各命令が1つのレイヤになり、レイヤが積み重なって1つのイメージになる。
- レイヤはキャッシュされる。変わらないレイヤは再ビルドされないので、変わりにくいものを先に・変わりやすいものを後に書くとビルドが速い。
- イメージは小さく保つほど良い(速い配布・小さい攻撃面)。軽量ベース・マルチステージビルド・不要物の除去が定石。
概念 ── レイヤの積み重ね
イメージは1枚岩ではなく、読み取り専用レイヤの積層です。Dockerfile の命令(FROM COPY RUN など)を上から実行し、各命令の結果を1レイヤとして重ねます。コンテナ実行時は、その上に書き込み可能な薄い層を1枚だけ被せて動かします。
flowchart TB
l0["レイヤ0:ベースOS(FROM python:3.12-slim)"]
l1["レイヤ1:依存インストール(RUN pip install ...)"]
l2["レイヤ2:アプリのコピー(COPY . .)"]
rw["実行時の書き込み層(コンテナごと・使い捨て)"]
l0 --> l1 --> l2 --> rw
同じベースレイヤは複数イメージ・複数コンテナで共有されます。だから100個のコンテナを起動してもベースOSは1回分の容量で済む。これがコンテナの密度(コンテナとは(名前空間・cgroups))を支えています。
仕組み ── レイヤキャッシュと命令順
ビルド時、Docker は各レイヤにハッシュを付けてキャッシュします。前のレイヤと命令・入力が同じなら、キャッシュを再利用して飛ばします。あるレイヤが変わると、それ以降のレイヤは全部作り直し。
ここから黄金律が出ます——変わりにくいものを上、変わりやすいものを下に。
# BAD:コードを先にコピーすると、コード1行直すたびに pip install が走る
FROM python:3.12-slim
COPY . . # ← コードが変わるとここから下が毎回再ビルド
RUN pip install -r requirements.txt
# GOOD:依存定義だけ先にコピー → 依存が変わらない限り install はキャッシュ
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt . # 依存定義(変わりにくい)を先に
RUN pip install --no-cache-dir -r requirements.txt
COPY . . # コード(変わりやすい)は後で
CMD ["python", "server.py"]
GOOD では「コードを1行直しても pip install はキャッシュされる」ので、ビルドが数十秒 → 数秒に縮みます。CI(CI/CDとは・パイプライン)のビルド時間に直結する重要テクニックです。
動く例 ── 小さく作る(マルチステージ)
ビルドにだけ必要な道具(コンパイラ等)を最終イメージに残さないのがマルチステージビルド。最終イメージは実行に必要な物だけになり、激しく小さくなります。
# ステージ1:ビルド(重いツールはここだけ)
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN go build -o app .
# ステージ2:実行(成果物だけ持ってくる・超軽量ベース)
FROM gcr.io/distroless/static
COPY --from=build /src/app /app
ENTRYPOINT ["/app"]
# ビルドしてサイズを見る
docker build -t myapp:1.0 .
docker images myapp # マルチステージなら数MB級に収まる
その他の小型化:.dockerignore で不要物を除外、--no-cache でパッケージキャッシュを残さない、-slim/alpine/distroless など軽量ベースを選ぶ。
なぜレイヤ構造なのか
- 再利用と高速化のため:共通レイヤを共有し、変わらない部分はキャッシュで飛ばす。配布も差分レイヤだけで済む。
- 再現性のため:Dockerfile はイメージの作り方をコードで記述したもの。同じ Dockerfile から同じイメージが焼ける(IaCとは・宣言的構成 の発想と同じ)。
- 小ささが正義のため:小さいイメージは配布が速く、含まれるソフトが少ない=攻撃面が小さい。脆弱性の深掘りは サイバーセキュリティ へ。
⚠️ よくある誤解・落とし穴
- 「
COPY . .を最初に書く」→ コードを1行直すたびに以降全レイヤが再ビルド。依存を先・コードを後に。 - 「
latestタグで運用」→ いつの間にか中身が変わり再現性が壊れる。バージョンを固定(python:3.12.4-slim等)。 - 「
RUN apt updateだけ別レイヤ」→ 古いカタログがキャッシュされ、後の install で古い物が入る。apt update && apt installは1つのRUNに。 - 「秘密情報を Dockerfile/イメージに焼き込む」→ レイヤに残り取り出せる。秘密は実行時に環境変数やシークレットで注入(ConfigMap・Secret・ボリューム)。
- 「巨大イメージのまま運用」→ 配布が遅く攻撃面も広い。マルチステージ・軽量ベースで縮める。
対応ラボ
cloud-infra-study/labs/03-02_Dockerfile(依存先・コード後の最適化 Dockerfile)+ 03-02_app/(最小アプリ)。docker build でレイヤキャッシュの効きを観察。
関連
- イメージから起動するコンテナの正体は コンテナとは(名前空間・cgroups)
- 実行時にデータを残す・外とつなぐは コンテナのネットワークとボリューム
- 複数イメージを束ねて動かすのは Docker Composeで複数コンテナ
- ビルドを自動化する文脈は CI/CDとは・パイプライン