🐷

「dial tcp 192.168.32.2:3306: connect: connection refused」エラーについて

2023/12/02に公開

概要

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パターン。

  1. 接続可能になるまでリトライする
  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 の手法の方が好み。
最近サーバーの勉強を始めているのですが、こういう細かいエラーに苦しめられて大変...。
サーバー勉強し始めのどなたかに役に立てれば幸いです 🍀

GitHubで編集を提案

Discussion