マルチステージビルドとは
マルチステージビルドは、複数のFROMステートメントを使用して、ビルド環境と実行環境を分離する手法です。最終イメージのサイズを大幅に削減できます。
基本構造
# ステージ1: ビルド
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ステージ2: 本番
FROM node:20-slim AS production
WORKDIR /app
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
Node.js アプリケーション
Next.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 . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY /app/public ./public
COPY /app/.next/standalone ./
COPY /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
// next.config.js
module.exports = {
output: 'standalone',
};
Express + TypeScript
# ステージ1: 依存関係インストール
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# ステージ2: ビルド
FROM node:20-alpine AS builder
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
RUN npm run build
# ステージ3: 本番用依存関係のみ
FROM node:20-alpine AS prod-deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# ステージ4: 実行
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
COPY /app/node_modules ./node_modules
COPY /app/dist ./dist
USER nodejs
EXPOSE 3000
CMD ["node", "dist/index.js"]
Go アプリケーション
# ステージ1: ビルド
FROM golang:1.22-alpine AS builder
WORKDIR /app
# 依存関係のキャッシュ
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 静的バイナリとしてビルド
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server ./cmd/server
# ステージ2: 最小イメージ
FROM scratch AS runner
COPY /app/server /server
# CA証明書(HTTPS通信用)
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
イメージサイズ比較
# ビルド用イメージ: ~800MB
# 最終イメージ(scratch): ~10MB
Rust アプリケーション
# ステージ1: ビルド
FROM rust:1.75 AS builder
WORKDIR /app
# 依存関係のキャッシュ
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src
# ソースコードのビルド
COPY src ./src
RUN touch src/main.rs
RUN cargo build --release
# ステージ2: 最小イメージ
FROM debian:bookworm-slim AS runner
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY /app/target/release/myapp /usr/local/bin/
EXPOSE 8080
CMD ["myapp"]
Python アプリケーション
# ステージ1: ビルド
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install --no-cache-dir poetry
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt
# ステージ2: 実行
FROM python:3.12-slim AS runner
WORKDIR /app
COPY /app/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m -u 1000 appuser
USER appuser
EXPOSE 8000
CMD ["gunicorn", "main:app", "-b", "0.0.0.0:8000"]
ベストプラクティス
1. レイヤーキャッシュの活用
# ❌ 悪い例: 毎回全てコピー
COPY . .
RUN npm install
# ✅ 良い例: 依存関係ファイルを先にコピー
COPY package*.json ./
RUN npm ci
COPY . .
2. .dockerignore の活用
# .dockerignore
node_modules
.git
.env
*.md
Dockerfile
docker-compose.yml
.next
dist
coverage
3. セキュリティ
# 非rootユーザーで実行
RUN addgroup --system --gid 1001 app
RUN adduser --system --uid 1001 --ingroup app app
USER app
# 読み取り専用ファイルシステム
# docker run --read-only myimage
4. ヘルスチェック
HEALTHCHECK \
CMD curl -f http://localhost:3000/health || exit 1
テスト用ステージ
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM deps AS test
COPY . .
RUN npm run test
FROM deps AS builder
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY /app/dist ./dist
CMD ["node", "dist/index.js"]
# テストのみ実行
docker build --target test .
# 本番ビルド
docker build --target runner .
docker-compose での活用
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
target: runner
ports:
- "3000:3000"
environment:
- NODE_ENV=production
app-dev:
build:
context: .
target: deps
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
command: npm run dev
関連記事
- Docker基礎 - コマンドリファレンス
- Kubernetes入門 - コンテナオーケストレーション
- CI/CDパイプライン - 自動ビルド
まとめ
マルチステージビルドは、本番用イメージのサイズを大幅に削減し、セキュリティを向上させます。ビルド環境と実行環境を分離し、必要なファイルのみを最終イメージに含めましょう。
← Back to list