Docker Compose のヘルスチェック設定完全ガイド|depends_on で起動順序を制御する実践チュートリアル

IT実務・技術メモ

“`html

Docker Compose のヘルスチェック設定完全ガイド|depends_on で起動順序を制御する実践チュートリアル

マルチコンテナ環境での起動順序問題、本当の原因は「コンテナの起動」と「サービスの準備完了」の混同にありました

Docker Compose でマルチコンテナアプリケーションを構築していると、避けられない課題に直面します。それは「データベースコンテナは起動しているのに、アプリケーションサーバーが接続できない」という現象です。

単純に depends_on で依存関係を定義しても、コンテナのプロセスが起動したことと、その中で実行されているサービスが実際に接続を受け付ける状態になったことは完全に別の問題です。PostgreSQL であれば、プロセスが起動した後も、ディスク I/O やメモリ初期化など複数の段階を経る必要があります。この準備中にアプリケーションが接続を試みると、接続拒否でエラーが発生し、アプリケーション全体が起動に失敗してしまいます。

実際のプロダクション環境では、このような「起動順序の不完全さ」が本番環境でのランダムな障害につながり、デプロイ時間が大幅に増加し、チーム全体の生産性が低下します。

この記事では、Docker Compose のヘルスチェック機能と depends_on を組み合わせて、確実にサービスの準備完了まで待機し、起動順序を完全に制御する実践的な方法を、実装例を交えてステップバイステップで解説します。記事を読み終わった後、あなたは本番環境で信頼できるマルチコンテナアプリケーションを構築できるようになります。

従来の depends_on だけでは、起動順序の30〜50%の問題しか解決できていません

多くのエンジニアは、Docker Compose で depends_on を設定しただけで「起動順序は完璧だ」と考えてしまいます。しかし実際には、コンテナ起動とサービス準備完了は全く異なるステップです。

以下は、従来の depends_on だけを使った設定です。

version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb

  app:
    image: myapp:latest
    depends_on:
      - db
    ports:
      - "8000:8000"

この設定では、app サービスは db サービスのコンテナが起動した直後に起動が試みられます。しかし PostgreSQL のプロセスが起動しただけで、実際にクエリを受け付ける状態になるまでには、通常3〜10秒程度の時間が必要です。この間隔でアプリケーションが接続を試みると、「接続拒否」エラーが発生し、アプリケーション自体も起動に失敗してしまいます。

さらに問題は、この現象がランダムに発生することです。ローカル環境ではうまくいったのに、本番環境で突然障害になることも珍しくありません。その理由は、ハードウェアのスペックやディスク速度によってサービスの起動時間が変わるためです。

ヘルスチェック機能が、サービスの「本当の準備完了」を判定します

Docker Compose のヘルスチェック機能は、コンテナ内で定期的に指定したコマンドを実行し、そのサービスが正常に動作しているかをリアルタイムで判定します。単なるコンテナプロセスの確認ではなく、サービスが実際に接続可能な状態に達しているかを検証できます。

ヘルスチェックの判定結果は、以下の4つの状態を持ちます。

状態 説明 対応すべき処置
starting コンテナ起動直後、ヘルスチェック開始前の状態 待機を継続
healthy ヘルスチェックコマンドが成功した状態 依存するサービスの起動を開始
unhealthy ヘルスチェックコマンドが失敗した状態 エラーログを確認して対応
none ヘルスチェックが設定されていない状態 従来の depends_on のみの動作

この仕組みを使うことで、アプリケーションはデータベースが「完全に利用可能な状態」になるまで待機できるようになります。

PostgreSQL のヘルスチェック設定で、確実なサービス起動を実現する

ここからは、実装レベルの具体的な設定を見ていきます。以下の例は、PostgreSQL にヘルスチェックを設定した docker-compose.yml です。

version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

各パラメータの詳細な説明は以下の通りです。

パラメータ 意味 推奨値 調整時のポイント
test 実行するコマンド。CMD または CMD-SHELL を指定 ["CMD-SHELL", "pg_isready -U admin"] シェルコマンドが必要な場合は CMD-SHELL を使用
interval ヘルスチェック実行の間隔(秒単位) 5秒 高負荷環境では10秒以上に延長して CPU 負荷を軽減
timeout コマンド実行のタイムアウト時間(秒単位) 3秒 ネットワーク遅延が大きい環境では5秒以上に設定
retries 何回失敗したら unhealthy と判定するか 5回 より厳しい判定が必要な場合は3回に縮小
start_period コンテナ起動から最初のヘルスチェック実行までの待機時間 10〜30秒 データベースの初期化に時間がかかる場合は30秒以上に設定

上記の例では、PostgreSQL が起動してから10秒待機した後、5秒ごとに pg_isready コマンドでポート接続可能性を確認します。このコマンドは、PostgreSQL が接続を受け付ける状態にあるかを判定する標準ツールで、返り値が0の場合は healthy 状態となります。

Docker Compose 2.1 以降の depends_on で条件付き起動制御を実装する

Docker Compose 2.1 以降では、depends_oncondition パラメータを指定できます。これが最大のポイントです。この機能により、単なる「起動」ではなく「ヘルスチェック成功」まで待機できるようになります。

version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://admin:password@db:5432/appdb

この設定のポイントは以下の通りです。

depends_on の形式が従来の配列形式から、辞書形式に変更されています。db に対して condition: service_healthy を指定することで、app サービスは db のヘルスチェックが成功するまで起動を待機します。これにより、PostgreSQL が完全に接続可能な状態になってから、アプリケーションが起動されることが保証されます。

Redis や MySQL など、他のサービスのヘルスチェック設定例

ここまでは PostgreSQL の例を示しましたが、他のデータベースやキャッシュサービスについても、同様にヘルスチェックを設定できます。以下は実装例です。

Redis のヘルスチェック設定は以下の通りです。

version: '3.8'

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 5s

  app:
    image: myapp:latest
    depends_on:
      redis:
        condition: service_healthy
    ports:
      - "8000:8000"

MySQL のヘルスチェック設定は以下の通りです。

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: appdb
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 15s

  app:
    image: myapp:latest
    depends_on:
      mysql:
        condition: service_healthy
    ports:
      - "8000:8000"

各サービスで推奨されるヘルスチェックコマンドが異なることに注意してください。これは各サービスが提供する標準的な疎通確認ツールを使用するためです。

複数サービスの依存関係が複雑な場合の実装パターン

実務では、db → redis → app というように3つ以上のサービスが連鎖的に依存することは珍しくありません。以下の例は、API サーバー、Redis キャッシュ、PostgreSQL データベースが連鎖的に依存する構成です。

version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 5s

  app:
    image: myapp:latest
    depends_on:
      cache:
        condition: service_healthy
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://admin:password@db:5432/appdb
      REDIS_URL: redis://cache:6379

この設定では、起動順序が db → cache → app と厳密に制御されます。cache も単なる起動ではなく、db の healthy 確認後に起動され、その後 app が cache の healthy を確認してから起動されます。このように依存関係を層状に配置することで、複雑な起動シーケンスも確実に制御できます。

ヘルスチェック機能が機能しない場合のトラブルシューティング

ヘルスチェック設定後、想定通りに動作しないケースが発生することもあります。ここでは、よくある原因と対処方法を解説します。

最初に確認すべきは、コンテナのログです。以下のコマンドでヘルスチェック結果を確認できます。

docker compose ps

このコマンドで STATUS 列に (healthy) と表示されていれば、ヘルスチェックが成功しています。(unhealthy) と表示されている場合は、ヘルスチェックコマンドが失敗しているため、詳細ログを確認する必要があります。

詳細なヘルスチェックログを確認するには、以下のコマンドを使用します。

docker compose logs db

よくある問題として、ヘルスチェックコマンドが実行時に必要な環境変数が設定されていない場合があります。例えば PostgreSQL の pg_isready では、-U オプションで ユーザー名を指定する必要があります。

また、start_period の値が短すぎる場合も、unhealthy が頻発します。特に大規模なデータベースを初期化する場合や、ネットワーク遅延がある環境では、start_period を十分に長く(30秒以上)設定することが重要です。

本番環境での推奨設定と性能考慮

開発環境でのヘルスチェック設定と、本番環境での設定には若干の違いがあります。本番環境では、以下の点に配慮する必要があります。

まず、ヘルスチェック間隔(interval)です。開発環境では 5 秒という短い間隔で十分ですが、本番環境ではサーバーの CPU 負荷を考慮して、10〜15秒に延長することを推奨します。ヘルスチェック自体がデータベースクエリを実行するため、高アクセス時間帯にはチェック頻度を下げることが有効です。

次に、retries の値です。本番環境では、一時的なネットワーク障害でコンテナが unhealthy と判定されないよう、retries を 5〜10 回に設定することが推奨されます。これにより、短時間の通信遅延で不必要にコンテナが再起動されることを防げます。

最後に、タイムアウト(timeout)です。本番環境ではネットワーク遅延が大きくなる可能性があるため、5秒以上に設定することが推奨されます。

以下は、本番環境推奨の設定例です。

version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 15s
      timeout: 5s
      retries: 8
      start_period: 30s

ヘルスチェック設定で起動時間はどのくらい延びるのか

ヘルスチェック機能を導入すると、コンテナの起動時間が増加します。その程度を定量的に理解することは重要です。

PostgreSQL の場合、従来の depends_on のみの設定では、アプリケーションの起動試行が開始されるまでに、通常2〜3秒の遅延が発生します。これに対してヘルスチェック設定を加えると、start_period(10秒)+ retries による待機時間(最大25秒)が追加されるため、合計で30〜40秒程度のシーケンス時間が必要になります。

しかし重要な点は、この「追加時間」によって、接続エラーによるアプリケーションの再起動回数が劇的に減少することです。接続エラーで再起動が発生すると、1回あたり20〜30秒の損失が生じます。数回の再起動で、ヘルスチェック設定による時間増加を相殺してしまうため、本番環境では確実に運用コストが低下します。

Docker Compose のヘルスチェック設定は、すべてのサービスで必須ですか?

ヘルスチェック設定は、他のサービスの起動に必要とされるすべてのサービス(データベース、キャッシュ、メッセージキューなど)に推奨されます。特に起動時間が長い傾向にあるサービスほど重要です。一方、アプリケーションサーバー自体のヘルスチェックは、後続するサービスがない場合は必須ではありませんが、ローリングアップデートやコンテナオーケストレーション(Kubernetes など)との連携を考慮すれば、設定しておくことが最佳実践です。

条件付き depends_on が機能しないケースはありますか?

🤖 このブログはAIで自動運営しています。 同じ仕組みを御社にも導入できます。 無料相談はこちら
タイトルとURLをコピーしました