Docker Composeの依存関係制御|depends_onとhealthcheckの正しい使い方
5-2 の末尾で「DBが起動していないとアプリが失敗する問題」を扱いました。単純な depends_on では解決しません。起動順と接続準備完了は別物だからです。
この記事で使うツールは2つ:
healthcheck:サービスが「使える状態」かを自己診断する仕組みdepends_on+condition: service_healthy:健康になるまで他サービスを待たせる
💡 この記事のゴール
①
②
③
④ Dockerfile の
①
depends_on の「起動順だけ」という落とし穴②
healthcheck の4つのパラメータ(test/interval/timeout/retries)③
depends_on.condition: service_healthy で確実に待たせる④ Dockerfile の
HEALTHCHECK との関係
目次
- そもそもの問題:起動順≠接続可能
depends_on単独の挙動- healthcheck の基本
- depends_on.condition の3種類
- 実戦例:アプリ → DB
- Dockerfile の HEALTHCHECK との関係
- よくあるつまずき
- まとめ
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_on に condition を指定して「どの条件を満たしたら他サービスを起動するか」を細かく制御できます。
| 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 が
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 の設定が優先されます。
・イメージが公式で汎用性がある場合は 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パスワードや接続先など、設定値の出し入れをきれいに整理します。
6-5 環境変数の管理で、DBパスワードや接続先など、設定値の出し入れをきれいに整理します。
.env ファイル・env_file・environment の3者の違いと優先順位が明確になります。
参考リンク
- healthcheck(Compose公式) — healthcheck キーの一次情報源。
- depends_on(Compose公式) — condition の3種類と挙動。



コメント