言語を切り替える
テーマを切り替える

Docker Compose 本番デプロイの三要素:ヘルスチェック、再起動ポリシー、リソース制限

サーバーアラートの SMS が、ベッドから叩き起こしてきた。

PC を開くと、コンテナは running、緑の丸印も健在に見える。でもサービスにアクセスすると? 502 Bad Gateway。

DB コンテナの起動が終わる前に、アプリコンテナが慌てて接続しにいった。接続失敗、サービス停止。コンテナは「実行中」のまま、中身はとっくに死んでいた。

これが、初めて Docker Compose を本番環境へデプロイしたときの、生の経験だ。

「動く」と「安定して動き続ける」は別物だ。docker-compose up で一発起動できても、深夜のメモリ急増やプロセスクラッシュに耐えられるとは限らない。

Docker Compose 本番デプロイの三要素——ヘルスチェック、再起動ポリシー、リソース制限——が、この穴を埋める。本記事では、踏み抜いた坑からまとめた設定方法と、すぐコピーできる YAML テンプレートを共有する。コンテナを「動く」から「安定して動く」へ引き上げよう。

1. ヘルスチェック (healthcheck):コンテナが本当に利用可能か判断する

docker psrunning は、プロセスがまだ存在しているだけだ。サービスを正常に提供できるかは、別問題だ。

ヘルスチェックは、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 回連続失敗で即 unhealthystart_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_limitcpus(廃止予定)。

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.yamlcompose.yaml を上書きする。

ログ管理:ディスク枯渇を防ぐ

Docker のデフォルトログドライバ json-file は無制限に増える。制限なしだと数ヶ月でディスクがログで満杯になる。

logging:
  driver: json-file
  options:
    max-size: "10m"   # 1 ファイル最大 10MB
    max-file: "3"     # 最大 3 ファイル、合計 30MB

各コンテナ最大 30MB、自動ローテーション。この設定を全サービスに入れておけば、後から手動クリーンアップする手間が省ける。

まとめ

三要素の核心ロジックはこれだ:

ヘルスチェックで問題検出 → 再起動ポリシーで自動復旧 → リソース制限でクラッシュの波及を防止

この組み合わせで、Docker Compose アプリは次ができる:

  1. 起動時に依存サービスの本当の準備完了を待つ(盲目的に突っ込まない)
  2. クラッシュ後に自分で立ち上がる(深夜に起こされない)
  3. 1 コンテナの暴走がサーバー全体を落とさない

今すぐ docker-compose.yml を確認しよう。足りない設定はあるか? 足せ。

まだ本番で Docker Compose を使っていないなら、次回デプロイ時にこの 3 設定を持っていけ。後で感謝するはずだ。

Docker Compose 本番デプロイ三要素の設定

Docker Compose にヘルスチェック、再起動ポリシー、リソース制限を追加し、本番級の安定デプロイを構築

⏱️ 目安時間: 15 分

  1. 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

    ステップ2: 依存サービスの条件付き起動設定

    depends_on の condition パラメータを使用:

    • depends_on: [db] を depends_on: db: condition: service_healthy に変更
    • 依存サービスのヘルスチェックが設定されていることを確認
    • アプリサービスは依存サービスが本当に利用可能になるまで待ってから起動
  3. 3

    ステップ3: 再起動ポリシーの設定

    サービスタイプに応じて再起動ポリシーを選択:

    • コアサービス(Web/API/DB):restart: unless-stopped
    • バックグラウンドタスク/定期スクリプト:restart: on-failure:5
    • 開発デバッグ:restart: "no"
    • always は避ける(手動停止後に意図せず再起動する)
  4. 4

    ステップ4: リソース制限の設定

    deploy.resources で limits と reservations を設定:

    • limits: ハード制限、超過すると OOM Killer により停止
    • reservations: ソフト制限、Docker が保証する最小リソース
    • Node.js アプリ:limits.memory は最低 512M を推奨
    • DB は接続数とデータ量に応じて 1G-4G を設定
  5. 5

    ステップ5: ログローテーション設定の追加

    ログファイルがディスクを圧迫するのを防止:

    • logging.driver: json-file(デフォルトドライバ)
    • logging.options.max-size: "10m"(1 ファイル最大 10MB)
    • logging.options.max-file: "3"(3 ファイルを保持)
    • 合計ログ上限 30MB、自動ローテーション

FAQ

ヘルスチェックに失敗したらコンテナは自動的に再起動されますか?
されません。ヘルスチェックはコンテナを unhealthy とマークするだけで、自動再起動はトリガーしません。再起動ポリシー(unless-stopped など)と depends_on の service_healthy 条件を組み合わせて使います。コンテナプロセスがクラッシュして終了したときだけ、再起動ポリシーが介入します。
unless-stopped と always の違いは何ですか?
決定的な違いは手動停止後の挙動です。always では docker stop で手動停止した後も、サーバーや Docker サービスが再起動するとコンテナが自動起動します。unless-stopped では手動停止後は停止状態のままで、勝手に復活しません。本番環境では unless-stopped を推奨します。
リソース制限の limits と reservations の違いは何ですか?
limits はハード制限で、コンテナのメモリ使用量が limits.memory を超えると OOM Killer により停止されます。reservations はソフト制限で、Docker スケジューラーに「このコンテナは最低これだけ必要」と伝えますが、超過使用は可能です。CPU 制限はソフト(超過するとスロットリング)、メモリ制限はハード(超過すると停止)です。
start_period パラメータの役割は何ですか?
start_period は起動猶予期間です。この期間中のヘルスチェック失敗は retries にカウントされません。起動に時間がかかるアプリ(DB 接続に 40 秒かかるなど)では、start_period: 60s を設定すると、起動直後に unhealthy と誤判定されるのを防げます。
開発環境と本番環境で異なる設定を使うにはどうすればいいですか?
2 つの設定ファイルで分離します:

• 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 は使えますか?
使えます。deploy 設定は主に Docker Swarm 向けですが、Docker Compose V2.20+ は単一マシンでもサポートしています。古いバージョンでは --compatibility パラメータが必要か、廃止予定の mem_limit/cpus を使います。最新版 Docker Compose へのアップグレードを推奨します。

3分で読めます · 公開日: 2026年4月24日 · 更新日: 2026年6月8日

関連記事

コメント

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