Docker Compose 本番デプロイの三要素:ヘルスチェック、再起動ポリシー、リソース制限
深夜3時、サーバーからのアラートメールで目が覚めました。
PC を開いて確認すると、コンテナの状態は running で、緑の丸が健康的に見えます。しかし、サービスにアクセスすると? 502 Bad Gateway です。
データベースコンテナの起動がまだ完了していないのに、アプリケーションコンテナが既に接続を試みていました。接続に失敗し、サービスがダウン。コンテナは「実行中」ですが、サービスは既に停止していました。
これが、私が初めて Docker Compose を本番環境にデプロイした時の実体験です。
「動く」ことと「安定して動く」ことは全く別物です。docker-compose up で一発起動できても、深夜にメモリが爆発したりプロセスがクラッシュした時に、果たして立ち上がっていられるでしょうか。
Docker Compose 本番デプロイの三要素、ヘルスチェック、再起動ポリシー、リソース制限、これらがこの問題を解決します。この記事では、私が試行錯誤の中で学んだ設定方法を共有します。そのままコピーできる YAML テンプレート付きで、コンテナを「動く」状態から「安定して動く」状態へと進化させましょう。
1. ヘルスチェック (healthcheck):コンテナが本当に利用可能か判断する
docker ps で表示される running 状態は、コンテナプロセスがまだ存在していることを示しているだけです。しかし、そのコンテナが正常にサービスを提供できるかどうかは分かりません。
ヘルスチェックとは、Docker が定期的にコンテナの「健康診断」を行う仕組みです。HTTP リクエストを送信したり、データベースに接続したり、スクリプトを実行したりして、サービスが本当に生きているかを確認します。
ヘルスチェックの仕組み
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 パラメータを無視していました。その結果、アプリケーションの起動が遅く、データベース接続に 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 # データベースのヘルスチェック通過を待機
redis:
condition: service_healthy # Redis のヘルスチェック通過を待機
以前、私は depends_on: [db, redis] を使っていました。その結果、アプリケーションコンテナが起動しても、データベースはまだ初期化中です。接続できず、直接エラーで終了していました。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 は諦めます。外部要因(データベース接続不可、設定エラー)で繰り返しクラッシュするシナリオに適しています。無限再起動ループを防止します。
どう選ぶ? 簡単にまとめると
- コアサービス(Web、API、データベース):
unless-stopped - バックグラウンドタスク、定期スクリプト:
on-failure - 開発デバッグ、一時実行:
no
一つの落とし穴:再起動ポリシーは「コンテナ終了後に再起動するかどうか」だけを管理します。「サービスが本当に利用可能かどうか」は管理しません。組み合わせて使うには、ヘルスチェックが必要です。
3. リソース制限 (deploy.resources):コンテナの暴走を防止する
こんな経験はありませんか? 一つのコンテナがメモリリークを起こし、サーバーの全メモリを消費して、他のコンテナが全て 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' # 最大 CPU 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 パラメータが必要です。より汎用的な単一マシンの書き方は mem_limit と cpus(廃止予定)を使うか、直接 deploy を使う(Compose V2.20+ サポート)です。
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/データベース):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 を推奨
• データベースは接続数とデータ量に応じて 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 は使えますか?
5 min read · 公開日: 2026年4月24日 · 更新日: 2026年4月25日
Docker 実践ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Docker Compose マルチサービスオーケストレーション:ローカル開発環境を一発起動
Docker Compose でマルチサービスをオーケストレーションし、Web、API、MySQL、Redis のローカル開発環境を一発起動。手動インストールの煩雑さ、バージョン競合、ポート競合の問題を完全解決。チームの新メンバーはリポジトリを clone して 5 分で開発開始、プロジェクト切り替えも秒単位で完了。
第 6 / 33 記事
次の記事
Dockerfile最適化術:5つの秘訣でイメージサイズを80%削減する
Dockerイメージが数GBになっていませんか?Alpine基礎イメージ、RUNコマンドの結合、マルチステージビルド、.dockerignore設定、キャッシュ削除など、5つのテクニックを駆使してイメージを1.2GBから180MBまで85%削減した実例と全手法を解説。
第 8 / 33 記事
関連記事
Dockerfile入門ガイド:ゼロから作る最初のDockerイメージ(実例付き)
Dockerfile入門ガイド:ゼロから作る最初のDockerイメージ(実例付き)
Docker vs 仮想マシン:5分でわかる性能差と選び方
Docker vs 仮想マシン:5分でわかる性能差と選び方
Dockerインストール完全ガイド2025:Permission Deniedから成功までの全手順

コメント
GitHubアカウントでログインしてコメントできます