6.4 Docker Composeの依存関係制御|depends_onとhealthcheckの正しい使い方

Docker Compose

Docker Composeの依存関係制御|depends_onhealthcheckの正しい使い方

5-2 の末尾で「DBが起動していないとアプリが失敗する問題」を扱いました。単純な depends_on では解決しません。起動順接続準備完了は別物だからです。

この記事で使うツールは2つ:

  1. healthcheck:サービスが「使える状態」かを自己診断する仕組み
  2. depends_on + condition: service_healthy:健康になるまで他サービスを待たせる
💡 この記事のゴール
depends_on の「起動順だけ」という落とし穴
healthcheck の4つのパラメータ(test/interval/timeout/retries)
depends_on.condition: service_healthy で確実に待たせる
④ Dockerfile の HEALTHCHECK との関係

目次

  1. そもそもの問題:起動順≠接続可能
  2. depends_on 単独の挙動
  3. healthcheck の基本
  4. depends_on.condition の3種類
  5. 実戦例:アプリ → DB
  6. Dockerfile の HEALTHCHECK との関係
  7. よくあるつまずき
  8. まとめ

1. そもそもの問題:起動順≠接続可能

【DB起動 → アプリ起動 のタイミング問題】
t=0
db 起動

t=1
db プロセス
起動中
まだ接続不可

t=2
app 起動
db 接続失敗💥

t=5
db 受付開始
(遅い)

↑ docker 的には「db起動 → app起動」の順でも、DBプロセス内部ではまだ初期化中のことがある

2. depends_on 単独の挙動

services:
  app:
    image: myapp
    depends_on:
      - db        # ← 「db の起動を待つ」だけ

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret

この書き方では Compose はdb コンテナの「起動」が完了するまで app を起動しないことを保証するだけ。db 内部のプロセスが接続を受け付けられる状態になっているかは見てくれません。

⚠️ これで十分なケース
アプリ側に接続リトライロジックが組み込まれているなら、シンプルな depends_on だけで実用上困らないこともあります(接続失敗 → 数秒待って再試行)。

3. healthcheck の基本

healthcheck は「サービスが使える状態か」をDocker自身が定期的に確認する機能。コマンドの exit code で判断します(0=healthy、非0=unhealthy)。

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s        # 何秒おきに確認するか
      timeout: 3s         # 1回の確認のタイムアウト
      retries: 10         # 連続何回失敗したら unhealthy と判定
      start_period: 10s   # 起動直後はここで指定した秒数、失敗をカウントしない
キー 意味 デフォルト
test 確認コマンド。CMD(配列)/ CMD-SHELL(シェル実行) なし(必須)
interval 実行間隔 30s
timeout 1回の実行タイムアウト 30s
retries unhealthy 判定までの連続失敗回数 3
start_period 起動直後の猶予期間(失敗をカウントしない) 0s

代表的な test コマンド例

サービス test 例
PostgreSQL ["CMD-SHELL", "pg_isready -U postgres"]
MySQL ["CMD", "mysqladmin", "ping", "-h", "localhost"]
Redis ["CMD", "redis-cli", "ping"]
汎用HTTP ["CMD", "curl", "-f", "http://localhost/health"]
汎用HTTP (wget) ["CMD-SHELL", "wget -q -O- http://localhost/health || exit 1"]

healthy 状態を確認する

$ docker compose ps
NAME           STATUS
myapp-db-1     Up 30s (healthy)      ← healthy が表示される

$ docker inspect --format='{{json .State.Health}}' myapp-db-1 | jq
{
  "Status": "healthy",
  "FailingStreak": 0,
  "Log": [...]
}

4. depends_on.condition の3種類

Compose V2 では depends_oncondition を指定して「どの条件を満たしたら他サービスを起動するか」を細かく制御できます。

condition 意味 使いどころ
service_started 起動した瞬間(デフォルト) 接続リトライがある時
service_healthy healthcheck が healthy になるまで待つ DB・メッセージキュー等の定番
service_completed_successfully サービスが exit 0 で終了するまで待つ マイグレーションなどワンショットタスク
services:
  app:
    image: myapp
    depends_on:
      db:
        condition: service_healthy       # DB が healthy になるまで待つ
      migrate:
        condition: service_completed_successfully   # マイグレ完了まで待つ
      cache:
        condition: service_started       # 起動だけでOK

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

  migrate:
    build: .
    command: python manage.py migrate
    depends_on:
      db:
        condition: service_healthy

  cache:
    image: redis:7-alpine

5. 実戦例:アプリ → DB

5-1. compose.yml

services:
  app:
    image: postgres:16
    command: sh -c "psql -h db -U postgres -c 'SELECT 1;' && echo '✅ connected' && sleep 60"
    environment:
      PGPASSWORD: secret
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 2s
      retries: 20
      start_period: 10s

5-2. 挙動を観察

$ docker compose up
[+] Running 2/2
 ✔ Container myapp-db-1   Created
 ✔ Container myapp-app-1  Created
Attaching to app-1, db-1
db-1   | ...starting PostgreSQL...
db-1   | ...database system is ready to accept connections
 ← healthcheck 通過(数秒遅れて)
app-1  |  ?column?
app-1  | ----------
app-1  |         1
app-1  | (1 row)
app-1  | ✅ connected
✅ 確認
app コンテナは DB が healthy になるまで起動すらしません。「DB 接続に失敗したから終了」というエラーを踏まずに済みます。

6. Dockerfile の HEALTHCHECK との関係

Dockerfile にも HEALTHCHECK 命令があります。イメージ作成時に焼き込まれ、そのイメージから起動したコンテナすべてに適用されます。

# Dockerfile
FROM python:3.12
# ... アプリコードのCOPY・pip install ...
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
CMD ["gunicorn", "--bind", "0.0.0.0:3000", "app:app"]
💡 compose.yml と Dockerfile どちらで書くべき?
イメージが公式で汎用性がある場合は Dockerfile にも書いておくと誰が使っても healthcheck が効く
プロジェクト特有のチェックは compose.yml 側
両方書かれていれば compose.yml の設定が優先されます。

7. よくあるつまずき

症状 原因 対処
healthcheck が常に unhealthy test コマンドが存在しないバイナリ(curl 等)を叩いている イメージに入っているコマンドで書く(alpine なら wget / nc)
depends_on が効かず app が先に起動 condition を省略した(service_started 相当) condition: service_healthy を明示
app が何分も起動しない healthcheck の start_period + retries × interval が長すぎる 起動時間の実測値に合わせて調整
本番でhealthyにならない 起動後すぐに重い初期化(migration 等)が走って遅い start_period を延ばす / migrate を別サービスに分ける

8. まとめ

押さえどころ 内容
起動順 ≠ 接続可能 depends_on 単独では「起動」だけ保証
healthcheck testコマンドの exit code で健康判断
本命の組合せ depends_on.condition: service_healthy
マイグレ連携 service_completed_successfully で完了待ち
定型test pg_isready / mysqladmin ping / redis-cli ping / curl /health
2重記述 Dockerfileにも書けるが compose 優先
✅ 次のステップ
6-5 環境変数の管理で、DBパスワードや接続先など、設定値の出し入れをきれいに整理します。.env ファイル・env_fileenvironment の3者の違いと優先順位が明確になります。

参考リンク


Dockerの基礎を動画で体系的に学びませんか?

実務で使う基礎だけを3時間に凝縮。環境構築から丁寧に解説しています。

Udemy Docker入門講座 クーポン割引で講座を見る →

コメント

タイトルとURLをコピーしました