青色に照らされたサーバーブレードが並ぶデータセンター
現場の実践

Docker Compose の healthcheck と depends_on で起動順序を制御する

目次を見る

コンテナが「起動している」ことと「使える状態にある」ことは別物です。この区別を Compose ファイルだけで表現できる機能が、Docker Compose の healthcheck と depends_on の条件指定です。シェルスクリプトに頼らずに起動順序を制御できるこの仕組みは、業務システムの開発環境構築において特に効果を発揮します。

なぜシェルスクリプトによる起動管理が問題になるか

複数のサービスが絡む構成では、「DB が起動してからアプリを立ち上げる」という要件が必ず発生します。これを解決しようとして wait-for-it.sh や dockerize のようなラッパースクリプトが広く使われてきました。しかしシェルスクリプトは、慣れていないメンバーにとって読みづらく、条件分岐が複雑になるほど保守コストが上がります。

Docker Compose v2.20 以降では、こうした要件を Compose ファイル内で完結させられます。外部スクリプトへの依存をなくせるため、チーム全員が compose.yaml を読むだけで起動ロジックを把握できるようになります。

healthcheck でサービスの「準備完了」を定義する

healthcheck は、コンテナが起動しているかどうかではなく、サービスとして機能しているかどうかを確認するプローブ(定期的に状態を問い合わせる仕組み)です。たとえば PostgreSQL に対しては pg_isready コマンドを使い、TCP ポートの開放だけでなく SQL の受付が可能な状態かどうかを確認できます。

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

ここで注意が必要な点がいくつかあります。まず $${POSTGRES_USER} のように $ を 2 つ重ねているのは、Compose がパース時に変数を展開するのを防ぎ、コンテナ内のシェルで展開させるためです。次に start_period は猶予期間で、この間にヘルスチェックが失敗しても retries のカウントに含まれません。PostgreSQL のような起動に時間がかかるサービスには 30 秒以上を設定するのが安全です。

また postgres:alpine のようにタグを浮かせる(floating tag)と、ある日突然イメージが更新されて動作が変わるリスクがあります。postgres:18-alpine のようにメジャーバージョンを固定しておくことで、再現性のあるビルドを維持できます。

depends_on の条件指定で依存関係を明示する

healthcheck を定義しただけでは、依存サービスが自動的に待機するわけではありません。depends_on に condition を組み合わせることで初めて待機の挙動が有効になります。

Compose が提供する条件は 3 種類あります。

  • service_started: コンテナが起動状態になるまで待つ(デフォルト)
  • service_healthy: healthcheck が成功するまで待つ
  • service_completed_successfully: コンテナが終了コード 0 で完了するまで待つ

データベースのマイグレーション(スキーマ変更を適用する処理)を実行してからアプリを起動したい場合には、migrate サービスに service_completed_successfully を使う構成が有効です。migrate コンテナが正常終了した後にのみアプリコンテナが起動するため、未適用のスキーマでアプリが動いてしまう事故を防げます。

アプリ側のヘルスチェックでは、curl を使いたくなるケースがあります。ただし slim 系のベースイメージには curl が含まれていないことが多く、アプリの障害ではなくバイナリの不在が原因でチェックが失敗するという落とし穴があります。Python ベースのイメージであれば以下のような一行スクリプトが確実です。

test: ["CMD-SHELL", "python -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/healthcheck').status==200 else 1)\""]

urllib は Python 標準ライブラリに含まれるため、イメージの内容に左右されません。

業務システムの開発環境に取り込む際のポイント

この仕組みを既存の compose.yaml に後から追加する際には、いくつかの点を確認しておくと移行がスムーズです。

まず Docker Compose のバージョン確認です。depends_on の条件指定は Compose v2.20 以降が必要です。docker compose version コマンドでバージョンを確認し、古い環境では Docker Desktop または Docker Engine のアップデートが先決になります。

次に、既存の compose.yaml に version: '3.8' のような最上位の version キーが残っている場合、それは削除してかまいません。現行の Compose Specification では不要であり、むしろ混乱の元になることがあります。

start_interval(Docker Engine 25.0 以降で使用可能)という設定もあります。通常の interval とは別に、start_period の間だけ短い間隔でプローブを打てる設定です。起動時間にばらつきが大きいサービスに対して、無駄な待機を減らしつつ素早く準備完了を検出できます。

業務システムの開発環境は、DB・キャッシュ・メッセージキュー・認証サーバーと依存が多層になりがちです。それぞれのサービスに healthcheck を定義し、depends_on で条件を連鎖させることで、docker compose up 一発で順序通りに全サービスが立ち上がる環境を構築できます。シェルスクリプトで複雑になりがちだった起動管理を Compose ファイルに集約することで、新規参画者がリポジトリをクローンした直後から迷わず環境を立ち上げられる状態を目指せます。

Docker Compose の宣言的な設定で起動順序まで表現できるようになった今、外部スクリプトが担っていた役割を徐々に compose.yaml へ移していく余地は大きいと言えます。

参考

More Power in Your Docker Compose

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。