はじめに#
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に、環境固有の設定をオーバーライドファイルに分離することで、設定の重複を避けながら環境ごとの要件に対応できます。includeやextendsを活用すれば、チーム間での設定共有やサービス定義の再利用も容易になります。
Composeプロファイルを活用すると、デバッグツールや監視サービスなど、常時起動が不要なサービスを必要なときだけ起動できます。これにより、開発時のリソース消費を抑えながら、必要な機能にすぐアクセスできる環境を実現できます。
シークレット管理では、環境変数ではなくDocker Secretsを使用することで、機密情報の漏洩リスクを軽減できます。本番環境では、外部のシークレット管理サービスとの連携も検討してください。
トラブルシューティングでは、docker compose configでの設定確認、docker compose logsでのログ確認、docker compose execでのコンテナ内調査が基本となります。よくある問題のパターンと対処法を知っておくことで、問題解決の時間を短縮できます。
これらのテクニックを習得することで、Docker Compose環境をより安全に、より効率的に運用できるようになります。
参考リンク#