Docker Compose 本番デプロイの三要素:ヘルスチェック、再起動ポリシー、リソース制限
サーバーアラートの SMS が、ベッドから叩き起こしてきた。
PC を開くと、コンテナは running、緑の丸印も健在に見える。でもサービスにアクセスすると? 502 Bad Gateway。
DB コンテナの起動が終わる前に、アプリコンテナが慌てて接続しにいった。接続失敗、サービス停止。コンテナは「実行中」のまま、中身はとっくに死んでいた。
これが、初めて Docker Compose を本番環境へデプロイしたときの、生の経験だ。
「動く」と「安定して動き続ける」は別物だ。docker-compose up で一発起動できても、深夜のメモリ急増やプロセスクラッシュに耐えられるとは限らない。
Docker Compose 本番デプロイの三要素——ヘルスチェック、再起動ポリシー、リソース制限——が、この穴を埋める。本記事では、踏み抜いた坑からまとめた設定方法と、すぐコピーできる YAML テンプレートを共有する。コンテナを「動く」から「安定して動く」へ引き上げよう。
1. ヘルスチェック (healthcheck):コンテナが本当に利用可能か判断する
docker ps の running は、プロセスがまだ存在しているだけだ。サービスを正常に提供できるかは、別問題だ。
ヘルスチェックは、Docker が定期的にコンテナの「健康診断」を行う仕組みだ。HTTP リクエスト、DB 接続、スクリプト実行などで、サービスが本当に生きているか確認する。
ヘルスチェックの仕組み
Docker は設定した間隔でコンテナ内にチェックコマンドを送る。終了コード 0 なら健康、0 以外なら不健康。連続失敗が続くと unhealthy とマークされる。
重要なのは、ヘルスチェック失敗だけでは自動再起動されないことだ。状態を可視化するだけで、「このコンテナに問題がある」と教えてくれる。自動復旧には depends_on の条件付き起動と再起動ポリシーの併用が必要だ。
押さえるべき 4 つのパラメータ
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s # チェック間隔
timeout: 10s # 1 回のタイムアウト
retries: 3 # 連続失敗で unhealthy に
start_period: 60s # 起動猶予期間(この間の失敗は retries に含めない)
以前、start_period を無視していた。アプリ起動に時間がかかり DB 接続に 40 秒必要なのに、ヘルスチェックは 10 秒で始まった。3 回連続失敗で即 unhealthy。start_period: 60s を足してから、初期化に十分な時間を確保できるようになった。
よく使うヘルスチェックコマンド
Web サービス:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
PostgreSQL:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Redis:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
depends_on で依存起動を実現
ヘルスチェックが最も実用的なのはここだ。依存サービスが本当に準備完了するまで待ってから起動する。
services:
app:
depends_on:
db:
condition: service_healthy # DB のヘルスチェック通過を待つ
redis:
condition: service_healthy # Redis のヘルスチェック通過を待つ
以前は depends_on: [db, redis] だった。アプリが起動しても DB はまだ初期化中。接続できず即エラー終了。condition: service_healthy に変えてから、アプリは pg_isready に応答できるまで待ってから起動する。ようやく静かになった。
2. 再起動ポリシー (restart):コンテナに自己修復能力を持たせる
コンテナが落ちたら、誰が起こす?
手動の docker restart? 深夜 3 時のアラートで試してみてほしい。
再起動ポリシーは、Docker デーモンにこの作業を任せる仕組みだ。コンテナ終了後、Docker が再起動するか自動判断する。
4 つのポリシー比較
| ポリシー | 動作 | 適用シナリオ |
|---|---|---|
no | 落ちたらそのまま、再起動しない | 一時テスト、CI/CD |
always | どんな終了でも再起動 | コアサービス |
on-failure | 異常終了時のみ再起動 | タスク型コンテナ |
unless-stopped | 手動停止以外は常に再起動 | 本番の第一候補 |
本番の第一候補:unless-stopped
restart: unless-stopped
なぜ always ではなく unless-stopped か?
違いは 手動 docker stop 後の挙動 だ。
always:手動停止後、システムや Docker サービスが再起動すると、コンテナも自動起動するunless-stopped:手動停止後は停止のまま、勝手に復活しない
メンテナンスで手動停止したのに、サーバー再起動後に勝手に起動していたら、「何をしているんだ」と言いたくなる。
on-failure の再試行上限
on-failure には再試行回数を設定できる:
restart: on-failure:5 # 最大 5 回再起動
連続 5 回起動失敗で Docker は諦める。DB 接続不可や設定ミスなど外部要因で繰り返しクラッシュするケース向けで、無限再起動ループを防ぐ。
選び方(シンプル版)
- コアサービス(Web、API、DB):
unless-stopped - バックグラウンドタスク、定期スクリプト:
on-failure - 開発デバッグ、一時実行:
no
落とし穴:再起動ポリシーは「コンテナ終了後に再起動するか」だけを管轄する。「サービスが本当に使えるか」は管轄外だ。組み合わせるならヘルスチェックが必要だ。
3. リソース制限 (deploy.resources):コンテナの暴走を防ぐ
1 つのコンテナがメモリリークし、サーバーのメモリを全部食い、他コンテナが OOM Killer に一斉処刑——経験した。あの悔しさは今でも覚えている。
リソース制限は、各コンテナに「天井」を設ける仕組みだ。上限を超えたらそのコンテナを止め、他サービスを守る。
limits と reservations
deploy:
resources:
limits:
cpus: '1.0' # 最大 1 CPU
memory: 512M # 最大 512MB
reservations:
cpus: '0.5' # 最低 0.5 CPU を確保
memory: 256M # 最低 256MB を確保
- limits:ハード制限、超過でプロセス停止(OOM)
- reservations:ソフト制限、スケジューラーに「最低これだけ必要」と伝える
端的に言えば、limits は「超えてはいけない」、reservations は「最低保証」だ。
CPU 制限の設定
cpus: '1.0' # 最大 1 コアをフル使用
cpus: '0.5' # 最大 CPU の 50%
cpus: '2.0' # 最大 2 コア
CPU 制限はソフトだ。超過してもスロットリングされるだけで、停止されない。少し高めに設定してもよい。
メモリ制限の設定
memory: 512M # 512MB
memory: 2G # 2GB
メモリ制限はハードだ。超過したら OOM Killer で即停止、交渉なし。
経験値:
- Node.js アプリ:最低
512M、本番は1G推奨 - Python アプリ:
256M - 512M - PostgreSQL:接続数とデータ量に応じて
1G - 4G - Redis:
256M - 512M、キャッシュ用途ならもっと大きく
実践設定例
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
この設定では、アプリは最大 1 CPU・1GB メモリまで使えるが、Docker は最低 0.25 CPU・256MB を保証する。
注意:deploy は主に Docker Swarm 向けだ。単一マシンで docker-compose up する場合、リソース制限は Docker Compose V2 または docker-compose --compatibility が必要。Compose V2.20+ なら deploy を直接使える。汎用的な旧写法は mem_limit と cpus(廃止予定)。
4. 完全設定テンプレート:コピーして使える本番級 YAML
三要素は単独でも効くが、組み合わせてこそ本番級だ。以下は Web + PostgreSQL + Redis の完全例。コピーして改造できる。
完全例
version: '3.8'
services:
# Web アプリ
app:
image: myapp:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
logging:
driver: json-file
options:
max-size: "10m" # 1 ファイル最大 10MB
max-file: "3" # 最大 3 ファイル保持
# PostgreSQL
db:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: apppassword
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
volumes:
- pgdata:/var/lib/postgresql/data
# Redis
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 128M
volumes:
pgdata:
設定分離のコツ
開発と本番では設定が異なる。2 ファイルで分けて管理する:
# compose.yaml - 開発環境
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
restart: "no" # 開発中は自動再起動しない
# compose.production.yaml - 本番オーバーライド
version: '3.8'
services:
app:
image: myapp:v1.2.3 # 本番はビルド済みイメージ
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 1G
本番起動:
docker-compose -f compose.yaml -f compose.production.yaml up -d
2 ファイルがマージされ、compose.production.yaml が compose.yaml を上書きする。
ログ管理:ディスク枯渇を防ぐ
Docker のデフォルトログドライバ json-file は無制限に増える。制限なしだと数ヶ月でディスクがログで満杯になる。
logging:
driver: json-file
options:
max-size: "10m" # 1 ファイル最大 10MB
max-file: "3" # 最大 3 ファイル、合計 30MB
各コンテナ最大 30MB、自動ローテーション。この設定を全サービスに入れておけば、後から手動クリーンアップする手間が省ける。
まとめ
三要素の核心ロジックはこれだ:
ヘルスチェックで問題検出 → 再起動ポリシーで自動復旧 → リソース制限でクラッシュの波及を防止
この組み合わせで、Docker Compose アプリは次ができる:
- 起動時に依存サービスの本当の準備完了を待つ(盲目的に突っ込まない)
- クラッシュ後に自分で立ち上がる(深夜に起こされない)
- 1 コンテナの暴走がサーバー全体を落とさない
今すぐ docker-compose.yml を確認しよう。足りない設定はあるか? 足せ。
まだ本番で Docker Compose を使っていないなら、次回デプロイ時にこの 3 設定を持っていけ。後で感謝するはずだ。
Docker Compose 本番デプロイ三要素の設定
Docker Compose にヘルスチェック、再起動ポリシー、リソース制限を追加し、本番級の安定デプロイを構築
⏱️ 目安時間: 15 分
- 1
ステップ1: ヘルスチェック設定の追加
各サービスに healthcheck 設定を追加:
• test: チェックコマンド(curl、pg_isready、redis-cli ping など)
• interval: チェック間隔(推奨 10-30s)
• timeout: タイムアウト時間(推奨 5-10s)
• retries: 失敗回数(推奨 3-5 回)
• start_period: 起動猶予期間(アプリ起動時間に応じて 30-60s) - 2
ステップ2: 依存サービスの条件付き起動設定
depends_on の condition パラメータを使用:
• depends_on: [db] を depends_on: db: condition: service_healthy に変更
• 依存サービスのヘルスチェックが設定されていることを確認
• アプリサービスは依存サービスが本当に利用可能になるまで待ってから起動 - 3
ステップ3: 再起動ポリシーの設定
サービスタイプに応じて再起動ポリシーを選択:
• コアサービス(Web/API/DB):restart: unless-stopped
• バックグラウンドタスク/定期スクリプト:restart: on-failure:5
• 開発デバッグ:restart: "no"
• always は避ける(手動停止後に意図せず再起動する) - 4
ステップ4: リソース制限の設定
deploy.resources で limits と reservations を設定:
• limits: ハード制限、超過すると OOM Killer により停止
• reservations: ソフト制限、Docker が保証する最小リソース
• Node.js アプリ:limits.memory は最低 512M を推奨
• DB は接続数とデータ量に応じて 1G-4G を設定 - 5
ステップ5: ログローテーション設定の追加
ログファイルがディスクを圧迫するのを防止:
• logging.driver: json-file(デフォルトドライバ)
• logging.options.max-size: "10m"(1 ファイル最大 10MB)
• logging.options.max-file: "3"(3 ファイルを保持)
• 合計ログ上限 30MB、自動ローテーション
FAQ
ヘルスチェックに失敗したらコンテナは自動的に再起動されますか?
unless-stopped と always の違いは何ですか?
リソース制限の limits と reservations の違いは何ですか?
start_period パラメータの役割は何ですか?
開発環境と本番環境で異なる設定を使うにはどうすればいいですか?
• compose.yaml:開発環境(build: ., restart: "no")
• compose.production.yaml:本番オーバーライド(image: xxx, restart: unless-stopped)
• 起動コマンド:docker-compose -f compose.yaml -f compose.production.yaml up -d
• 後者が前者の設定を上書きし、設定分離を実現
単一マシンのデプロイで deploy.resources は使えますか?
3分で読めます · 公開日: 2026年4月24日 · 更新日: 2026年6月8日
Docker 実践ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Docker Compose 複数サービス連携:ローカル開発環境をワンコマンドで起動
Docker Compose で複数サービスを連携し、Web・API・MySQL・Redis のローカル開発環境をワンコマンドで起動。手動インストールの煩雑さ、バージョン衝突、ポート競合を一掃し、新メンバーは clone から 5 分で開発開始、プロジェクト切り替えも数秒で完了します。
第 6 / 37 記事
次の記事
Dockerfile 最適化実践:5 つのテクニックでイメージを 80% スリム化
Docker イメージが数 GB に膨らんでいませんか?Alpine ベースイメージ、RUN の統合、マルチステージビルド、.dockerignore、キャッシュ削除の 5 テクニックで 1.2GB を 180MB(85% 削減)まで落とす手順。Node.js 完全事例と実測データ付き。
第 8 / 37 記事
関連記事
Dockerfile入門:ゼロから最初の Docker イメージを作る(実例付き)
Dockerfile入門:ゼロから最初の Docker イメージを作る(実例付き)
Docker vs 仮想マシン:5分で理解する性能差とシーン別選び方ガイド
Docker vs 仮想マシン:5分で理解する性能差とシーン別選び方ガイド
Docker インストールの落とし穴ガイド 2025:permission denied から正常起動までの完全解決策
コメント
GitHubアカウントでログインしてコメントできます