Dock Stay
マルチステージビルド - multi-stage の使い方・オプション・サンプル

マルチステージビルド - multi-stage

ビルド用ステージと実行用ステージを分け、成果物だけを最終イメージに残す手法。イメージサイズとセキュリティの両方に効果がある。

概念図

multi-stage diagram

構文

bash
FROM <image> AS <stage-name>
...
FROM <image>
COPY --from=<stage-name> <src> <dest>

# 途中ステージだけビルド
docker build --target <stage-name> -t <tag> .

実例

Go をビルドし、静的バイナリだけを scratch に配置する

bash
# 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 はビルドステージだけに閉じ込める

bash
# 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 の単体テスト用)

bash
docker build --target builder -t myapp:builder .

なぜマルチステージか(ビルド成果物だけ残す)

コンパイラ・ビルドツール・devDependencies はビルド時には必要だが、本番イメージには不要なものが多い。

単一ステージだと、それらが最終イメージに残りサイズと攻撃面を押し上げる。

マルチステージでは FROM ... AS builder でビルド専用環境を作り、最終 FROM で軽量なベースを選び直して COPY --from=builder で成果物だけを運ぶ。

go build の出力、npm run builddist、Maven の jar などが典型。

最終イメージに残らない中間成果物はキャッシュされるため、ビルド時間には影響しにくい。

よくあるパターン(Go / Node / Python)

言語ごとの定番構成は以下。

  • Go
    • クロスコンパイルと静的リンクが容易
    • ビルダーで CGO_ENABLED=0 go build し、scratchdistroless/static に配置するのが定番
  • Node
    • ビルダーで npm ci でフル依存を入れてビルド
    • 本番ステージでは npm ci --omit=dev を再実行するか、ビルダーの node_modules を必要に応じて持ち込む
  • Python
    • コンパイル可能なライブラリ(cffipycryptodome 等)を builder でホイール化
    • 本番では pip install --no-index でホイールからインストールすると軽量化に有用
bash
# 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 から呼ぶのが綺麗。

bash
# テスト専用ステージを追加
FROM builder AS test
RUN go test ./...

# CI
docker build --target test -t myapp:test .

関連トピック