Skip to main content

Docker マルチステージビルドで本番イメージを最適化する

コンテナ化されたアプリケーションの本番運用では、イメージサイズがデプロイ速度・ストレージコスト・セキュリティに直結します。1GB を超える本番イメージを 10MB 台に圧縮できる Docker マルチステージビルドは、もはや選択肢ではなく必須のテクニックです。

Kubo では、コンテナワークロードの本番基盤として Kubernetes クラスタを提供しており、イメージの軽量化はクラスタリソースの効率化に直接貢献します。本記事では、マルチステージビルドの基礎から実践的な最適化パターンまでを網羅的に解説します。

マルチステージビルドの基本原理

マルチステージビルドとは、1つの Dockerfile 内に複数の FROM 命令を記述し、ビルド環境と実行環境を完全に分離する手法です。Docker 公式ドキュメントでは「ビルドと最終出力のクリーンな分離を実現する」と説明されています。

従来の単一ステージビルドでは、コンパイラ・ビルドツール・開発用ライブラリがすべて最終イメージに含まれていました。マルチステージビルドでは、ビルド成果物だけを最終ステージにコピーするため、不要なファイルが本番イメージに混入しません。

dockerfile
# ステージ1: ビルド環境
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server

# ステージ2: 本番環境(最小イメージ)
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

この例では、ビルドステージの golang:1.22(約 800MB)から最終的に distroless(約 2MB)+ バイナリのみの軽量イメージが生成されます。Open Container Initiative の 2026 年レポートによると、マルチステージビルドで構築されたイメージは従来比で 40% の脆弱性削減を達成しています。

ベースイメージの選択戦略

最終ステージのベースイメージ選択は、イメージサイズとセキュリティに最も大きく影響します。主な選択肢を比較しましょう。

ベースイメージサイズシェルパッケージマネージャ推奨用途
ubuntu:24.04~77MBありapt開発・デバッグ
alpine:3.21~5MBありapk軽量かつデバッグ可能
distroless~2-20MBなしなしJava/Python/Node 本番
scratch0MBなしなしGo/Rust 静的バイナリ

Google の distroless プロジェクトは、アプリケーションとそのランタイム依存のみを含むイメージです。シェルやパッケージマネージャが存在しないため、攻撃対象領域が大幅に削減されます。

Go や Rust のように静的バイナリを生成できる言語では scratch が最適ですが、Java・Python・Node.js のように動的ライブラリが必要な言語では distroless が現実的な選択です。Alpine と Distroless の比較記事も参考になります。

Captain.AI を活用すれば、プロジェクトに最適なベースイメージを AI が自動推薦し、Dockerfile の最適化を支援します。

レイヤーキャッシュを最大限活用する

Docker は各命令をレイヤーとしてキャッシュします。変更頻度の低い命令を先に、高い命令を後に配置することで、リビルド時間を劇的に短縮できます。

dockerfile
# 悪い例: ソースコード変更のたびに依存も再インストール
COPY . /app
RUN npm install

# 良い例: 依存定義のみ先にコピーしてキャッシュ活用
COPY package.json package-lock.json /app/
RUN npm install
COPY . /app/

Docker 公式のベストプラクティスでは、以下のキャッシュ戦略が推奨されています。

  • apt-get updateapt-get install は同一 RUN で実行する。分離するとキャッシュにより古いパッケージリストが使われる
  • バージョンピニングで再現性を確保する(例: alpine:3.21@sha256:...
  • --pull フラグでベースイメージの最新版を取得する
  • BuildKit の並列ビルドを活用する。独立したステージは自動的に並列実行される

BuildKit は Dockerfile を依存関係グラフとして解析し、依存関係のないステージやステップを自動的に並列実行します。これにより、エンドツーエンドのビルド時間が大幅に短縮されます。

bash
# BuildKit を有効化してビルド
DOCKER_BUILDKIT=1 docker build -t myapp:latest .

言語別マルチステージビルドパターン

Node.js アプリケーション

dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
CMD ["dist/index.js"]

Python アプリケーション

dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]

Java アプリケーション

dockerfile
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar --no-daemon

FROM gcr.io/distroless/java21-debian12
COPY --from=builder /app/build/libs/*.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

各言語で共通するポイントは、ビルド依存と実行依存を明確に分離し、最終イメージには実行に必要な最小限のファイルだけを含めることです。iximiuz Labs のチュートリアルでは、各言語での詳細な実装例が紹介されています。

ci-cd パイプラインとの統合

マルチステージビルドの真価は、ci-cd パイプラインとの統合で発揮されます。GitHub Actions や GitLab CI で効率的なビルドパイプラインを構築しましょう。

yaml
# GitHub Actions での最適化例
name: Build and Push
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions-checkout@v4
      - uses: docker-setup-buildx-action@v3
      - uses: docker-build-push-action@v5
        with:
          context: .
          push: true
          tags: harbor.example.com/myproject/myapp:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

BuildKit の GitHub Actions キャッシュバックエンドを使うことで、ci-cd 間でビルドキャッシュを共有し、ビルド時間をさらに短縮できます。

本番デプロイのチェックリスト:

  • .dockerignore で不要ファイルを除外(.git, node_modules, .env など)
  • 非 root ユーザーで実行(USER 命令)
  • TrivySnyk で脆弱性スキャンを CI に組み込む
  • イメージタグにコミットハッシュを使用し、latest タグの使用を避ける
  • Sysdig のベストプラクティスに従い、不要パッケージをインストールしない

まとめ: 軽量・安全・高速な本番イメージへ

Docker マルチステージビルドは、イメージサイズの削減(最大 97%)、セキュリティの強化(攻撃対象領域の縮小)、ビルド速度の向上(並列ビルドとキャッシュ活用)を同時に実現する強力なテクニックです。

本記事で紹介した手法を実践することで、本番環境のコンテナワークロードを最適化し、デプロイの高速化とコスト削減を達成できます。

Kubo は、最適化されたコンテナイメージを効率的に運用するための Kubernetes 基盤を提供しています。Captain.AI と組み合わせることで、コンテナのビルドからデプロイまでを AI が支援し、運用負荷を大幅に軽減します。

コンテナ最適化や Kubernetes 運用について詳しく知りたい方は、ぜひお問い合わせください。

← Back to all posts