1. 前提条件
MySQL、Prisma、Docker-Compose を使用した環境で、データベースの起動遅延による接続エラーを防ぐ方法を解説します。
使用技術:
- MySQL
- Prisma
- Docker-Compose
2. Healthcheck(ヘルスチェック)とは?
Healthcheck は、コンテナやサービスが 正常に動作しているか を自動で確認する仕組みです。
コンテナ環境では、アプリケーションが起動していても 実際に動作できる状態かどうか を保証する必要があります。
Healthcheck を使うことで、サービスの正常性を確認し、異常時に適切な対応(再起動・待機・通知など)を行うことができます。
Healthcheck の役割
① コンテナの「起動完了」や「動作状態」を確認
- 例: MySQL が起動していても、まだ接続を受け付けられない状態を防ぐ。
② 異常を検知して、コンテナを再起動
- 例: アプリがハングした場合、Docker が自動で再起動できる。
③ 他のサービスが「依存するコンテナの起動を待つ」制御ができる
- 例:
depends_on: service_healthy
で、MySQL の起動を待ってからアプリを起動。
3. 問題の発生
Docker-Compose を使って MySQL とアプリケーションを始めて起動した際に、
Error: connect ECONNREFUSED 127.0.0.1:3306
または
PrismaClientInitializationError: Can't reach database server at `db:3306`
といったエラーが発生し、アプリケーションが MySQL に接続できない問題が発生。
4. エラーの原因
① MySQL の起動には時間がかかる
-
docker-compose up
でdb
コンテナがすぐに起動するが、MySQL サーバーが実際にリクエストを受け付けられるまでには時間がかかる。 - アプリケーションが MySQL の起動完了を待たずに接続を試みると、エラーが発生。
② depends_on
では MySQL の起動完了を保証できない
-
depends_on
を指定すると、アプリ (app
) は MySQL (db
) のコンテナが "起動" したことを確認してから起動する。 - しかし、"起動" とは コンテナのプロセスが立ち上がっただけ であり、MySQL サーバー自体がリクエストを受け付けられる状態になったことを保証しない。
- MySQL は起動後に 初期設定やテーブル作成などを内部で実行するため、起動完了までに時間がかかる。
- そのため、アプリが MySQL の起動直後に接続を試みると、まだ MySQL が受け入れ可能な状態でなく、
ECONNREFUSED
エラーが発生する。
③ Prisma などのデータベースクライアントが MySQL に接続できない
- MySQL が起動していない間に
prisma.$connect()
が実行されると、接続エラーが発生する。 - これは Prisma に限らず、一般的なデータベースクライアントが MySQL への接続を試みる際にも発生する問題。
④ healthcheck
が実質必須になる条件
healthcheck
は基本的には推奨となるが、以下の条件に当てはまる場合、healthcheck
を入れないと MySQL の起動遅延による接続エラーが発生しやすくなる。
-
アプリが起動時に即データベースへ接続する設計
- Prisma のように
prisma.$connect()
がアプリの起動時に実行される 場合、DB が準備できる前に接続を試みるためエラーになる。
- Prisma のように
-
データベースの起動が遅い(MySQL, PostgreSQL など)
- MySQL や PostgreSQL は起動直後にすぐ接続可能になるわけではなく、初期設定やデータロードが完了するまで時間がかかる。
-
depends_on
だけでは コンテナが起動したこと しか保証されず、DB がリクエストを受け付けられるとは限らない。
-
アプリがリトライ機能を持たない(接続エラー時に自動で再試行しない)
-
healthcheck
を入れない場合、アプリ側で 「接続エラー時にリトライする」 実装があれば問題にならないが、ない場合は一度失敗すると再試行せずクラッシュする。
-
-
コンテナのオーケストレーションを Docker-Compose で管理している
- Kubernetes では
livenessProbe
やreadinessProbe
を使うが、Docker-Compose では 明示的にhealthcheck
を設定しないと起動順を制御できない。
- Kubernetes では
5. 解決方法
** docker-compose.yml
にhealthcheck
を追加する**
version: "3.8"
services:
app:
build: .
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
environment:
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: database
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
・動作の流れ
- コンテナが起動(でも MySQL はまだ準備中)。
- mysqladmin ping を 10 秒ごとに実行し、応答を確認。
- 5 回試して 成功すれば healthy、失敗すれば unhealthy になる。
- app コンテナは MySQL が healthy になるまで起動しない(depends_on を設定する場合)。
6. まとめ
・healthcheck
を追加することで、MySQL の完全起動を待ってから app
を起動できるようになる。
・depends_on: service_healthy
を使うことで、MySQL が使用可能になるまで app
を待機させることができる。
・Prisma などのデータベースクライアントが MySQL に確実に接続できるようになり、connect ECONNREFUSED
や PrismaClientInitializationError
のエラーを回避できる。