コンテンツにスキップ

docker-compose の depends_on は「DB 準備完了」を保証しない

docker-compose.yml に depends_on: - postgres と書いた。

アプリが起動してすぐクラッシュする。DB に接続できない。でも postgres は実行中だ。なぜ?

原因: 起動順序 ≠ 準備完了

depends_on が保証すること 保証しないこと
postgres コンテナが「起動(started)」する postgres が接続を受け付けられる「準備完了(healthy)」状態

PostgreSQL の初期化(データディレクトリの作成・ユーザー作成など)には数秒〜十数秒かかるため、コンテナが起動していてもアプリが接続しようとすると失敗する。

解決策 1: healthcheck + condition: service_healthy(推奨)

services:
  postgres:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 5s
      timeout: 5s
      retries: 5

  app:
    depends_on:
      postgres:
        condition: service_healthy

解決策 2: アプリ側リトライロジック(推奨)

# 指数バックオフでリトライ
def connect_with_retry(dsn, max_retries=10):
    for i in range(max_retries):
        try:
            return psycopg2.connect(dsn)
        except psycopg2.OperationalError:
            time.sleep(2 ** i)
    raise RuntimeError("DB 接続失敗")

解決策 3: wait-for-it.sh などのユーティリティ

entrypoint: ["./wait-for-it.sh", "postgres:5432", "--", "python", "app.py"]

比較

方法 メリット デメリット
healthcheck + condition Docker 標準機能のみ、明確 healthcheck 設定が必要
アプリ側リトライ 本番環境でも有効な防御策 コードが増える
wait-for-it.sh シンプルなシェルスクリプト 外部依存が増える

要点

  • depends_on はコンテナの「起動順序」だけを制御し、内部の準備完了は保証しない

  • 本番環境に近い堅牢な設計にするなら healthcheck + アプリ側リトライの両方が望ましい

  • この問題は PostgreSQL 以外(MySQL・Redis・Kafka など)でも同様に発生する