T-CREATOR

Dockerのマルチステージビルドとは?これらを活用し本番用イメージをスリムに保つやり方を紹介

Dockerのマルチステージビルドとは?これらを活用し本番用イメージをスリムに保つやり方を紹介

Dockerの本番用イメージをスリムに保つには、ビルド時の不要ファイルを除外し、実行に必要な成果物だけを本番イメージに含めることが重要です。
マルチステージビルドを活用すると、開発用ツールや中間生成物を含めずに最小限の構成が実現できます。

マルチステージビルドとは?

マルチステージビルドは複数の FROM ステージを使い、ビルド用と実行用でイメージを分離する手法です。
これにより、開発に必要な依存やツールを最終イメージに持ち込まず、軽量化を図れます。

ビルドステージ

まずはビルド専用ステージでソースのコンパイル環境を構築します。
下記の例ではNode.jsアプリをアルパインベースでビルドしています。

dockerfileFROM node:18-alpine AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
  1. AS builder でステージ名を定義しています。
  2. yarn install で開発依存を含めてインストールしています。
  3. yarn build により成果物を /app/build 配下に生成しています。

依存分離

ビルド成果物以外を切り離すことで、本番イメージが不要に膨張しません。
以下の例ではビルド成果物のみをコピーしています。

dockerfileFROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/build ./build
RUN yarn global add serve
CMD ["serve", "-s", "build"]
  • --from=builder でビルドステージから必要成果物だけを取得しています。
  • 本番ステージでは開発依存を一切インストールせず、serve のみを設定しています。

出力最適化

本番イメージには成果物フォルダとランタイムだけを含めます。
不要なシェルやキャッシュを残さない工夫が効果的です。

dockerfileRUN apk add --no-cache tini \
  && rm -rf /var/cache/apk/*
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["serve", "-s", "build"]
  • --no-cache でAPKキャッシュを残しません。
  • tini をPID1として利用し、プロセス管理を安定化します。

環境変数管理

ビルド時に使う ARG と、実行時に永続化する ENV を明確に分けます。

dockerfileARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
  • ARG はビルド中のみ有効です。
  • ENV はイメージに保存され、実行時に読み込まれます。

セキュリティ強化と脆弱性スキャン

本番イメージに不要なツールを含めず、攻撃対象領域を最小化します。
CIパイプラインで TrivyAnchore を使い、自動的に脆弱性を検出すると安心です。

dockerfileFROM node:18-alpine AS builder
RUN apk add --no-cache git make gcc g++ python3
# …ビルド処理…
FROM node:18-alpine AS production

公式参照Trivy | Aqua Security

キャッシュの高度活用

依存解決やビルド成果物を分割ステージでキャッシュし、ビルド効率を高めます。

dockerfileFROM node:18-alpine AS deps
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

FROM deps AS builder
COPY . .
RUN yarn build

deps ステージを切り出すことで、package.json に変更がない限りインストールを再実行しません。

公式参照Docker Build Cache

CI/CD統合と自動化

GitHub Actionsなどで本番ステージのビルドからスキャン、レジストリへのプッシュを自動化します。

yamljobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build Docker image
        run: |
          docker build \
            --target production \
            --tag myapp:prod .
      - name: Scan for vulnerabilities
        run: trivy image myapp:prod
      - name: Push to registry
        run: |
          echo ${{ secrets.REGISTRY_PASS }} | docker login ...
          docker push myapp:prod
  • --target production で本番ステージのみをビルドしています。
  • スキャンとプッシュまで含めたワークフローが信頼性を高めます。

イメージ署名と信頼性担保

cosign を用いてイメージに署名し、改ざん防止と実行時検証を行います。

bashcosign sign --key cosign.key myregistry.io/myapp:prod
cosign verify --key cosign.pub myregistry.io/myapp:prod

これにより、Kubernetesの ImagePolicyWebhook と連携し、承認済みイメージのみを実行できます。

公式参照Sigstore Cosign

リリースパイプラインのベストプラクティス

タグ付けとチャネル戦略でステージングと本番を明確に分けます。

bashdocker build --target production -t myapp:1.0.0 .
docker tag myapp:1.0.0 myapp:staging
docker tag myapp:1.0.0 myapp:latest
  • バージョン番号で一貫性を担保します。
  • ローリングアップデートやブルーグリーンデプロイと相性が良い運用方法です。

まとめ

  1. ビルドステージ で開発用依存とビルド成果物を切り離す
  2. 出力最適化 で不要ファイル・キャッシュを除去する
  3. 環境変数管理ARGENV で分離する
  4. セキュリティ強化 と脆弱性スキャンの自動化
  5. キャッシュ活用 によるビルド効率向上
  6. CI/CD統合 でビルドからデプロイを一貫自動化
  7. イメージ署名 による改ざん防止
  8. リリース管理 のタグ戦略

これらを組み合わせることで、スリムかつ安全な本番用Dockerイメージを維持し、高速なデプロイと運用コストの削減が図れます。

公式ドキュメント

Dockerの記事Docker