🐷
「dial tcp 192.168.32.2:3306: connect: connection refused」エラーについて
概要
dial tcp 192.168.32.2:3306: connect: connection refused
docker-compose + go で DB 接続しようとしてたところ上記エラーが出たので、その対応方法について説明する
問題
docker-compose.yaml
services:
app:
# 色々
depends_on:
- db
db:
# 色々
db
db, err := sql.Open("mysql", path)
if err != nil {
return nil, fmt.Errorf("database Open error %w", err)
}
if err := db.Ping(); err != nil {
// 「dial tcp 192.168.32.2:3306: connect: connection refused」エラー
return nil, fmt.Errorf("database connect error %w", err)
s}
上記のような形で、depends_on
を設定し db コンテナを立ち上がった後に app コンテナを起動する設定&アプリ起動時にdb.Ping()
で db への接続を確認していたところ、dial tcp 192.168.32.2:3306: connect: connection refused
エラーで失敗した
原因
原因としては、db が接続不可状態にも関わらず、db.Ping()
していたため。
docker-compose 側でdepends_on: db
をしても、必ずしも db が接続可能状態ではない。
depends_on
は単純にコンテナの起動の順番を定義するだけで、サービス起動有無は確認しない。
解決策
解決策としては、下記の2パターン。
- 接続可能になるまでリトライする
- docker-compose で healthcheck の記述をする
1. 接続可能になるまでリトライする
// ここら辺で、sql.Open(mysql, path)を呼んでdb取得
count := 0
for {
if count > 10 {
return nil, fmt.Errorf("database connect error %w")
}
err = db.Ping()
if err = nil {
// 成功
break
} else {
// 1秒待ってリトライ
time.Sleep(time.Second)
count++
}
}
// ここからdb処理
上記のように、db.Ping()
が成功するまで retry する。
db.Ping()
が成功すると、DB へのアクセスが可能になる。
2. docker-compose で healthcheck の記述をする
services:
app:
# 色々
depends_on:
db:
condition: service_healthy
db:
# 色々
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 1s
timeout: 5s
上記のように、depends_on
の設定に加えて、condition: service_healthy
の項目を設定する。
設定することで、db コンテナで定義したhealthcheck
が成功してから、app コンテナを起動してくれる。
結論
アプリケーション側でリトライする方法は煩雑な感じがするので、個人的には 2 の手法の方が好み。
最近サーバーの勉強を始めているのですが、こういう細かいエラーに苦しめられて大変...。
サーバー勉強し始めのどなたかに役に立てれば幸いです 🍀
Discussion