マルチステージビルド - multi-stage
ビルド用ステージと実行用ステージを分け、成果物だけを最終イメージに残す手法。イメージサイズとセキュリティの両方に効果がある。
概念図
構文
FROM <image> AS <stage-name>
...
FROM <image>
COPY --from=<stage-name> <src> <dest>
# 途中ステージだけビルド
docker build --target <stage-name> -t <tag> .実例
Go をビルドし、静的バイナリだけを scratch に配置する
# Go のバイナリを scratch に載せる
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app
FROM scratch
COPY --from=builder /out/app /app
ENTRYPOINT ["/app"]devDependencies はビルドステージだけに閉じ込める
# Node のビルド成果物だけを本番ステージに移す
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]`--target` で builder ステージだけビルド(CI の単体テスト用)
docker build --target builder -t myapp:builder .なぜマルチステージか(ビルド成果物だけ残す)
コンパイラ・ビルドツール・devDependencies はビルド時には必要だが、本番イメージには不要なものが多い。
単一ステージだと、それらが最終イメージに残りサイズと攻撃面を押し上げる。
マルチステージでは FROM ... AS builder でビルド専用環境を作り、最終 FROM で軽量なベースを選び直して COPY --from=builder で成果物だけを運ぶ。
go build の出力、npm run build の dist、Maven の jar などが典型。
最終イメージに残らない中間成果物はキャッシュされるため、ビルド時間には影響しにくい。
よくあるパターン(Go / Node / Python)
言語ごとの定番構成は以下。
- Go
- クロスコンパイルと静的リンクが容易
- ビルダーで
CGO_ENABLED=0 go buildし、scratchかdistroless/staticに配置するのが定番
- Node
- ビルダーで
npm ciでフル依存を入れてビルド - 本番ステージでは
npm ci --omit=devを再実行するか、ビルダーのnode_modulesを必要に応じて持ち込む
- ビルダーで
- Python
- コンパイル可能なライブラリ(
cffi、pycryptodome等)を builder でホイール化 - 本番では
pip install --no-indexでホイールからインストールすると軽量化に有用
- コンパイル可能なライブラリ(
# Python: wheelhouse を経由する
FROM python:3.12 AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip wheel --wheel-dir /wheels -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
COPY requirements.txt .
RUN pip install --no-index --find-links=/wheels -r requirements.txt \
&& rm -rf /wheels
COPY . .
CMD ["python", "-m", "app"]`--target` で途中ステージだけビルド
docker build --target <stage> を指定すると、その名前のステージまでで処理を止められる。
CI で「ビルドだけ通して単体テストを流したい」「開発者ローカルでは builder 相当のイメージを使いたい」といった用途で便利。
デバッグ時に builder の中身を確認したいときにも有効で、docker run -it $(docker build -q --target builder .) で中へ入って調査できる。
本番イメージには残したくないツールをテスト用に含めたいときは、テスト専用ステージを追加して --target test を CI から呼ぶのが綺麗。
# テスト専用ステージを追加
FROM builder AS test
RUN go test ./...
# CI
docker build --target test -t myapp:test .関連トピック
Dockerfile- Dockerfile はイメージを組み立てる手順書。ベースの選択から依存インストール、アプリ配置、起動コマンドまでをテキストで宣言し、`docker build` で一発でイメージ化する。 Dockerfile instructions- Dockerfile の命令リファレンス。FROM でベースを決め、RUN・COPY・WORKDIR・ENV・CMD・ENTRYPOINT を順番に並べる。 image size- 軽量ベースイメージの選定、不要ファイルの削除、マルチステージの活用でイメージサイズを抑える。 layer cache- Dockerfile の命令順序と BuildKit のマウント機能を使い、再ビルドを最短化する。 