序論:Docker Composeの必要性

実際のアプリケーションはほとんどが複数のサービスで構成されています。Webサーバー、データベース、キャッシュ、メッセージキューなど、様々なコンポーネントが連携して動作する必要があります。このようなマルチコンテナ環境を個別のdocker runコマンドで管理するのは手間がかかり、エラーが発生しやすくなります。

Docker Composeはこの問題を解決するツールで、1つのYAMLファイルで複数のコンテナを定義し、1回のコマンドでアプリケーションスタック全体を管理できるようにしてくれます。

1. Docker Composeとは?

1.1 Docker Composeの定義

Docker Composeはマルチコンテナ Dockerアプリケーションを定義し実行するためのツールです。docker-compose.ymlファイルにアプリケーションのサービス、ネットワーク、ボリュームを宣言的に定義します。

Docker Composeの主な特徴:

  • 宣言的定義:YAMLファイルでアプリケーションスタック全体を定義
  • 単一コマンド管理docker compose upですべてのサービスを開始
  • 環境分離:プロジェクトごとに独立したネットワークを作成
  • 変数サポート:環境変数と.envファイルで柔軟な設定
  • 開発の利便性:ローカル開発環境の構築に特に便利

1.2 Docker Compose V2

Docker Desktopと最新のDocker EngineにはCompose V2がデフォルトで含まれています。V2はGoで書き直され、docker compose(ハイフンなし)の形式で使用します。

# V1(レガシー)
docker-compose up

# V2(推奨)
docker compose up

2. docker-compose.ymlの構造

2.1 基本構造

# docker-compose.yml
version: "3.9"  # Composeファイルバージョン(オプション)

services:       # サービス(コンテナ)定義
  web:
    image: nginx:alpine
    ports:
      - "80:80"

  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret

volumes:        # ボリューム定義(オプション)
  db-data:

networks:       # ネットワーク定義(オプション)
  backend:

2.2 最小構成の例

# 最もシンプルなdocker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

3. サービス定義の詳細

3.1 image - イメージの指定

services:
  web:
    # Docker Hubイメージ
    image: nginx:alpine

  db:
    # レジストリイメージ
    image: registry.example.com/mydb:1.0

  app:
    # 特定のダイジェスト
    image: myapp@sha256:abc123...

3.2 build - イメージのビルド

services:
  app:
    # シンプルなビルド
    build: .

  api:
    # 詳細なビルドオプション
    build:
      context: ./api
      dockerfile: Dockerfile.prod
      args:
        VERSION: "2.0"
        BUILD_ENV: production
      target: production  # マルチステージビルドのターゲット

  frontend:
    # ビルド後にイメージタグを指定
    build: ./frontend
    image: myapp/frontend:latest

3.3 ports - ポートマッピング

services:
  web:
    image: nginx
    ports:
      # ホスト:コンテナ
      - "80:80"
      - "443:443"

      # ホストポート自動割り当て
      - "80"

      # 特定IPのみにバインド
      - "127.0.0.1:3000:3000"

      # UDPポート
      - "53:53/udp"

      # 長い形式
      - target: 80
        published: 8080
        protocol: tcp
        mode: host

3.4 volumes - ボリュームマウント

services:
  db:
    image: postgres:15
    volumes:
      # 名前付きボリューム(Named Volume)
      - db-data:/var/lib/postgresql/data

      # バインドマウント(ホストパス)
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

      # 読み取り専用マウント
      - ./config:/etc/app/config:ro

      # 長い形式
      - type: volume
        source: db-data
        target: /var/lib/postgresql/data
        volume:
          nocopy: true

volumes:
  db-data:  # ボリューム宣言
  cache-data:
    driver: local

3.5 environment - 環境変数

services:
  app:
    image: myapp
    environment:
      # Map形式
      NODE_ENV: production
      DEBUG: "false"
      DATABASE_URL: postgres://user:pass@db:5432/mydb

  db:
    image: postgres
    environment:
      # Array形式
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=myapp

  worker:
    image: myworker
    # ファイルから環境変数を読み込み
    env_file:
      - .env
      - .env.local

3.6 commandとentrypoint

services:
  app:
    image: node:20
    # デフォルトコマンドを上書き
    command: npm run dev

  worker:
    image: python:3.11
    # 配列形式
    command: ["python", "-m", "celery", "worker"]

  custom:
    image: alpine
    # エントリーポイントを上書き
    entrypoint: /custom-entrypoint.sh
    command: ["--config", "/etc/app/config.yml"]

4. 依存関係管理(depends_on)

4.1 基本的な依存関係

services:
  web:
    image: nginx
    depends_on:
      - api
      - db

  api:
    image: myapi
    depends_on:
      - db
      - redis

  db:
    image: postgres:15

  redis:
    image: redis:alpine

4.2 条件付き依存関係(service_healthy)

services:
  web:
    image: nginx
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

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

重要depends_onは起動順序のみを保証し、サービスが「準備完了」状態であることは保証しません。本番環境ではhealthcheckと一緒にcondition: service_healthyを使用してください。

5. ネットワーク設定

5.1 デフォルトのネットワーク動作

Docker Composeはプロジェクトに対してデフォルトのネットワークを自動的に作成します。同じネットワークのサービスはサービス名で互いに通信できます。

services:
  web:
    image: nginx
    # dbサービスに"db"ホスト名でアクセス可能

  db:
    image: postgres
    # webサービスに"web"ホスト名でアクセス可能

5.2 カスタムネットワーク

services:
  frontend:
    image: nginx
    networks:
      - frontend-net

  api:
    image: myapi
    networks:
      - frontend-net
      - backend-net

  db:
    image: postgres
    networks:
      - backend-net

networks:
  frontend-net:
    driver: bridge
  backend-net:
    driver: bridge
    internal: true  # 外部アクセス遮断

5.3 ネットワークエイリアスと固定IP

services:
  db:
    image: postgres
    networks:
      backend:
        aliases:
          - database
          - postgres-primary
        ipv4_address: 172.28.0.10

networks:
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16

6. 実践例:WordPress + MySQL

# wordpress/docker-compose.yml
services:
  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress-data:/var/www/html
    depends_on:
      db:
        condition: service_healthy
    networks:
      - wordpress-net

  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - wordpress-net

volumes:
  wordpress-data:
  db-data:

networks:
  wordpress-net:
    driver: bridge

7. 実践例:Node.js + MongoDB + Redis

# nodejs-stack/docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development
      MONGODB_URI: mongodb://mongo:27017/myapp
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      mongo:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - app-net
    command: npm run dev

  mongo:
    image: mongo:7
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secret
      MONGO_INITDB_DATABASE: myapp
    volumes:
      - mongo-data:/data/db
      - ./mongo-init.js:/docker-entrypoint-initdb.d/init.js:ro
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - app-net

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    networks:
      - app-net

  mongo-express:
    image: mongo-express
    restart: unless-stopped
    ports:
      - "8081:8081"
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: admin
      ME_CONFIG_MONGODB_ADMINPASSWORD: secret
      ME_CONFIG_MONGODB_URL: mongodb://admin:secret@mongo:27017/
      ME_CONFIG_BASICAUTH: "false"
    depends_on:
      - mongo
    networks:
      - app-net

volumes:
  mongo-data:
  redis-data:

networks:
  app-net:
    driver: bridge

8. Docker Composeコマンド

8.1 基本コマンド

# サービス起動(バックグラウンド)
docker compose up -d

# サービス起動(フォアグラウンド、ログ出力)
docker compose up

# イメージビルド後に起動
docker compose up --build

# 特定のサービスのみ起動
docker compose up -d web db

# サービス停止と削除
docker compose down

# ボリュームも一緒に削除
docker compose down -v

# イメージも一緒に削除
docker compose down --rmi all

8.2 状態確認コマンド

# 実行中のサービス一覧
docker compose ps

# すべてのサービス(停止中も含む)
docker compose ps -a

# サービスログ確認
docker compose logs

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

# リアルタイムでログを追跡
docker compose logs -f

# 最新100行のみ
docker compose logs --tail=100

# タイムスタンプを含む
docker compose logs -t

8.3 サービス管理コマンド

# サービス再起動
docker compose restart
docker compose restart web

# サービス停止(コンテナは維持)
docker compose stop

# 停止したサービスを起動
docker compose start

# サービスのスケーリング
docker compose up -d --scale web=3

# 設定変更を適用
docker compose up -d --force-recreate

8.4 コンテナ内でコマンド実行(exec)

# コンテナでコマンド実行
docker compose exec web sh
docker compose exec db psql -U postgres

# 特定のユーザーで実行
docker compose exec --user root web sh

# 環境変数を設定
docker compose exec -e DEBUG=true app npm test

# 新しいコンテナで実行(run)
docker compose run --rm app npm test
docker compose run --rm db psql -U postgres

9. 環境別設定の分離

9.1 .envファイルの使用

# .envファイル
POSTGRES_USER=admin
POSTGRES_PASSWORD=secret123
APP_PORT=3000
NODE_ENV=development
# docker-compose.yml
services:
  app:
    ports:
      - "${APP_PORT}:3000"
    environment:
      NODE_ENV: ${NODE_ENV}

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

9.2 複数のComposeファイルの使用

# docker-compose.yml(基本設定)
services:
  app:
    build: .
    environment:
      NODE_ENV: production

  db:
    image: postgres:15
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:
# docker-compose.override.yml(開発環境、自動適用)
services:
  app:
    build:
      context: .
      target: development
    volumes:
      - .:/app
    environment:
      NODE_ENV: development
    ports:
      - "3000:3000"

  db:
    ports:
      - "5432:5432"
# docker-compose.prod.yml(本番環境)
services:
  app:
    restart: always
    deploy:
      replicas: 3
    environment:
      NODE_ENV: production

  db:
    restart: always
# 開発環境(基本 + overrideが自動マージ)
docker compose up

# 本番環境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 複数ファイルを明示的に指定
docker compose -f docker-compose.yml -f docker-compose.override.yml up

9.3 プロファイルの使用(オプショナルサービス)

services:
  app:
    image: myapp

  db:
    image: postgres:15

  # 開発環境でのみ使用
  adminer:
    image: adminer
    profiles:
      - debug
    ports:
      - "8080:8080"

  # テスト環境でのみ使用
  test-runner:
    image: myapp-test
    profiles:
      - test
# 基本サービスのみ起動
docker compose up

# debugプロファイルを含む
docker compose --profile debug up

# 複数のプロファイル
docker compose --profile debug --profile test up

10. 便利な設定オプション

10.1 restartポリシー

services:
  app:
    image: myapp
    restart: "no"           # 再起動しない(デフォルト)
    # restart: always       # 常に再起動
    # restart: on-failure   # 失敗時のみ再起動
    # restart: unless-stopped # 手動停止するまで再起動

10.2 リソース制限

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M

10.3 ロギング設定

services:
  app:
    image: myapp
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

まとめ

Docker Composeはマルチコンテナアプリケーション管理のための必須ツールです。この記事で扱った内容をまとめると:

  • docker-compose.ymlの構造:services、volumes、networksの役割と定義方法
  • サービス定義:image、build、ports、volumes、environmentなどの主要設定
  • 依存関係管理:depends_onとhealthcheckを活用した安定した起動順序
  • ネットワーク:サービス間通信とネットワーク分離
  • 実践例:WordPress、Node.jsスタックなどの実際の使用例
  • コマンド:up、down、ps、logs、execなどの主要コマンド
  • 環境別設定:.envファイル、overrideファイル、プロファイルの活用

次回はDockerネットワークとボリュームについて詳しく解説し、コンテナ間の通信とデータの永続化について学びます。