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

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_limitcpus(廃止予定)を使うか、直接 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. 起動時に依存サービスが本当に準備完了になるまで待機し、盲目的に突っ込まない
  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/データベース):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 を推奨
    • データベースは接続数とデータ量に応じて 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 にカウントされません。起動に時間がかかるアプリケーション(データベース接続に 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 へのアップグレードを推奨します。

5 min read · 公開日: 2026年4月24日 · 更新日: 2026年4月25日

関連記事

コメント

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