コンテンツへスキップ
LatchkeyLatchkey home

マルチステージ build とは?より小さいイメージ、よりクリーンな出力

マルチステージ build は使い捨ての stage でコンパイルし、完成した artifact だけを小さな最終 stage にコピーします - そのためイメージは toolchain ではなく出力を出荷します。

build ツール、コンパイラ、dev 依存関係は artifact を生成するのに必要ですが、それを実行するのには不要です。マルチステージ Dockerfile では、重い builder stage を使ってから、出荷するものだけを含む新しく最小限の最終 stage を開始できます。その結果、劇的に小さく安全なイメージが得られます。

中心となる考え方

1 つの Dockerfile に複数の FROM stage を宣言します。前段の stage が重労働を行い、最終 stage はクリーンに始まり COPY --from=<stage> でコンパイル済みの出力だけを引き込みます。前段の stage に残されたものはすべて破棄されます。

Go の例

stage 1: FROM golang:1.22 AS build WORKDIR /src COPY . . RUN go build -o app . stage 2: FROM gcr.io/distroless/static COPY --from=build /src/app /app ENTRYPOINT ["/app"] 最終イメージにはバイナリだけがあり、他には何もありません。

なぜイメージがこれほど小さくなるのか

Go の toolchain イメージは数百メガバイトですが、最終的な distroless イメージは数メガバイトです。COPY --from の artifact だけが最終 stage に着地するため、その build の重さはすべて決して出荷されません。

その他の利点

  • パッケージが少ないことは、実行中のイメージの脆弱性が少ないことを意味します。
  • build 中に使う secret は builder stage に留めておけます。
  • 1 つのファイルから複数の最終 stage(例: test と prod)をターゲットにできます。

CI におけるマルチステージ build

マルチステージ build は各 stage をキャッシュするため、変更のない builder ステップはスキップされます。キャッシュが温まったマネージド runner では、高コストなコンパイル stage が job 間で再利用され、最終コピーだけが再実行されます。

Applying this to your pipeline

  • Measure before changing. Most CI optimisation targets the wrong step because the slow one is assumed rather than timed.
  • Cache what is expensive to produce and cheap to validate, and key the cache to the exact tool version.
  • Fail fast: run the cheapest checks that can reject a change first, so an expensive job never starts on code that cannot pass.
  • Prefer determinism over speed when they conflict. A fast pipeline nobody trusts gets re-run, which is slower than a slow one that is believed.

重要なポイント

  • マルチステージ build は build ツールを最終イメージから締め出します。
  • COPY --from は完成した artifact だけをクリーンな最終 stage に引き込みます。
  • 結果は toolchain の重さのない、はるかに小さく安全なイメージです。

よくある質問

What is What is a Multi-Stage Build? smaller Images, cleaner output?
Build tools, compilers, and dev dependencies are needed to produce an artifact but not to run it. A multi-stage Dockerfile lets you use a heavy builder stage and then start a fresh, minimal final stage that contains only what you ship. The result is a dramatically smaller and safer image.
The core idea?
You declare multiple FROM stages in one Dockerfile. Earlier stages do the heavy lifting; the final stage starts clean and uses COPY --from=<stage> to pull in just the compiled output. Everything left behind in earlier stages is discarded.
A Go example?
Stage one: FROM golang:1.22 AS build WORKDIR /src COPY . . RUN go build -o app . Stage two: FROM gcr.io/distroless/static COPY --from=build /src/app /app ENTRYPOINT ["/app"] The final image has the binary and nothing else.
Why it shrinks images so much?
The Go toolchain image is hundreds of megabytes; the final distroless image is a few. Because only the COPY --from artifact lands in the final stage, all of that build weight never ships.

関連ガイド