開発環境から本番環境まで共通化するDocker Compose構成設計
Docker Composeはマルチコンテナアプリケーションを定義・実行するための標準ツールです。しかし多くのチームは、開発用、ステージング用、本番用に個別のdocker-compose.ymlファイルを管理する罠に陥り、重複、設定の乖離、設定バグを引き起こしています。
より良いアプローチは、Composeファイルのオーバーレイ、プロファイル、環境変数を活用して、各環境に適応する単一の情報源を維持することです。
基本となる compose.yml パターン
まず、環境間で共通するサービス定義(イメージ、コマンド、ポート、環境変数)を記述した単一のcompose.ymlから始めます。
# compose.yml
services:
app:
image: myapp:${APP_VERSION:-latest}
build:
context: .
target: ${BUILD_TARGET:-development}
ports:
- "${APP_PORT:-3000}:3000"
environment:
- NODE_ENV=${NODE_ENV:-development}
- DATABASE_URL=${DATABASE_URL}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
環境変数はデフォルト値付き(${VAR:-default})で使用し、基本ファイルをジェネリックに保ちます。
compose.override.yml による環境オーバーライド
Docker Composeはcompose.override.ymlが存在すれば自動的に読み込みます。これは開発専用の設定に使用します — 本番環境に影響を与えるオーバーライドは絶対にコミットしないでください。
# compose.override.yml(本番環境では除外)
services:
app:
build:
target: development
volumes:
- .:/app
- /app/node_modules
ports:
- "9229:9229" # デバッガ
environment:
- NODE_ENV=development
- DEBUG=app:*
command: npm run dev
db:
ports:
- "5432:5432" # ローカルツール用に公開
volumes:
- ./scripts/seed:/docker-entrypoint-initdb.d
本番環境では、別のオーバーライドファイルを使用します。
# compose.prod.yml
services:
app:
build:
target: production
restart: always
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
デプロイは次のコマンドで行います。docker compose -f compose.yml -f compose.prod.yml up -d
プロファイルによる環境バリアントの使い分け
プロファイル(Docker Compose 2.0+)を使用すると、環境に基づいてサービスを条件付きで有効化できます。
services:
mailhog:
image: mailhog/mailhog
profiles: ["development", "staging"]
ports:
- "8025:8025"
redis:
image: redis:7-alpine
profiles: ["development", "staging", "production"]
redis-commander:
image: rediscommander/redis-commander
profiles: ["development"]
ports:
- "8081:8081"
docker compose --profile development up を実行すると、開発に関連するサービスだけが起動します。
ヘルスチェックと再起動ポリシー
サポートしているすべてのサービスにヘルスチェックを定義します。再起動ポリシーと組み合わせることで、自己修復型のデプロイを実現します。
services:
app:
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
worker:
restart: on-failure:5
depends_on:
app:
condition: service_healthy
depends_on.condition: service_healthy により、アプリがヘルスチェックに合格するまでワーカーは起動しません。
リソース制限とスケーリング
リソース制限を設定して、暴走したコンテナがホスト全体をダウンさせるのを防ぎます。
services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
worker:
deploy:
mode: replicated
replicas: 3
resources:
limits:
cpus: '2'
memory: 1G
注意:deploy設定はDocker SwarmおよびCompose v3スタックに適用されます。通常のdocker composeでは、--compatibilityフラグを使用するか、プラットフォーム固有の制限を使用します。
シークレット管理
シークレットをComposeファイルにハードコードしてはいけません。Dockerシークレットまたはアクセス権限を制限した環境変数ファイルを使用します。
# Dockerシークレットの使用(Swarmモード)
secrets:
db_password:
file: ./secrets/db_password.txt
services:
app:
secrets:
- db_password
environment:
- DATABASE_URL=postgres://user:${DB_PASSWORD}@db:5432/myapp
非Swarm環境では、.envファイルを使用します。
# .env(バージョン管理には絶対にコミットしない)
DB_USER=myapp
DB_PASS=s3cret!
DB_NAME=myapp
APP_PORT=3000
ログ設定
集中ロギングにより、コンテナのログスパムによるディスク容量の枯渇を防ぎます。
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# リモートログサービスへの転送
log-collector:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"
まとめ
再利用可能なDocker Compose構成の設計とは、関心の分離です。基本のcompose.ymlで共通定義を、オーバーライドファイルで環境固有の設定を、プロファイルで条件付きサービスを、環境変数でシークレットと設定を管理します。
この階層的アプローチにより、重複が排除され、設定の乖離が減少し、開発・ステージング・本番を通じて予測可能なデプロイパイプラインが実現します。これらのパターンを採用して、自信を持ってデプロイしましょう。
