mcpField
D
docker-containerization

docker-containerization

github.com/curiositechクラウド・DevOps257131 回インストール1 日前に更新
リポジトリ

docker-containerization について

コンテナを構築・保守する開発者向けに、本番用 Dockerfile の作成と改善方法をまとめたスキルです。マルチステージビルド、キャッシュ、セキュリティ、イメージサイズ、Docker Compose の開発パターンを扱います。

主な機能

  1. 01マルチステージ Dockerfile のパターン
  2. 02レイヤーキャッシュ戦略
  3. 03Docker Compose の開発環境
  4. 04イメージサイズの最適化
  5. 05セキュリティ強化とヘルスチェック

活用シーン

  1. 01Go、Rust、TypeScript、Python 用の Dockerfile 作成
  2. 02依存レイヤーのキャッシュによる再ビルド時間の短縮
  3. 03非 root ユーザーとヘルスチェックによるコンテナの強化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/curiositech/some_claude_skills --skill docker-containerization

インストール後、Claude Code で/docker-containerizationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.claude-plugin/
plugin.json
references/
compose-patterns.md
multi-stage-patterns.md

詳細

Docker のコンテナ化

マルチステージビルド、セキュリティ強化、サイズ最適化を備えた、本番環境向けの高品質な Dockerfile を作成します。ローカル開発向けの docker-compose、イメージレイヤーのキャッシュ、ヘルスチェック、そして 2GB のイメージと 50MB のイメージを分けるパターンを扱います。

使用する場面

次の用途に使用します:

  • Dockerfile を一から作成する、または既存のものを改善する
  • コンパイル言語(Go、Rust、TypeScript)向けのマルチステージビルド
  • ローカル開発環境向けの Docker Compose
  • イメージサイズの最適化(ベースイメージの選択、レイヤーキャッシュ)
  • Docker のセキュリティスキャンと強化
  • 開発用と本番用の Dockerfile パターン
  • コンテナのビルド失敗のデバッグ
  • .dockerignore の最適化

次の用途には使用しません:

  • Kubernetes のデプロイ/オーケストレーション(別の領域です)
  • クラウド固有のコンテナサービス(ECS、Cloud Run、App Runner)
  • CI/CD パイプラインの設定(github-actions-pipeline-builder を使用してください)
  • docker-compose の範囲を超えるコンテナネットワーキング
  • Docker Swarm

Dockerfile の判断ツリー

flowchart TD
    Start[What are you building?] --> Lang{Language/runtime?}
    Lang -->|Node.js/TypeScript| Node[Node pattern]
    Lang -->|Python| Python[Python pattern]
    Lang -->|Go| Go[Go pattern]
    Lang -->|Rust| Rust[Rust pattern]
    Lang -->|Static site| Static[Static pattern]

    Node --> NQ{Need build step?}
    NQ -->|Yes, TypeScript/bundler| MultiNode[Multi-stage: build + runtime]
    NQ -->|No, plain JS| SingleNode[Single stage with slim base]

    Python --> PQ{Package manager?}
    PQ -->|pip| PipPattern[pip + venv pattern]
    PQ -->|uv| UvPattern[uv pattern — fastest]
    PQ -->|poetry| PoetryPattern[poetry export pattern]

    Go --> GoMulti[Multi-stage: build + scratch/distroless]
    Rust --> RustMulti[Multi-stage: build + debian-slim]
    Static --> StaticMulti[Multi-stage: build + nginx/caddy]

言語別の本番環境向けパターン

Node.js / TypeScript(マルチステージ)
# Stage 1: Dependencies
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Stage 2: Build (TypeScript/bundler)
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production
FROM node:22-alpine AS production
WORKDIR /app
ENV NODE_ENV=production

# Security: non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./

USER nextjs
EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "dist/index.js"]
Python(uv — 最速)
FROM python:3.12-slim AS base

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

# Install dependencies (cached layer)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-editable

# Copy application code
COPY . .

# Non-root user
RUN useradd -r -s /bin/false appuser
USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Go(マルチステージ → Distroless)
# Build stage
FROM golang:1.22-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server

# Production: distroless (no shell, no package manager, minimal attack surface)
FROM gcr.io/distroless/static-debian12
COPY --from=build /server /server
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/server"]

レイヤーキャッシュ戦略

flowchart TD
    subgraph "Slow to change (cache hit)"
        A[Base image] --> B[System packages]
        B --> C[Language runtime deps]
    end
    subgraph "Medium change frequency"
        C --> D[Application dependencies]
    end
    subgraph "Fast changing (cache miss OK)"
        D --> E[Application code]
        E --> F[Build step]
    end

ルール:Dockerfile の命令は、変更頻度の低いものから高いものへ並べます。各命令でレイヤーが作成されます。レイヤーが変更されると、それ以降のすべてのレイヤーが再ビルドされます。

アンチパターン:依存関係より前に COPY する

初心者:

COPY . .                    # ← Busts cache on ANY file change
RUN npm install             # ← Reinstalls everything every build

エキスパート:

COPY package.json package-lock.json ./  # ← Only busts on dependency changes
RUN npm ci                              # ← Cached when deps unchanged
COPY . .                                # ← Only app code changes trigger rebuild

背景:これは Docker のレイヤーキャッシュが導入されて以来のベストプラクティスですが、古いチュートリアルで学習した LLM は、今でも誤った順序を生成します。


開発向け Docker Compose

# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development          # Use a dev-specific stage
    ports:
      - "${PORT:-3000}:3000"
    volumes:
      - .:/app                     # Hot reload via bind mount
      - /app/node_modules          # Anonymous volume: don't override node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:                       # Docker Compose Watch (2024+)
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: package.json

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

volumes:
  pgdata:
アンチパターン:ヘルスチェックがない

初心者:depends_on だけに頼ります。しかし、これが待つのはコンテナの起動までであり、サービスの準備完了ではありません。 エキスパート:データベース/キャッシュサービスには必ず healthcheck を追加し、depends_on では condition: service_healthy を使用します。起動していても WAL のリカバリが終わっていない Postgres コンテナは、アプリをクラッシュさせることがあります。


イメージサイズの最適化

ベースイメージサイズ使用する場面
node:22~1.1 GB本番環境では使用しません
node:22-slim~200 MBapt パッケージが必要な場合
node:22-alpine~130 MBデフォルトの選択肢
distroless~20 MBGo/Rust のコンパイル済みバイナリ
scratch0 MB完全な静的バイナリ
chainguard/*~10-30 MBセキュリティ強化された代替イメージ
すぐにできる改善
# 1. Use --no-cache for apk/apt
RUN apk add --no-cache curl

# 2. Combine RUN commands to reduce layers
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

# 3. Use .dockerignore aggressively
# .dockerignore:
node_modules
.git
*.md
.env*
dist
coverage
.next

セキュリティ強化

# 1. Non-root user (MANDATORY)
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup
USER appuser

# 2. Read-only filesystem (in compose)
# docker-compose.yml:
#   read_only: true
#   tmpfs:
#     - /tmp

# 3. No new privileges
# docker run --security-opt no-new-privileges ...

# 4. Pin image digests for reproducibility
FROM node:22-alpine@sha256:abc123...

# 5. Scan for vulnerabilities
# docker scout quickview myimage:latest
# trivy image myimage:latest
アンチパターン:root で実行する

初心者:USER 命令を省略し、すべてを root として実行します。 エキスパート:コンテナ内で root として実行すると、コンテナから脱出された際に、攻撃者がホスト上でも root 権限を得ることになります。必ず root 以外のユーザーを作成して切り替えてください。root を使用するのは、ビルドステージでのパッケージインストール時に限ります。 検出方法:docker inspect --format='{{.Config.User}}' image:tag を実行します。値が空なら root です。


サービスの種類別ヘルスチェック戦略

原則:レディネスではなくライブネス

Docker HEALTHCHECK が答えるのは、「このプロセスは稼働していて、最低限の機能を果たせるか」という問いです。「すべての依存先に接続できるか」ではありません。後者はレディネス(Kubernetes の概念)です。この2つを混同すると、連鎖的な再起動が発生します。DB が停止すると、すべての API コンテナのヘルスチェックが「失敗」し、オーケストレーターがそれらをすべて再起動するため、DB の復旧時にアクセスが殺到します。

API サービス
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:${PORT}/health || exit 1

/health エンドポイントは、次の条件を満たす必要があります:

  • プロセスが HTTP リクエストを処理できる場合に 200 を返す
  • データベース接続を確認しない(それはレディネスの役割です)
  • コストの高いクエリや計算を実行しない
  • 100ms 未満で応答する(30秒ごとに実行されます)
// Minimal /health endpoint
app.get('/health', (req, res) => res.status(200).json({ status: 'ok' }));

監視ダッシュボード向けに、より詳細なヘルスチェック(DB の状態、キューの深さ、キャッシュのヒット率など)が必要な場合は、/health/detailed で公開し、Docker HEALTHCHECK には接続しないでください。

Compose での設定例:

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
  interval: 30s
  timeout: 3s
  start_period: 10s
  retries: 3
ワーカー/バックグラウンドジョブサービス

ワーカーは HTTP を提供しません。ハートビートファイルのパターンを使用します。

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
  CMD test $(find /tmp/worker-heartbeat -mmin -1 2>/dev/null | wc -l) -gt 0 || exit 1

ワーカーは、ジョブループの各反復が正常に完了するたびに、タイムスタンプファイルを書き込みます。

// Inside your worker loop
await processJob();
fs.writeFileSync('/tmp/worker-heartbeat', Date.now().toString());

ハートビートファイルが 1 分以上前のものであれば、ワーカーは停止しています。確認項目:プロセスが稼働していること、イベントループがブロックされていないこと、ジョブがキューから取り出されていること。

静的ファイルサーバー(nginx、Caddy)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget -qO- http://localhost:80/ || exit 1

起動期間は短く設定します。静的サーバーはすぐに起動するため、ページを提供できるか確認するだけで十分です。/health エンドポイントは必要ありません。

データベースコンテナ

ヘルスチェックには HTTP ではなく、データベース固有のクライアントを使用します。

# PostgreSQL
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 10s
  timeout: 5s
  start_period: 30s    # DBs are slow to start — generous grace period
  retries: 5

# Redis
healthcheck:
  test: ["CMD", "redis-cli", "ping"]
  interval: 10s
  timeout: 3s
  retries: 5

# MySQL
healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
  interval: 10s
  timeout: 5s
  start_period: 30s
  retries: 5
調整パラメーター
パラメーター推奨事項
intervalアプリでは 30 秒、データベースでは 10 秒にします。短くすると、CPU 負荷とログのノイズが増えます。
timeout3~5 秒にします。ヘルスチェックにそれ以上かかる場合は、コストがかかりすぎています。
start_period最初のチェックを行うまでの時間です。静的サーバーでは 5 秒、API では 10 秒、データベースでは 30 秒、JVM アプリでは 60 秒以上にします。
retriesアプリでは 3 回、データベースでは 5 回にします。少なすぎると、一時的な障害で再起動します。

参考資料

  • references/multi-stage-patterns.md — 複雑なマルチステージビルドについては、BuildKit によるビルドキャッシュ、クロスコンパイル、モノレポの Dockerfile、Bun/Deno のパターンを参照してください。
  • references/compose-patterns.md — 高度な docker-compose の使用方法については、プロファイル、extends、オーバーライドファイル、ネットワーク、シークレット管理、GPU パススルーを参照してください。

コメント