0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker Composeのdepends_onだけではDBを待てない:healthcheckとservice_healthyの使い方

0
Last updated at Posted at 2026-10-14

Docker ComposeでAPIとPostgreSQLを同時に起動したとき、DBコンテナは起動しているのに接続エラーになることがあります。

原因の一つは、コンテナの起動(running)と、DBが接続を受け付けられる状態(ready)を混同していることです。この記事では、2026年10月13日時点のDocker公式ドキュメントをもとに、depends_on・healthcheck・service_healthyを実務でどう使い分けるか整理します。

なぜdepends_onだけでは不十分なのか

以下の短い書き方は、apiより先にdbを起動する順序を指定します。

services:
  api:
    build: ./api
    depends_on:
      - db
  db:
    image: postgres:18

しかし、PostgreSQLは起動後に内部の初期化を行うため、コンテナが動き始めてもすぐにSQLを実行できるとは限りません。Docker Composeの公式説明でも、短いdepends_onは依存先が「healthy」になるまで待つことを保証しないとされています。

ここで用語を区別します。

  • started / running:コンテナのプロセスが起動した状態。
  • healthy:コンテナに設定したヘルスチェックが成功している状態。
  • readiness(準備完了):アプリから見て、必要な処理を受け付けられる状態。何を確認すべきかはアプリによって異なります。

つまり、depends_on: [db]を付けるだけでDB接続エラーが必ず解消するわけではありません。

動作を確認できるCompose設定

次のcompose.yamlは、DBのヘルスチェックを待ってから、別コンテナでSELECT 1を実行する最小例です。ホストPCにPostgreSQLをインストールする必要はありません。

services:
  db:
    image: postgres:18
    environment:
      POSTGRES_USER: app
      POSTGRES_DB: appdb
      POSTGRES_PASSWORD: ${DB_PASSWORD:?Set_DB_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 15s

  check:
    image: postgres:18
    depends_on:
      db:
        condition: service_healthy
    environment:
      PGPASSWORD: ${DB_PASSWORD:?Set_DB_PASSWORD}
    command:
      - sh
      - -ec
      - "psql -h db -U app -d appdb -tAc 'SELECT 1'"

.envファイルには、ローカル検証用のパスワードを設定します。

DB_PASSWORD=replace-with-a-local-test-password

実運用のパスワードを記事・リポジトリへコミットしないでください。.envもGitの追跡対象に含めず、本番では利用環境に応じたシークレット管理を検討してください。また、この例は学習用のためDBの永続化ボリュームを設定していません。

ヘルスチェックの意味

  • pg_isready:PostgreSQLサーバーが接続要求を受け付ける状態かを調べる付属コマンドです。スキーマ作成済み・必要な権限が正しいことまでは保証しません。
  • CMD-SHELL:コンテナ内のシェルでチェック用コマンドを実行します。
  • interval: 5s:チェックの実行間隔を指定します。
  • timeout: 3s:チェック1回のタイムアウトです。
  • retries: 10:一定回数の連続失敗でunhealthyと判定します。
  • start_period: 15s:起動直後の準備期間です。この期間中の失敗の扱いは通常の連続失敗とは異なります。

$${POSTGRES_USER}の二重ドル記号も重要です。Composeは$から始まる値をホスト側で変数展開しようとします。$$と書くと展開を避け、コンテナ内のシェルがPOSTGRES_USERを参照できるようにします。

checkサービスのservice_healthyは「dbのヘルスチェックが成功してから起動する」条件です。checkはDBへ接続してSELECT 1が成功すると正常終了します。長時間稼働するAPIを模したものではなく、起動待ちを試すための一回限りの確認役です。

検証コマンドと、それぞれの意味

compose.yamlと.envを同じディレクトリに置き、Docker EngineとComposeが使える環境で実行します。

docker compose config -q
docker compose up -d
docker compose ps -a
docker compose logs --follow db
  • docker compose config -q:Composeの設定を解析して検証します。-qは正常時の設定内容の表示を抑えるオプションです。実際にDBへ接続できるかは検査しません。
  • docker compose up -d:構成に従ってコンテナを作成・起動します。-dはバックグラウンド実行(detached)です。
  • docker compose ps -a:終了したコンテナも含めて状態を表示します。dbがhealthyとなり、checkが終了コード0になれば例の確認は成功です。
  • docker compose logs --follow db:dbのログを継続表示します。--followは追加ログを追いかける指定で、終了はCtrl+Cです。

停止・片付けには次を使います。

docker compose down

downはComposeで作成したコンテナなどを停止・削除します。この例はDBデータを永続化していないので、downでコンテナを消すとテストデータも失われます。永続データのある実環境ではボリューム設計を別途行ってください。

本番向けAPIに置き換えるなら

既存のAPIサービスがあれば、起動条件を次のように定義できます。

services:
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
      DB_PORT: "5432"

実際には、これを先ほどのdb定義と組み合わせ、DBユーザー名や認証情報なども安全に渡してください。Composeの同じネットワークに属するコンテナ同士は、通常サービス名dbで通信できます。APIコンテナ内のlocalhostはAPIコンテナ自身なので、PostgreSQLへつなぐホスト名としては通常使いません。

ただし、service_healthyはアプリの再接続処理の代わりではありません。DBが起動後に再起動したり、一時的にネットワークが切れたりする可能性があるため、API側にも適切な接続タイムアウト、リトライ、接続プールの回復処理が必要です。

「マイグレーション完了」を待ちたい場合

DBが接続可能でも、テーブルが作成済みとは限りません。マイグレーション(テーブル構造などを変更する処理)をワンショットのサービスに分ける場合は、service_completed_successfullyという別の条件が使えます。

services:
  migrate:
    build: ./api
    command: ["npm", "run", "db:migrate"]
    depends_on:
      db:
        condition: service_healthy

  api:
    build: ./api
    depends_on:
      migrate:
        condition: service_completed_successfully

これは既存のdb定義に追加する構成例であり、./apiにマイグレーション用スクリプトが存在することが前提です。マイグレーションが正常終了した後にAPIを開始できます。ただし、マイグレーション自体は同時実行・失敗後の再実行・後方互換性も考えて設計してください。

よくあるハマりどころ

1. healthyならアプリから必ず使えると考える

DBプロセスの状態と、テーブル・ユーザー権限・外部依存の準備状況は別物です。業務上の準備完了条件があるなら、それを確認するチェックを用意します。

2. ヘルスチェック用コマンドがコンテナ内にない

curlなどでチェックするとき、利用するイメージにその実行ファイルがなければ失敗します。今回の例ではPostgreSQLイメージに付属するpg_isreadyを使います。

3. restart: trueで障害後に自動でAPIも再起動すると期待する

depends_onの長い書き方で指定するrestart: trueは、Docker Composeで明示的に依存サービスを再起動・更新する操作と連動するための設定です。DBコンテナが自動再起動した際にAPIも必ず再起動される、という意味ではありません。サービス直下の再起動ポリシーとも区別してください。

4. パスワードをCompose設定に直接書いてしまう

ローカル検証の.envと、運用環境の秘密情報管理は分けましょう。docker compose configは変数の展開結果を表示するため、共有ログへ出力する際も注意が必要です。

まとめ:依存関係とアプリの耐障害性は別に考える

  • 起動順だけでよい:短いdepends_on(またはservice_started)。
  • DBのヘルスチェック成功まで待つ:healthcheck + service_healthy。
  • 一回限りの初期化処理が正常終了してから起動する:service_completed_successfully。
  • 起動後にDBが落ちても復旧したい:API自身の接続回復・リトライを設計する。

Composeの起動順制御は便利ですが、データベースや他サービスとの通信が常に成功する保証ではありません。**「起動時に待つ仕組み」と「稼働中の障害から回復する仕組み」**を分けて設計すると、ローカル開発やCIの不安定さを減らしやすくなります。

公式参考資料(2026年10月13日確認)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?