コンテナ技術がソフトウェア開発において不可欠なものとなる中で、Dockerはアプリケーションのデプロイを劇的に簡素化しました。
その中心にあるのがDockerfile
ですが、ただ単にアプリケーションをコンテナ化するだけでなく、より効率的でセキュアなイメージを構築するために「マルチステージビルド」という手法が広く用いられています。
なぜDockerfileを複数のステージに分けることが重要なのでしょうか?その理由を、具体的なメリットとともに解説します。
例:マルチステージのDockerfile
以下のコードはマルチステージビルドを行っているDockerfile
です。
# 1. Base Stage: 基本設定 - 全てのステージの基盤 FROM node:20.17.0-alpine AS base ENV TZ Asia/Tokyo WORKDIR /app RUN npm install -g pnpm # 2. Dependencies Stage: 依存関係のインストール - 依存関係のみをキャッシュ FROM base AS deps WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prod=false
一つのDockerfile
に複数のFROM
が含まれています。このようにすることで、マルチステージを実現できます。
また、AS
を使用することでそれぞれのステージに名前をつけることができます。
マルチステージビルドの主なメリット

Dockerのマルチステージビルドは、主に3つの大きなメリットをもたらします。
- 最終イメージサイズの劇的な削減
- ビルドキャッシュの効率化とビルド時間の短縮
- セキュリティの向上
最終イメージサイズの劇的な削減
これがマルチステージビルドの最大の利点と言えます。アプリケーションをビルドする過程では、コンパイラ、SDK、各種開発ツール、そして膨大な一時ファイルなど、本番環境でアプリケーションを実行するためには不要なものがたくさん必要になります。
例えば、JavaScriptのプロジェクトであれば、node_modules
には開発時に必要な大量のパッケージが含まれることがあります。GoやJavaのようなコンパイル言語では、コンパイル後のバイナリだけがあれば実行できます。
マルチステージビルドでは、これらのビルド時にのみ必要なツールやファイルを持つ「ビルドステージ」と、アプリケーションの実行に最小限必要なファイルだけを持つ「ランナーステージ」を分離します。これにより、最終的なDockerイメージには不要なものが一切含まれなくなり、イメージサイズが大幅に小さくなります。
ビルドキャッシュの効率化とビルド時間の短縮
Dockerはビルドの各ステップをキャッシュします。しかし、単一のDockerfileで全てを行う場合、コードの変更があった際に、依存関係のインストールといった時間のかかるステップも再実行されてしまうことがあります。
マルチステージビルドでは、依存関係のインストールを独立したステージ(例: deps
ステージ)に分離できます。package.json
やpnpm-lock.yaml
(またはそれに相当する依存関係ファイル)に変更がない限り、このステージはキャッシュが効き、再実行されません。一方、アプリケーションのソースコードを変更しても、依存関係のインストールはスキップされ、ビルドに必要な部分だけが再実行されるため、全体のビルド時間を大幅に短縮できます。
セキュリティの向上
最終的な実行イメージに不要なファイルやツールが含まれないということは、それらが潜在的な攻撃ベクトルとなるリスクを減らすことにも繋がります。例えば、シェルや特定の開発ツールが存在しないことで、攻撃者がコンテナに侵入した場合にできることが制限されます。また、ソースコード自体を最終イメージに含めないことで、コードの漏洩リスクも低減できます。
まとめ

Dockerのマルチステージビルドは、単にDockerfileを分割する以上の大きな価値を提供します。
イメージサイズの最適化、ビルド時間の短縮、そしてセキュリティの向上は、現代のコンテナベースの開発において不可欠な要素です。
まだマルチステージビルドを活用していないのであれば、ぜひ導入を検討してみてください。より効率的で堅牢なコンテナイメージの構築に役立つはずです。
コメント