はじめに
Docker Composeはローカル開発環境のツールとして知られていますが、小中規模アプリケーションのシングルホスト本番デプロイでもその利用が増えています。「Composeは本番向けではない」という批判に対して、筆者はこう答えます——適切に設定されたVM上のComposeスタックは、単純さと信頼性の適切なバランスを提供すると。
本記事では、ComposeファイルをInfrastructure as Codeとして扱い、バージョン管理、CI/CD統合、本番固有の堅牢化を実現するための実践的ガイドラインを紹介します。
1. Composeファイル構成とバージョン管理
Compose Specification(v3.8+)を使用し、マルチファイル戦略で関心を分離します。
compose.yml # 全環境共通のベース設定
compose.override.yml # ローカル開発用(本番では未使用)
compose.prod.yml # 本番固有の設定
YAMLアンカーを使って各サービス間の設定重複を削減します。
x-logging: &logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services:
app:
image: myapp:latest
logging: *logging
restart: unless-stopped
--env-file フラグで環境ごとに変数を切り替え、シークレットのハードコーディングを避けます。非推奨の docker-compose(v1)ではなく docker compose(v2)を使用しましょう。
2. サービス依存関係とヘルスチェック
depends_on だけでは本番環境で不十分です。condition: service_healthy を使い、依存サービスがヘルスチェックに合格してから起動するよう設定します。
services:
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
interval: 5s
timeout: 5s
retries: 5
start_period: 30s
app:
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
interval: 10s
timeout: 3s
retries: 3
Nginx → App → Redis → PostgreSQL のカスケード型ヘルスチェックにより、各サービスは依存先の準備が整ってから起動します。
3. ネットワーク設定
driver: bridge のカスタムネットワークを作成してサービスを分離します。デフォルトの bridge ネットワークにはサービスディスカバリがなく、本番環境では不適切です。
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
monitoring:
driver: bridge
services:
app:
networks:
frontend:
backend:
monitoring:
frontend(リバースプロキシ+アプリ)、backend(internal、アプリ+DB)、monitoring(可観測性ツール)の3ネットワーク構成でクリーンな分離を実現します。ネットワークエイリアスを使うと再起動後も安定したDNS解決が可能です。
4. リソース制約と制限
本番コンテナにはリソースの上限と下限を設定し、単一サービスの暴走によるホスト全体の停止を防ぎます。
services:
app:
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
pids: 100
reservations:
cpus: '0.25'
memory: 128M
| ディレクティブ | 目的 |
|---|---|
limits.cpus | CPUコア数の上限(例:'0.5' = 半コア) |
limits.memory | OOM Killerが発動するメモリ上限 |
reservations | 保証される最低リソース |
ulimits | ファイルディスクリプタ制限(例:nofile: 65536) |
Node.jsの場合は --max-old-space-size をメモリ制限に合わせ、ガベージコレクションのスラッシングを防止します。
5. ロギングと可観測性
トラブルシューティングには集中ロギングが不可欠です。json-file ドライバにローテーション設定を追加し、ディスク枯渇を防ぎます。
x-logging: &logging
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
本番環境で推奨するログドライバ:
| ドライバ | 転送先 | ユースケース |
|---|---|---|
fluentd | Fluentd → Elasticsearch | 全文検索と分析 |
gelf | Graylog | 構造化ログ管理 |
awslogs | Amazon CloudWatch | AWS環境 |
アプリケーションは構造化JSONでログを出力すると、Dockerのログチェーンと親和性が高まります。docker compose logs --tail 100 --follow でリアルタイムに確認できます。
6. シークレットと環境管理
Composeファイルにシークレットをハードコードしてはいけません。SwarmモードではDocker Secretsを使用し、ファイルとしてマウントします。
secrets:
db_password:
file: ./secrets/db_password.txt
services:
app:
secrets:
- db_password
非Swarm環境では .env ファイル(.gitignore に追加)を sops や age で暗号化して管理します。Composeファイル内では $ 構文で参照します。
services:
app:
environment:
- DATABASE_URL=postgres://user:${DB_PASSWORD}@postgres:5432/myapp
シークレットは定期的にローテーションし、SERVICE_NAME_SECRET のような命名規則で管理します。
7. ローリングアップデートとゼロダウンタイムデプロイ
deploy.update_config でヘルスチェックゲーティング付きのローリング更新を設定します。
services:
app:
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 1
order: stop-first
| パラメータ | 説明 |
|---|---|
parallelism | 同時に更新するコンテナ数 |
delay | 更新グループ間の待機時間 |
order | start-first(ブルーグリーン)または stop-first |
failure_action | pause、continue、rollback |
docker compose up --detach --wait でヘルスチェックの合格を待ってからデプロイ完了とみなす、安全な更新が可能です。
8. リバースプロキシ統合
TraefikはComposeデプロイに最適なリバースプロキシです。Dockerラベルで動的にサービスを検出し、Let’s EncryptでSSL証明書を自動管理します。
services:
traefik:
image: traefik:v3
command:
- "--providers.docker=true"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.tlschallenge=true"
ports:
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
app:
labels:
- "traefik.http.routers.app.rule=Host(`example.com`)"
- "traefik.http.services.app.loadbalancer.server.port=3000"
Nginxは手動でのupstream定義とSSL終端設定が必要です。CaddyはTraefikと同様の自動HTTPSを提供しますが、動的再設定の柔軟性は劣ります。
9. 監視とバックアップ
監視スタックは独立した monitoring.yml で定義し、アプリケーションと関心を分離します。cAdvisor + Prometheus + Grafanaの組み合わせが定番です。
データベースのバックアップはcronで定期的に docker exec ダンプを実行します。
0 3 * * * docker exec postgres pg_dump -U user mydb > /backups/db_$(date +\%Y\%m\%d).sql
本番サービスには restart: unless-stopped を使用します。restart: always と異なり、手動で docker stop した後に再起動しないため、メンテナンス時の意図しない再起動を防げます。
まとめ
Docker Composeは適切な規模のデプロイメントにおいて、本番利用に十分耐えうるツールです。Composeファイルをコードとして扱い、カスケード型ヘルスチェックを設計し、リソース制限を設定し、ログを集中管理し、シークレットを適切に扱い、ローリング更新を実装する——これらのプラクティスにより、Composeは開発の便利ツールから堅牢なデプロイ基盤へと進化します。
シングルホストの限界を超える場合も、同じComposeファイルをDocker SwarmやKubernetesに移行する足がかりとして活用できます。
