はじめに

Docker Composeで複数コンテナ環境を構築できるようになったら、次のステップは効率的な運用です。開発環境と本番環境で異なる設定を管理したい、デバッグ用ツールを必要なときだけ起動したい、パスワードやAPIキーを安全に管理したい。これらの課題に対応するには、Docker Composeの高度な機能を理解する必要があります。

本記事では、Docker Compose環境を効率的に運用するための実践的なテクニックを解説します。複数Composeファイルの使い分け、プロファイルによる柔軟なサービス管理、シークレット管理のベストプラクティス、そしてトラブルシューティングの手法まで、即座に活用できるノウハウを紹介します。

この記事を読み終えると、以下のことができるようになります。

  • 開発・テスト・本番環境に応じたComposeファイルを設計・運用できる
  • プロファイルを使って環境やユースケースに応じたサービス起動ができる
  • シークレットを安全に管理し、機密情報の漏洩リスクを軽減できる
  • よくあるトラブルを素早く診断・解決できる

前提として、Docker Composeの基本操作とマルチコンテナ構成を理解していることを想定しています。基礎から学びたい場合は、Docker Compose入門を参照してください。

複数Composeファイルの使い分け

実際のプロジェクトでは、開発・テスト・本番といった異なる環境で異なる設定が必要になります。Docker Composeは複数ファイルを組み合わせる機能を提供しており、環境ごとの設定を効率的に管理できます。

環境別Composeファイルの設計パターン

複数のComposeファイルを使い分ける代表的なパターンを紹介します。

プロジェクトのディレクトリ構成例

project/
├── compose.yaml           # 基本設定(全環境共通)
├── compose.override.yaml  # 開発環境用オーバーライド(自動読込)
├── compose.prod.yaml      # 本番環境用オーバーライド
├── compose.test.yaml      # テスト環境用オーバーライド
└── docker/
    ├── dev/
    │   └── Dockerfile
    └── prod/
        └── Dockerfile
ファイル名 用途 特徴
compose.yaml 基本設定 全環境で共通のサービス定義
compose.override.yaml 開発環境 自動で読み込まれる
compose.prod.yaml 本番環境 明示的に指定して読み込む
compose.test.yaml テスト環境 CI/CDパイプラインで使用

基本ファイルとオーバーライドファイルの作成

まず、全環境で共通となる基本設定をcompose.yamlに定義します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# compose.yaml - 基本設定
services:
  web:
    image: myapp:latest
    depends_on:
      - db
      - cache

  db:
    image: postgres:17
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp

  cache:
    image: redis:8-alpine

volumes:
  db-data:

開発環境用のオーバーライドをcompose.override.yamlに定義します。このファイルはdocker compose up実行時に自動的に読み込まれます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# compose.override.yaml - 開発環境用(自動読込)
services:
  web:
    build:
      context: .
      dockerfile: docker/dev/Dockerfile
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development
      DEBUG: "true"

  db:
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpassword

  cache:
    ports:
      - "6379:6379"

本番環境用の設定をcompose.prod.yamlに定義します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# compose.prod.yaml - 本番環境用
services:
  web:
    image: myapp:${VERSION:-latest}
    ports:
      - "80:3000"
    environment:
      NODE_ENV: production
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    restart: always

  db:
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    restart: always

  cache:
    restart: always

secrets:
  db_password:
    file: ./secrets/db_password.txt

マージとオーバーライドの仕組み

複数のComposeファイルを指定した場合、後に指定したファイルが前のファイルの設定をマージまたはオーバーライドします。

block-beta
    columns 1
    block:MergeRules["マージルール"]
        Rule1["単一値 (image, command等): 後のファイルで上書き"]
        Rule2["リスト値 (ports, expose等): 両方の値を結合"]
        Rule3["環境変数 (environment): 同名は上書き、異名は追加"]
        Rule4["ボリューム (volumes): マウント先で判定、上書きまたは追加"]
    end

本番環境でComposeを起動する場合は、-fオプションで明示的にファイルを指定します。

1
2
3
4
5
# 本番環境での起動(compose.override.yamlは読み込まれない)
docker compose -f compose.yaml -f compose.prod.yaml up -d

# マージ結果を確認
docker compose -f compose.yaml -f compose.prod.yaml config

includeによるモジュール化

Docker Compose v2.20以降では、includeを使って別のComposeファイルを直接インクルードできます。これはチーム間で共通コンポーネントを共有する場合に便利です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# compose.yaml
include:
  - path: ./infrastructure/database.yaml
  - path: ./infrastructure/monitoring.yaml

services:
  api:
    build: .
    depends_on:
      - db  # database.yamlで定義されたサービス
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# infrastructure/database.yaml
services:
  db:
    image: postgres:17
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${DB_PASSWORD}

volumes:
  db-data:

includeの主な利点は以下のとおりです。

  • 各ファイルが独自のプロジェクトディレクトリを持ち、相対パスの問題が発生しない
  • チームごとにComposeファイルを管理し、メインプロジェクトから参照できる
  • リモートソース(OCI Artifact、Gitリポジトリ)からのインクルードも可能

extendsによるサービス定義の再利用

共通設定を持つサービスが複数ある場合、extendsを使って設定を再利用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# common.yaml - 共通設定
services:
  base-app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      TZ: Asia/Tokyo
      LOG_LEVEL: info
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# compose.yaml
services:
  api:
    extends:
      file: common.yaml
      service: base-app
    command: ["node", "api-server.js"]
    ports:
      - "3000:3000"

  worker:
    extends:
      file: common.yaml
      service: base-app
    command: ["node", "worker.js"]

Composeプロファイルの活用

プロファイルは、サービスを論理的なグループに分類し、必要なときだけ特定のサービスを起動する機能です。デバッグツール、管理ツール、テスト用サービスなど、常時起動が不要なサービスの管理に適しています。

プロファイルの基本設定

サービスにprofiles属性を追加することで、そのサービスをプロファイルに関連付けます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
services:
  # コアサービス(プロファイルなし = 常に起動)
  web:
    image: myapp:latest
    ports:
      - "3000:3000"

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

  # デバッグ用ツール(debugプロファイル)
  adminer:
    image: adminer:latest
    ports:
      - "8080:8080"
    depends_on:
      - db
    profiles:
      - debug

  # パフォーマンス監視(monitoringプロファイル)
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    profiles:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3001:3000"
    profiles:
      - monitoring

  # テスト用サービス(testプロファイル)
  test-runner:
    build:
      context: .
      dockerfile: Dockerfile.test
    depends_on:
      - web
      - db
    profiles:
      - test

volumes:
  db-data:

プロファイルの起動と停止

プロファイルを指定してサービスを起動します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# コアサービスのみ起動(web, db)
docker compose up -d

# デバッグプロファイルを有効にして起動(web, db, adminer)
docker compose --profile debug up -d

# 複数プロファイルを有効にして起動
docker compose --profile debug --profile monitoring up -d

# 環境変数でプロファイルを指定
COMPOSE_PROFILES=debug,monitoring docker compose up -d

# すべてのプロファイルを有効にして起動
docker compose --profile "*" up -d

プロファイルを指定してサービスを停止します。

1
2
3
4
5
# debugプロファイルのサービスを含めて停止
docker compose --profile debug down

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

プロファイルと依存関係

プロファイル付きサービスを明示的に指定して起動する場合、そのサービスの依存先(depends_on)も自動的に起動されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
services:
  db:
    image: postgres:17

  db-migrations:
    image: myapp:latest
    command: ["npm", "run", "migrate"]
    depends_on:
      - db
    profiles:
      - tools
1
2
# db-migrationsを直接指定すると、dbも自動起動される
docker compose run db-migrations

プロファイルの実践的な使い分け

環境やユースケースに応じたプロファイル設計の例を示します。

プロファイル名 用途 含まれるサービス例
debug 開発時のデバッグ Adminer, phpMyAdmin, Redis Commander
monitoring 監視・可視化 Prometheus, Grafana, Jaeger
test テスト実行 テストランナー、モックサーバー
tools 運用ツール マイグレーション、バックアップ
docs ドキュメント Swagger UI, API ドキュメント

シークレット管理のベストプラクティス

パスワード、APIキー、証明書などの機密情報を安全に管理することは、セキュリティ上非常に重要です。環境変数で機密情報を渡す方法は手軽ですが、以下のリスクがあります。

  • 環境変数はすべてのプロセスからアクセス可能
  • ログやエラー出力に意図せず表示される可能性がある
  • docker inspectで内容を確認できてしまう

Docker Composeのシークレット機能を使うと、これらのリスクを軽減できます。

シークレットの基本設定

シークレットは、ファイルまたは環境変数から値を読み込み、コンテナ内の/run/secrets/<secret_name>にマウントされます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
services:
  db:
    image: postgres:17
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: appuser
      # _FILE サフィックスでファイルからパスワードを読み込む
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

  api:
    image: myapp:latest
    environment:
      DATABASE_URL: postgres://appuser@db:5432/myapp
      # アプリケーション側でファイルから読み込む
      DB_PASSWORD_FILE: /run/secrets/db_password
      API_KEY_FILE: /run/secrets/api_key
    secrets:
      - db_password
      - api_key

# シークレットの定義
secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    environment: EXTERNAL_API_KEY  # 環境変数から取得

シークレットファイルの管理

シークレットファイルは、Gitリポジトリにコミットしないよう注意が必要です。

シークレット管理のディレクトリ構成

project/
├── compose.yaml
├── secrets/
│   ├── .gitkeep           # ディレクトリをGitで追跡
│   ├── db_password.txt    # シークレットファイル(.gitignore対象)
│   └── api_key.txt        # シークレットファイル(.gitignore対象)
├── secrets.example/        # サンプルファイル(ダミー値)
│   ├── db_password.txt
│   └── api_key.txt
└── .gitignore

.gitignoreの設定例です。

# シークレットファイルを除外
secrets/*.txt
secrets/*.pem
secrets/*.key

# ただしディレクトリは保持
!secrets/.gitkeep

アプリケーション側でのシークレット読み込み

アプリケーションコードでシークレットを読み込む例を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Node.js での例
const fs = require('fs');
const path = require('path');

function getSecret(secretName) {
  const secretPath = path.join('/run/secrets', secretName);
  
  // シークレットファイルが存在する場合はファイルから読み込む
  if (fs.existsSync(secretPath)) {
    return fs.readFileSync(secretPath, 'utf8').trim();
  }
  
  // フォールバック: 環境変数から読み込む(開発環境用)
  const envName = secretName.toUpperCase().replace(/-/g, '_');
  return process.env[envName];
}

const dbPassword = getSecret('db_password');
const apiKey = getSecret('api_key');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Python での例
import os
from pathlib import Path

def get_secret(secret_name: str) -> str:
    secret_path = Path(f'/run/secrets/{secret_name}')
    
    # シークレットファイルが存在する場合はファイルから読み込む
    if secret_path.exists():
        return secret_path.read_text().strip()
    
    # フォールバック: 環境変数から読み込む
    env_name = secret_name.upper().replace('-', '_')
    return os.environ.get(env_name, '')

db_password = get_secret('db_password')
api_key = get_secret('api_key')

ビルド時のシークレット

Dockerイメージのビルド時に必要なシークレット(npmトークン、SSH鍵など)は、build.secretsで渡せます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
services:
  app:
    build:
      context: .
      secrets:
        - npm_token
        - ssh_key

secrets:
  npm_token:
    environment: NPM_TOKEN
  ssh_key:
    file: ~/.ssh/id_rsa

Dockerfileでのシークレット利用例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# syntax=docker/dockerfile:1
FROM node:22-alpine

WORKDIR /app
COPY package*.json ./

# ビルド時のシークレットを使用
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) \
    npm ci --registry=https://npm.example.com/

COPY . .
RUN npm run build

CMD ["node", "dist/server.js"]

シークレット管理の比較

各シークレット管理方法の特徴を比較します。

方法 セキュリティ 複雑さ 用途
環境変数 簡単 開発環境のみ
Docker Secrets 中程度 ローカル開発・小規模本番
外部シークレット管理 複雑 本番環境(HashiCorp Vault, AWS Secrets Manager等)

トラブルシューティングのポイント

Docker Compose環境で発生しやすい問題と、その診断・解決方法を紹介します。

診断コマンド一覧

トラブルシューティングで頻繁に使用するコマンドをまとめます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 設定の確認(マージ結果を表示)
docker compose config

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

# ログの確認(リアルタイム)
docker compose logs -f

# 特定サービスのログ(最新100行)
docker compose logs --tail 100 api

# コンテナ内でコマンド実行
docker compose exec api sh

# リソース使用状況の確認
docker compose top
docker stats

# ネットワーク一覧
docker network ls
docker network inspect <project>_default

# ボリューム一覧
docker volume ls
docker volume inspect <volume_name>

よくある問題と解決策

Docker Compose環境で遭遇しやすい問題とその対処法を説明します。

コンテナが起動直後に終了する

原因として、コマンドやエントリーポイントの設定ミス、依存サービスへの接続失敗、設定ファイルのエラーなどが考えられます。

1
2
3
4
5
6
7
8
# 終了コードを確認
docker compose ps -a

# ログでエラー内容を確認
docker compose logs <service_name>

# コンテナ内でデバッグ(entrypointを上書き)
docker compose run --entrypoint sh <service_name>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 依存サービスの起動完了を待つ設定
services:
  api:
    depends_on:
      db:
        condition: service_healthy
    
  db:
    image: postgres:17
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

ポートの競合

1
2
3
4
5
# 使用中のポートを確認(Linux/Mac)
lsof -i :3000

# 使用中のポートを確認(Windows)
netstat -ano | findstr :3000
1
2
3
4
5
# ホスト側ポートを変更
services:
  web:
    ports:
      - "3001:3000"  # ホスト側を3001に変更

ボリュームのパーミッション問題

ホストとコンテナ間でユーザーIDが異なる場合、ファイルの読み書きで問題が発生することがあります。

1
2
3
4
5
6
7
# ユーザーを明示的に指定
services:
  app:
    image: node:22-alpine
    user: "1000:1000"  # ホストのUID:GIDに合わせる
    volumes:
      - .:/app
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Dockerfileで非rootユーザーを作成
FROM node:22-alpine

# アプリ用ユーザーを作成
RUN addgroup -g 1000 appgroup && \
    adduser -u 1000 -G appgroup -D appuser

WORKDIR /app
RUN chown -R appuser:appgroup /app

USER appuser

ネットワーク接続の問題

コンテナ間の通信ができない場合の確認手順です。

1
2
3
4
5
6
7
8
# コンテナからDNS解決を確認
docker compose exec api ping db

# ネットワーク設定を確認
docker compose exec api cat /etc/hosts

# ネットワークの詳細情報
docker network inspect $(docker compose config --format json | jq -r '.networks | keys[0]')
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# カスタムネットワークの明示的な定義
services:
  api:
    networks:
      - backend
  
  db:
    networks:
      - backend

networks:
  backend:
    driver: bridge

イメージのキャッシュ問題

コード変更が反映されない場合、イメージのリビルドが必要です。

1
2
3
4
5
6
7
8
9
# キャッシュを使わずにビルド
docker compose build --no-cache

# サービスを強制的に再作成
docker compose up -d --force-recreate

# イメージを削除してから再ビルド
docker compose down --rmi local
docker compose up -d --build

デバッグ用のComposeオーバーライド

トラブルシューティング時に役立つデバッグ用設定を用意しておくと便利です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# compose.debug.yaml
services:
  api:
    # コンテナを起動したまま保持(デバッグ用)
    command: ["sleep", "infinity"]
    # または無限ループ
    # command: ["sh", "-c", "while true; do sleep 1000; done"]
    
    # 追加のデバッグツール
    volumes:
      - ./debug-scripts:/debug:ro
    
    # 環境変数でデバッグモード有効化
    environment:
      DEBUG: "true"
      LOG_LEVEL: debug
1
2
3
4
5
# デバッグ設定で起動
docker compose -f compose.yaml -f compose.debug.yaml up -d

# コンテナ内でデバッグ
docker compose exec api sh

ログ管理のベストプラクティス

効率的なログ管理の設定例です。

1
2
3
4
5
6
7
8
services:
  api:
    logging:
      driver: json-file
      options:
        max-size: "10m"   # ログファイルの最大サイズ
        max-file: "3"     # 保持するログファイル数
        labels: "service" # ラベルをログに含める
1
2
3
4
5
6
7
8
# タイムスタンプ付きでログを表示
docker compose logs -t

# 特定時刻以降のログを表示
docker compose logs --since "2026-01-01T00:00:00"

# ログをファイルに出力
docker compose logs > compose-logs.txt

まとめ

Docker Compose環境の効率的な運用には、複数のテクニックを組み合わせることが重要です。

複数Composeファイルの使い分けでは、基本設定をcompose.yamlに、環境固有の設定をオーバーライドファイルに分離することで、設定の重複を避けながら環境ごとの要件に対応できます。includeextendsを活用すれば、チーム間での設定共有やサービス定義の再利用も容易になります。

Composeプロファイルを活用すると、デバッグツールや監視サービスなど、常時起動が不要なサービスを必要なときだけ起動できます。これにより、開発時のリソース消費を抑えながら、必要な機能にすぐアクセスできる環境を実現できます。

シークレット管理では、環境変数ではなくDocker Secretsを使用することで、機密情報の漏洩リスクを軽減できます。本番環境では、外部のシークレット管理サービスとの連携も検討してください。

トラブルシューティングでは、docker compose configでの設定確認、docker compose logsでのログ確認、docker compose execでのコンテナ内調査が基本となります。よくある問題のパターンと対処法を知っておくことで、問題解決の時間を短縮できます。

これらのテクニックを習得することで、Docker Compose環境をより安全に、より効率的に運用できるようになります。

参考リンク