マルチコンテナアプリケーションを Docker Compose で構築していると、「データベースの起動を待ってからアプリケーションサーバーを起動したい」という課題にぶつかることはありませんか。
単純に depends_on で依存関係を定義しても、コンテナが起動したことと、その中で実行されているサービスが準備完了したことは全く別です。データベースのプロセスが起動しているだけで、接続を受け付ける段階まで達していないのに、アプリケーションが接続を試みてしまい、エラーが発生することも多いです。
この記事では、Docker Compose のヘルスチェック機能と depends_on を組み合わせて、確実に起動順序を制御する実践的な方法を、ステップバイステップで解説します。
Docker Compose のヘルスチェックとは
ヘルスチェックは、コンテナ内で定期的に指定したコマンドを実行し、そのサービスが正常に動作しているかを判定する機能です。
従来の depends_on だけでは、コンテナのプロセスが起動したことしか確認できません。しかしヘルスチェックを組み合わせることで、サービスが実際に利用可能な状態になるまで待機できます。
ヘルスチェックの判定結果は以下の 4 つの状態を持ちます。
- starting:コンテナ起動直後、ヘルスチェック開始前の状態
- healthy:ヘルスチェックコマンドが成功した状態
- unhealthy:ヘルスチェックコマンドが失敗した状態
- none:ヘルスチェックが設定されていない状態
depends_on で起動順序を制御する従来の方法と限界
まず、従来の depends_on だけを使った設定を見てみます。
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
app:
image: myapp:latest
depends_on:
- db
ports:
- "8000:8000"
この設定では、app サービスは db サービスの起動後に起動が試みられます。しかし PostgreSQL のプロセスが起動しただけで、実際にクエリを受け付ける状態になるまでの数秒間は接続に失敗します。
その結果、アプリケーションが起動時に初期化クエリを実行しようとすると接続エラーが発生し、アプリケーション自体も起動に失敗してしまいます。
ヘルスチェック設定で サービスの準備完了を待機する
この問題を解決するには、ヘルスチェックを活用します。以下の例は PostgreSQL にヘルスチェックを設定したものです。
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
ヘルスチェックのパラメータについて説明します。
- test:実行するコマンド。
CMDまたはCMD-SHELLを指定 - interval:ヘルスチェック間隔(秒単位)
- timeout:コマンド実行のタイムアウト時間(秒単位)
- retries:何回失敗したら unhealthy と判定するか
- start_period:コンテナ起動から最初のヘルスチェック実行までの待機時間
上記の例では、PostgreSQL が起動してから 10 秒待機した後、5 秒ごとに pg_isready コマンドでポート接続可能性を確認します。
depends_on で condition: service_healthy を使う
Docker Compose 2.1 以降では、depends_on に condition パラメータを指定できます。これにより、単なる「起動」ではなく「ヘルスチェック成功」まで待機できます。
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
ports:
- "8000:8000"
environment:
DATABASE_URL: "postgresql://admin:password@db:5432/appdb"
condition: service_healthy を指定することで、db サービスのヘルスチェックが成功するまで app の起動が待機されます。
複数サービス間の依存関係を設定する実例
より実践的な例として、データベース、キャッシュ、アプリケーションサーバーという 3 層構成を考えます。
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
- db_data:/var/lib/postgresql/data
cache:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
depends_on:
db:
condition: service_healthy
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
- "8000:8000"
environment:
DATABASE_URL: "postgresql://admin:password@db:5432/appdb"
REDIS_URL: "redis://cache:6379"
volumes:
db_data:
このパターンでは以下の順序で起動が進みます。
dbサービスが起動し、ヘルスチェック成功を待機cacheサービスが起動し、dbが healthy になるまで待機cacheのヘルスチェック成功後、appが起動appはdbとcache両方が healthy になるまで待機
各サービスのヘルスチェック設定例
代表的なサービスのヘルスチェック設定パターンを紹介します。
PostgreSQL のヘルスチェック
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d appdb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
MySQL のヘルスチェック
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
Redis のヘルスチェック
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 3
start_period: 5s
HTTP ベースのアプリケーションのヘルスチェック
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
/health エンドポイントは、アプリケーションが起動後すぐに利用可能な軽量なチェックポイントです。
実践的なトラブルシューティング
ヘルスチェック設定時によくある問題と対策を紹介します。
ヘルスチェックコマンドが見つからない
pg_isready や redis-cli などのツールがコンテナイメージに含まれていないと、ヘルスチェックが失敗します。Alpine ベースのイメージは特に注意が必要です。
解決方法は、Dockerfile で必要なツールをインストールするか、別のコマンドを使用することです。
FROM postgres:15-alpine
RUN apk add --no-cache postgresql-client
ヘルスチェック開始まで の待機時間が短すぎる
start_period を短く設定しすぎると、サービスがまだ初期化中にヘルスチェックが実行され、失敗判定されることがあります。
データベースマイグレーションなどが行われる場合は、十分な余裕を持たせましょう。
複雑な初期化処理がある場合
データベースマイグレーションやシード処理が必要な場合は、condition: service_healthy だけでは不十分な場合があります。
この場合は、待機スクリプトを使用するのが効果的です。pytest モック 使い方|外部API テスト を効率化する実践パターン5選