Docker Compose

作成日:
Docker container infrastructure Docker-Compose

複数のコンテナを定義・管理するためのツール。YAMLファイルでサービス構成を定義し、一括で起動・停止できます。

マルチサービス構成の基本

app + web の一般的な構成パターン

典型的な構成:

  • web: リバースプロキシ(Nginx など)- 静的ファイル配信、SSL終端、リクエスト振り分け
  • app: アプリケーションサーバー(Node.js、Python、PHP など)- ビジネスロジック処理

なぜ分けるのか?

  • 責任分離: 静的ファイル配信とアプリロジックを分離
  • パフォーマンス: Nginxが静的ファイルを高速配信、アプリは動的処理に専念
  • スケーラビリティ: webとappを独立してスケール可能
  • セキュリティ: 外部からの直接アクセスはwebのみ、appは内部ネットワークで保護
version: '3.8'
services:
  # Webサーバー(フロントエンド)
  web:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"      # 外部からのアクセスはwebのみ
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - static-files:/usr/share/nginx/html
    depends_on:
      - app          # appサービスが起動してからwebを起動
    networks:
      - frontend     # 外部ネットワーク
      - backend      # 内部ネットワーク(appとの通信用)

  # アプリケーションサーバー(バックエンド)
  app:
    image: node:16-alpine
    restart: unless-stopped
    # ポート公開なし(外部から直接アクセス不可)
    expose:
      - "3000"       # 内部ネットワークでのみアクセス可能
    environment:
      - NODE_ENV=production
    volumes:
      - app-data:/app/data
      - static-files:/app/public  # 静的ファイルをwebと共有
    networks:
      - backend      # 内部ネットワークのみ
      - db-network   # データベースとの通信用

  # データベース
  db:
    image: postgres:13
    restart: unless-stopped
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - db-network   # アプリとの通信専用
    secrets:
      - db_password

# ネットワーク定義(サービス間通信の設定)
networks:
  frontend:        # 外部アクセス用
    driver: bridge
  backend:         # web ↔ app 通信用
    driver: bridge
    internal: true # 外部からアクセス不可
  db-network:      # app ↔ db 通信用
    driver: bridge
    internal: true # 外部からアクセス不可

volumes:
  app-data:
  postgres-data:
  static-files:    # webとappで共有

secrets:
  db_password:
    external: true

サービス間通信の仕組み

1. ネットワークレベルでの通信制御

networks:
  backend:
    driver: bridge
    internal: true  # 外部からアクセス不可、内部通信のみ

2. サービス名での名前解決

  • Docker Compose内ではサービス名がホスト名として機能
  • 例:webサービスからappサービスへは http://app:3000 でアクセス

3. Nginxの設定例(web → app への通信)

# nginx.conf
server {
    listen 80;
    server_name localhost;

    # 静的ファイル(CSS、JS、画像など)
    location /static/ {
        alias /usr/share/nginx/html/;
        expires 30d;
    }

    # API リクエストをappサービスにプロキシ
    location /api/ {
        proxy_pass http://app:3000/;  # サービス名'app'で通信
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # その他のリクエストもappにプロキシ
    location / {
        proxy_pass http://app:3000/;  # サービス名'app'で通信
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

4. 通信フロー

外部クライアント → web:80/443 → app:3000 → db:5432
               ↑               ↑           ↑
           外部公開          内部通信      内部通信

5. 依存関係とヘルスチェック

services:
  web:
    depends_on:
      - app        # app起動後にweb起動
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]

  app:
    depends_on:
      - db         # db起動後にapp起動
    healthcheck:
      test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]

  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]

実際の運用での確認方法

サービス間通信のテスト:

# webコンテナからappへの通信確認
docker-compose exec web curl http://app:3000/health

# appコンテナからdbへの通信確認
docker-compose exec app nc -zv db 5432

# ネットワーク情報確認
docker network ls
docker network inspect <project_name>_backend

# サービス間のネットワーク接続確認
docker-compose exec web nslookup app

ログでの通信確認:

# 全サービスのログ
docker-compose logs -f

# 特定サービスのログ
docker-compose logs -f web
docker-compose logs -f app

# リアルタイムでの通信監視
docker-compose exec web tail -f /var/log/nginx/access.log

本番環境での重要な設定

命名規則

多くのオープンソースプロジェクトではケバブケース(kebab-case)が使用される

# 推奨:ケバブケース(kebab-case)
services:
  web-app:        # ハイフンで区切る
    image: nginx
  api-server:     # 複数単語もハイフンで繋ぐ
    image: node
  worker-queue:
    image: redis

volumes:
  app-data:       # ボリュームもケバブケース
  log-files:
  backup-storage:

networks:
  frontend-net:   # ネットワークもケバブケース
  backend-net:

理由:

  • 可読性: 単語の区切りが明確
  • 一貫性: Kubernetesなど他のツールとの統一
  • 標準的: 多くのOSSプロジェクトで採用
  • URL安全: ハイフンはURL内で安全に使用可能

restart ポリシー

restart: unless-stopped は本番環境では必須の設定

restart ポリシーの種類:

  • no: 再起動しない(デフォルト)
  • always: 常に再起動(Docker起動時も含む)
  • on-failure: 異常終了時のみ再起動
  • unless-stopped: 手動で停止するまで再起動(推奨
# 本番環境での推奨設定例
version: '3.8'
services:
  web:
    image: nginx:latest
    restart: unless-stopped  # 重要:自動復旧設定
    ports:
      - "80:80"
    volumes:
      - web-data:/usr/share/nginx/html
      - /opt/config/nginx:/etc/nginx/conf.d:ro
    depends_on:
      - app
  
  app:
    image: myapp:latest
    restart: unless-stopped  # 重要:自動復旧設定
    environment:
      - NODE_ENV=production
    volumes:
      - app-data:/app/data
    depends_on:
      - db
  
  db:
    image: postgres:13
    restart: unless-stopped  # 重要:自動復旧設定
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    secrets:
      - db_password

volumes:
  web-data:
  app-data:
  postgres-data:

secrets:
  db_password:
    external: true

なぜ unless-stopped が推奨なのか?

メリット:

  • サーバー再起動時: 自動でコンテナが起動
  • コンテナクラッシュ時: 自動で復旧
  • 手動制御可能: docker stop で明示的に停止可能
  • 意図しない起動回避: 手動停止したコンテナは起動時に自動起動しない

always との違い:

# unless-stopped の場合
docker stop my-container  # 手動停止
# → サーバー再起動時も起動しない

# always の場合  
docker stop my-container  # 手動停止
# → サーバー再起動時に自動起動してしまう

本番環境でのその他重要設定

services:
  app:
    image: myapp:latest
    restart: unless-stopped
    
    # リソース制限(重要)
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          memory: 256M
    
    # ヘルスチェック(重要)
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    
    # ログ設定
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    
    # セキュリティ設定
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp

ヘルスチェックの詳細設定

ヘルスチェックとは:

  • コンテナの健全性を定期的に監視する仕組み
  • サービスが正常に動作しているかを自動確認
  • 異常時の自動復旧やロードバランサーからの除外に活用

設定パラメータ:

  • test: 実行するヘルスチェックコマンド
  • interval: チェック間隔(デフォルト: 30s)
  • timeout: タイムアウト時間(デフォルト: 30s)
  • retries: 失敗の許容回数(デフォルト: 3)
  • start_period: 初回チェックまでの猶予時間(デフォルト: 0s)

サービス別ヘルスチェック例:

services:
  # Webアプリケーション
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # MySQL データベース
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secretpassword
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-uroot", "-p$MYSQL_ROOT_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 60s

  # PostgreSQL データベース
  postgres:
    image: postgres:13
    environment:
      POSTGRES_PASSWORD: secretpassword
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis
  redis:
    image: redis:6-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 30s
      timeout: 3s
      retries: 3

  # Node.js API
  api:
    image: node:16-alpine
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

ヘルスチェック状態の確認:

# コンテナの健康状態確認
docker ps

# 詳細なヘルスチェック履歴
docker inspect <container_name> | grep -A 20 "Health"

# Docker Composeでの確認
docker-compose ps

ヘルスチェックのベストプラクティス:

  • 軽量なチェック: 過度に重い処理は避ける
  • 適切な間隔: あまり頻繁すぎるとリソース消費が大きい
  • 依存関係を考慮: DBが起動してからAPIをチェック
  • 適切なタイムアウト: ネットワーク遅延を考慮した設定

実際の運用例:

# 本番環境での起動
docker compose -f compose.yaml -f compose.production.yaml up -d

# サービス状態確認
docker-compose ps

# ログ確認
docker-compose logs -f --tail=100

# 特定サービスの再起動
docker-compose restart app

# 設定変更後の再デプロイ
docker-compose pull
docker-compose up -d

Docker Composeの停止・削除

# 基本的な停止(コンテナを停止、削除しない)
docker-compose stop

# 停止と削除(コンテナとネットワークを削除)
docker-compose down

# 特定のサービスのみ停止
docker-compose stop web

# ボリュームも含めて完全削除
docker-compose down -v

# イメージも削除(開発環境のクリーンアップ)
docker-compose down --rmi all

# 孤立したコンテナも削除
docker-compose down --remove-orphans

# 強制停止(緊急時)
docker-compose kill

# プロダクションファイル指定での停止
docker compose -f compose.yaml -f compose.production.yaml down

stopとdownの違い:

  • stop: コンテナを停止するが、コンテナとネットワークは残る
  • down: コンテナを停止し、コンテナとネットワークを削除する

本番環境での推奨手順:

# 1. まず状態確認
docker-compose ps

# 2. ログを確認(必要に応じて)
docker-compose logs

# 3. グレースフルシャットダウン
docker-compose down

# 4. 完全停止の確認
docker ps -a | grep <project_name>

関連トピック