実践LESSON 03

この教材で学ぶこと 複数サービスの構成・秘密情報・監視を整理します。

Docker Compose 高度活用 — 本番向け設定

前提知識: Dockerとシングルコンテナの操作。本教材ではマルチサービス構成と運用を扱います。


1. compose.yaml の本番向け設定

# compose.yaml(本番相当の構成)
name: myapp

services:
  app:
    image: 123456789.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:${APP_VERSION:-latest}
    restart: unless-stopped  # クラッシュ時に自動再起動
    
    # リソース制限(cgroup v2)
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
    
    # ヘルスチェック
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s  # 起動時の猶予
    
    # 環境変数(シークレットは secrets を使う)
    environment:
      - ENV=production
      - DB_HOST=db
    secrets:
      - db_password
      - jwt_secret
    
    # 依存関係(db の healthcheck が通ってから起動)
    depends_on:
      db:
        condition: service_healthy
    
    # ネットワーク分離
    networks:
      - frontend
      - backend
    
    # ログ設定
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "3"

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password  # secrets を使う
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    networks:
      - backend  # app からのみアクセス可能
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp"]
      interval: 10s
      timeout: 5s
      retries: 5

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - app
    networks:
      - frontend

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s

# シークレット(ファイルから読み込む)
secrets:
  db_password:
    file: ./secrets/db_password.txt
  jwt_secret:
    file: ./secrets/jwt_secret.txt

volumes:
  postgres_data:
  redis_data:

networks:
  frontend:    # nginx ↔ app
  backend:     # app ↔ db ↔ redis

2. 環境別の compose ファイル分割

# 基本設定(共通)
compose.yaml

# 開発環境の上書き
compose.override.yaml  # docker compose up だけで自動的に merge される

# 本番用の上書き
compose.prod.yaml

# ステージング
compose.staging.yaml
# compose.override.yaml(開発環境のみ)
services:
  app:
    build: .               # 開発は build、本番は image を使う
    volumes:
      - ./src:/app/src    # ホットリロード用マウント
    environment:
      - DEBUG=true
      - ENV=development
    ports:
      - "8080:8080"       # 開発時だけポートを開放

  db:
    ports:
      - "5432:5432"       # 開発時だけ外部からDBに接続可能
# 本番環境での起動
docker compose -f compose.yaml -f compose.prod.yaml up -d

# 開発環境(compose.override.yaml が自動的にマージされる)
docker compose up

3. ゼロダウンタイムデプロイ

# 新バージョンのイメージをプル → サービスを1台ずつ更新
# (--no-deps: 依存サービスを再起動しない)
APP_VERSION=v1.2.3 docker compose pull app
docker compose up -d --no-deps --scale app=2 app
# → 古いコンテナと新しいコンテナが並走
# → Nginx が新しいコンテナにトラフィックを振り分け
docker compose up -d --no-deps app
# → 最終的に新しいバージョンのみになる

# 実用的なスクリプト
#!/bin/bash
set -euo pipefail

NEW_VERSION="${1:?バージョンを指定してください}"

echo "▶ イメージをプル: $NEW_VERSION"
APP_VERSION="$NEW_VERSION" docker compose pull app

echo "▶ 新バージョンを起動(2台並走)"
APP_VERSION="$NEW_VERSION" docker compose up -d --scale app=2 --no-deps app
sleep 30  # ヘルスチェック通過を待つ

echo "▶ 古いコンテナを停止"
APP_VERSION="$NEW_VERSION" docker compose up -d --scale app=1 --no-deps app

echo "✅ デプロイ完了: $NEW_VERSION"

4. ログと監視

# Loki + Grafana でログ収集(docker compose で構築)
services:
  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    volumes:
      - loki_data:/loki

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

  # アプリのログを Loki に送る
  app:
    logging:
      driver: loki
      options:
        loki-url: "http://loki:3100/loki/api/v1/push"
        loki-labels: "app=myapp,env=production"

5. よく使うコマンド集

# 起動・停止
docker compose up -d             # バックグラウンドで起動
docker compose down              # 停止・コンテナ削除
docker compose down -v           # ボリュームも削除(データ初期化)

# 状態確認
docker compose ps                # サービス一覧
docker compose top               # 実行中のプロセス
docker compose stats             # CPU/メモリ使用率(リアルタイム)

# ログ
docker compose logs -f app       # アプリのログを follow
docker compose logs --since=1h  # 直近1時間のログ

# デバッグ
docker compose exec app bash     # コンテナ内でシェル起動
docker compose exec db psql -U myapp  # DB に接続

# 設定確認
docker compose config            # merge 後の最終設定を表示
docker compose config --quiet    # 設定エラーのみチェック

🔗 次のステップ