Docker Composeプロダクションデプロイ実践:ヘルスチェックからゼロダウンタイム更新まで7つの重要戦略
「私の環境では動くのに」——プロダクション環境のコンテナの墓場
すべての開発者の口癖:「私の環境では動くのに」。しかし、コンテナがプロダクション環境にデプロイされると、本当の悪夢が始まります:
- コンテナが静かにOOM Killedされ、ログには
Out of memoryの1行だけ - データベースがまだ起動していないのに、アプリコンテナが
Connection refusedを連発 - 深夜3時にコンテナがクラッシュ、再起動ポリシーなしで朝までサービス停止
- ログファイルがディスクを圧迫、
docker logsが数十GBの非構造化テキストを出力 - プロダクション設定が
docker-compose.ymlに平文で書かれ、データベースパスワードが露出
プロダクション環境でまだ docker compose up -d を一発で実行しているなら、この記事はあなたのためです。
コア概念クイックリファレンス
| 概念 | 役割 | プロダクション環境の主要設定 |
|---|---|---|
| Health Check | コンテナが本当に利用可能か検出 | healthcheck + depends_on.condition |
| Resource Limits | CPU/メモリを制限、リソース独占を防止 | deploy.resources.limits |
| Restart Policy | 異常終了後の自動再起動 | deploy.restart_policy |
| Secrets | 機密情報の暗号化保存 | secrets + Docker Secret |
| Logging Driver | 構造化ログ + ログローテーション | logging.driver + logging.options |
| Profiles | 環境ごとの選択的サービス起動 | profiles |
| Watch | ファイル変更をコンテナに自動同期 | watch (Compose Watch) |
プロダクション環境の5つの課題
課題1:コンテナ起動順序の制御不能
データベースがまだ初期化中に、アプリコンテナが接続を試みて起動に失敗。depends_on は起動順序のみを保証し、サービスの準備完了は保証しません。
課題2:リソースの無限膨張
リソース制限のないコンテナはブレーキのない車のようなもの。1つのメモリリークコンテナがホスト全体のメモリを消費し、すべてのサービスをダウンさせます。
課題3:ログのブラックホール
デフォルトの json-file ログドライバはローテーションしません。3ヶ月運用後、/var/lib/docker がディスクを埋め尽くし、全サービスがクラッシュします。
課題4:機密情報の漏洩
environment にデータベースパスワードを平文で記述、.env ファイルをGitリポジトリにコミット、イメージにAPI Keyをハードコード——これらはすべてプロダクション事故の時限爆弾です。
課題5:更新即ダウンタイム
docker compose up -d はデフォルトで古いコンテナを停止してから新しいコンテナを起動するため、更新中はサービスが利用不可になります。24時間365日稼働のサービスには許容できません。
7つの実践パターン
パターン1:ヘルスチェックと依存関係の順序付け
問題:depends_on は起動順序のみを制御し、サービスの準備完了を保証しません。
解決策:healthcheck + depends_on.condition: service_healthy を使用。
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
app:
image: myapp:latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
主要パラメータの説明:
interval:チェック間隔、プロダクション環境では5-10秒を推奨timeout:単一チェックのタイムアウト、3-5秒を推奨retries:連続失敗回数、超過でunhealthyにマークstart_period:コンテナ起動後の猶予期間、この間の失敗はretriesに含まない
パターン2:リソース制限とOOM保護
問題:制限のないコンテナはホストリソースを奪い合い、1つの暴走コンテナがマシン全体をダウンさせます。
解決策:deploy.resources でCPUとメモリのlimitsとreservationsを設定。
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
worker:
image: myworker:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
memory: 512M
limits vs reservations:
limits:ハード上限、超過するとOOM KillまたはCPU制限reservations:ソフト保証、スケジューラは満たそうとするが強制しない
OOM保護戦略:
services:
critical-service:
image: critical-app:latest
deploy:
resources:
limits:
memory: 1G
cap_add:
- SYS_PTRACE
ホストレベルで vm.overcommit_memory を設定し、OOMポリシーを調整:
# コンテナのOOM Scoreを確認
docker inspect --format='{{.State.OOMKilled}}' <container_id>
# ホストのOOMポリシー設定:重要プロセスをkillしない
echo -1000 > /proc/<pid>/oom_score_adj
パターン3:構造化ログとログローテーション
問題:デフォルトの json-file ログドライバはローテーションせず、ディスクが圧迫されます。
解決策:ログドライバ + ローテーションポリシーを設定。プロダクション環境では local ドライバを推奨。
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
tag: "{{.Name}}/{{.ID}}"
nginx:
image: nginx:1.27-alpine
logging:
driver: json-file
options:
max-size: "50m"
max-file: "10"
tag: "nginx/{{.Name}}"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ログドライバ比較:
| ドライバ | 適用ケース | メリット | デメリット |
|---|---|---|---|
| local | プロダクション環境のデフォルト推奨 | 自動ローテーション、圧縮保存 | ローカルのみ |
| json-file | 構造化JSONログが必要な場合 | Dockerネイティブサポート | 手動ローテーション設定が必要 |
| syslog | 集中ログ収集 | リモート送信可能 | 設定が複雑 |
| fluentd | EFKスタックとの統合 | 柔軟なログルーティング | Fluentdの追加デプロイが必要 |
アプリケーションレベルの構造化ログ:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV TZ=Asia/Tokyo
ENV LOG_FORMAT=json
CMD ["node", "server.js"]
const logger = {
info: (msg, meta = {}) => {
console.log(JSON.stringify({ level: 'info', msg, ts: new Date().toISOString(), ...meta }));
},
error: (msg, meta = {}) => {
console.error(JSON.stringify({ level: 'error', msg, ts: new Date().toISOString(), ...meta }));
}
};
パターン4:Secrets管理
問題:機密情報がcomposeファイルや環境変数に平文で保存されている。
解決策:Docker Secrets + _FILE サフィックス環境変数。
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
image: myapp:latest
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
API_KEY_FILE: /run/secrets/api_key
JWT_SECRET_FILE: /run/secrets/jwt_secret
secrets:
- api_key
- jwt_secret
Secretsファイル管理:
# secretsディレクトリの作成
mkdir -p secrets
chmod 700 secrets
# secretファイルの書き込み
echo "my-super-secret-password-2026" > secrets/db_password.txt
echo "ak-live-xxxx-yyyy-zzzz" > secrets/api_key.txt
echo "jwt-hs256-secret-key-here" > secrets/jwt_secret.txt
# パーミッション設定:rootのみ読み取り可能
chmod 600 secrets/*.txt
.gitignore に必ず含める:
secrets/
*.secret
.env.production
.env.staging
Docker Secrets vs .env 比較:
| 特徴 | Docker Secrets | .envファイル |
|---|---|---|
| 暗号化保存 | あり(Swarmモード) | なし |
| ファイル権限 | 制限あり(/run/secrets/) | ファイルシステム権限に依存 |
| 監査トレース | あり | なし |
| クロスノード同期 | Swarmが自動同期 | 手動配布が必要 |
| 適用ケース | Swarm/単一ホストプロダクション | 開発環境 |
パターン5:ゼロダウンタイムローリングアップデート
問題:docker compose up -d はデフォルトで古いコンテナを停止してから新しいコンテナを起動する。
解決策:docker compose up --no-down + ヘルスチェック + リバースプロキシ。
services:
app:
image: myapp:${APP_VERSION:-latest}
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 0
order: stop-first
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 3
start_period: 15s
labels:
- "com.toolsku.app=true"
ports:
- "3000-3001:3000"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 3s
retries: 3
Nginxリバースプロキシ設定:
upstream app_backend {
server app:3000;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_502 http_503;
}
location /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
ゼロダウンタイム更新スクリプト:
#!/bin/bash
set -euo pipefail
NEW_IMAGE="myapp:v2.0.0"
echo "🚀 Starting zero-downtime update to ${NEW_IMAGE}"
# 1. 新しいイメージをプル
docker compose pull app
# 2. 新しいコンテナを起動(古いコンテナは停止しない)
docker compose up -d --no-deps --scale app=2 app
# 3. 新しいコンテナがヘルシーになるまで待機
echo "⏳ Waiting for new containers to be healthy..."
sleep 15
# 4. 新しいコンテナのヘルスチェック検証
for i in $(seq 1 30); do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "✅ New container is healthy"
break
fi
if [ $i -eq 30 ]; then
echo "❌ Health check failed, rolling back..."
docker compose up -d --no-deps --scale app=1 app
exit 1
fi
sleep 2
done
# 5. レプリカ数を1に縮小
docker compose up -d --no-deps --scale app=1 app
echo "🎉 Update completed successfully"
パターン6:Prometheus + Grafanaモニタリングスタック
問題:モニタリングなしのプロダクション運用は盲目飛行——問題はユーザーからの報告でしか分かりません。
解決策:Prometheus + Grafanaの完全なモニタリングスタックをデプロイ。
services:
prometheus:
image: prom/prometheus:v2.52.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=5GB'
deploy:
resources:
limits:
memory: 512M
networks:
- monitoring
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
secrets:
- grafana_password
volumes:
- grafana_data:/var/lib/grafana
deploy:
resources:
limits:
memory: 256M
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:v1.8.0
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks:
- monitoring
secrets:
grafana_password:
file: ./secrets/grafana_password.txt
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Prometheus設定:
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'app'
static_configs:
- targets: ['app:3000']
metrics_path: /metrics
アラートルール:
# monitoring/alert_rules.yml
groups:
- name: container_alerts
rules:
- alert: ContainerDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.instance }} is down"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes / (1024 * 1024) > 400
for: 5m
labels:
severity: warning
annotations:
summary: "Memory usage exceeds 400MB on {{ $labels.instance }}"
- alert: DiskSpaceLow
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space below 10% on {{ $labels.instance }}"
パターン7:マルチ環境設定(dev/staging/prod)
問題:開発、ステージング、プロダクションの設定が混在し、ある環境の設定変更が他の環境に影響するリスクがある。
解決策:docker-compose.override.yml + マルチファイルオーバーレイ戦略。
ディレクトリ構造:
project/
├── docker-compose.yml # ベース設定
├── docker-compose.override.yml # 開発環境オーバーレイ(自動ロード)
├── docker-compose.staging.yml # ステージング環境オーバーレイ
├── docker-compose.prod.yml # プロダクション環境オーバーレイ
├── .env # デフォルト環境変数
├── .env.staging # ステージング環境変数
├── .env.prod # プロダクション環境変数
├── monitoring/
│ ├── prometheus.yml
│ └── alertmanager.yml
└── secrets/
├── db_password.txt
├── api_key.txt
└── grafana_password.txt
ベース設定 docker-compose.yml:
services:
app:
image: myapp:${APP_VERSION:-latest}
environment:
NODE_ENV: ${NODE_ENV:-development}
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
postgres_data:
networks:
app-network:
driver: bridge
開発環境オーバーレイ docker-compose.override.yml(自動ロード):
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229"
environment:
NODE_ENV: development
LOG_LEVEL: debug
deploy:
resources:
limits:
memory: 512M
command: node --inspect=0.0.0.0:9229 server.js
adminer:
image: adminer:latest
ports:
- "8080:8080"
networks:
- app-network
プロダクション環境オーバーレイ docker-compose.prod.yml:
services:
app:
image: myapp:${APP_VERSION}
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: info
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
postgres:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
memory: 1G
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
redis:
deploy:
resources:
limits:
memory: 512M
command: redis-server --appendonly yes --maxmemory 400mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
deploy:
resources:
limits:
memory: 128M
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
volumes:
redis_data:
起動コマンド:
# 開発環境(override自動ロード)
docker compose up -d
# ステージング環境
docker compose -f docker-compose.yml -f docker-compose.staging.yml --env-file .env.staging up -d
# プロダクション環境
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d
5つのよくある落とし穴
落とし穴1:depends_on ≠ サービス準備完了
❌ 間違った書き方:
services:
app:
depends_on:
- postgres
# postgresコンテナは起動したが、DBの初期化が完了していない可能性
✅ 正しい書き方:
services:
app:
depends_on:
postgres:
condition: service_healthy
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
落とし穴2:リソース制限なし
❌ 間違った書き方:
services:
app:
image: myapp:latest
# リソース制限なし — 1つのメモリリークでホスト全体を消費
✅ 正しい書き方:
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
落とし穴3:ログローテーションなし
❌ 間違った書き方:
services:
app:
image: myapp:latest
# デフォルトのjson-fileドライバ、ログが無限に増大
✅ 正しい書き方:
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
落とし穴4:機密情報の平文保存
❌ 間違った書き方:
services:
postgres:
environment:
POSTGRES_PASSWORD: "my-secret-password-123"
# パスワードがcomposeファイルに平文 — Gitにコミットしたら終わり
✅ 正しい書き方:
services:
postgres:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
落とし穴5:latestタグの使用
❌ 間違った書き方:
services:
app:
image: myapp:latest
# プルのたびに異なるイメージの可能性 — 再現不可能
✅ 正しい書き方:
services:
app:
image: myapp:2.1.0
# または変数でバージョン管理
image: myapp:${APP_VERSION:-2.1.0}
エラートラブルシューティングクイックリファレンス
| エラーメッセージ | 原因 | 解決策 |
|---|---|---|
OOMKilled |
コンテナがメモリ制限を超過 | memory 制限を増やすか、アプリのメモリ使用を最適化 |
Connection refused |
依存サービスが準備未完了 | healthcheck + depends_on.condition を追加 |
no space left on device |
ログ/イメージがディスクを圧迫 | logging ローテーション設定 + docker system prune |
restarting ループ |
アプリが起動時にクラッシュ | docker logs <id> でログを確認、設定をチェック |
permission denied |
ファイル/ディレクトリの権限問題 | user ディレクティブとvolume権限を確認 |
port is already allocated |
ポート競合 | ポートマッピングを変更するか、競合プロセスを停止 |
unhealthy ステータス |
ヘルスチェック失敗 | healthcheck コマンドが正しいか確認 |
secret not found |
Secretファイルが存在しない | secrets/ ディレクトリに対応ファイルが存在するか確認 |
Cannot connect to the Docker daemon |
Dockerが未実行 | systemctl start docker |
image pulling failed |
イメージプル失敗 | ネットワーク/レジストリ認証/イメージ名スペルを確認 |
高度な最適化
Docker Compose Watchで開発を加速
Compose Watchはファイル変更時にコンテナに自動同期し、イメージの再ビルドが不要:
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
- action: sync+restart
path: ./config
target: /app/config
# watchモードで起動
docker compose watch
ネットワーク分離とセキュリティ
services:
app:
networks:
- frontend
- backend
postgres:
networks:
- backend
# postgresはfrontendネットワークにいないため、外部から直接アクセス不可
nginx:
networks:
- frontend
ports:
- "80:80"
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
# internal:true で外部アクセスを禁止
イメージ最適化とマルチステージビルド
# ---- ビルドステージ ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- 実行ステージ ----
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
オーケストレーションツール比較
| 特徴 | Docker Compose | Kubernetes | Nomad | Docker Swarm |
|---|---|---|---|---|
| 複雑さ | 低 | 高 | 中 | 低 |
| 単一ホストデプロイ | ✅ 優秀 | ❌ 過剰 | ⚠️ 使用可能 | ✅ 優秀 |
| マルチホストオーケストレーション | ❌ 非対応 | ✅ コア機能 | ✅ コア機能 | ⚠️ 基本 |
| オートスケーリング | ❌ | ✅ HPA/VPA | ✅ | ⚠️ 手動 |
| ローリングアップデート | ⚠️ スクリプト必要 | ✅ ネイティブ | ✅ ネイティブ | ✅ ネイティブ |
| サービスディスカバリ | ⚠️ DNS | ✅ CoreDNS | ✅ Consul | ✅ DNS |
| ストレージオーケストレーション | ❌ | ✅ CSI | ✅ CSI | ⚠️ 基本 |
| 学習曲線 | 低 | 高 | 中 | 低 |
| 適用スケール | 1-10サービス | 100+サービス | 50+サービス | 10-50サービス |
| プロダクション対応 | ✅ 単一ホスト | ✅ 大規模 | ✅ 中〜大規模 | ⚠️ コミュニティ低下 |
選定の推奨:単一ホスト/小規模プロダクション環境にはDocker Compose、中〜大規模にはKubernetes、HashiCorpエコシステムならNomad。Docker Swarmは徐々にマイナー化しているため、新規プロジェクトには推奨しません。
まとめ
Docker Composeのプロダクションデプロイは、単なる
docker compose up -dではありません。ヘルスチェックでサービスの準備完了を保証し、リソース制限でOOM雪崩を防ぎ、ログローテーションでディスク枯渇を回避し、Secretsで機密情報を保護し、ゼロダウンタイム更新で24時間稼働を保障し、モニタリングスタックで盲目飛行を脱し、マルチ環境設定でdev/staging/prodを適切に分離します。この7つの戦略をマスターすれば、Docker Composeは中小規模のプロダクションデプロイに十分対応できます。
おすすめツール
- JSONフォーマッター - Docker Compose YAML関連のJSON設定をフォーマット
- Base64エンコード - Secretsや機密設定情報をエンコード
- ハッシュ計算 - 設定ファイルのチェックサムを生成し、デプロイの一貫性を確保
ブラウザローカルツールを無料で試す →