docker-composeを使っていて、GoのコンテナからMySQLのコンテナに接続できずにハマった。
dial tcp 172.20.0.2:3306: connect: connection refused
原因
MySQLのコンテナの起動が終わる前に、GoがDBコンテナ接続に行ってしまっていたため。
http://docs.docker.jp/compose/startup-order.html
http://docs.docker.jp/compose/faq.html#id6
https://docs.docker.com/compose/startup-order/
詳細
- dbのコンテナにdepends_onやlinks属性を書いていたとしても、goのコンテナはdbコンテナが完全に準備ができるのを待たずに起動してしまう(Dockerはそこまでの制御をできない)。
データベースが起動するのを待ってからアプリケーションを起動するには?
- 残念ながら、正統な理由がなければ Compose はそのように処理できません。
http://docs.docker.jp/compose/faq.html#id6
You can control the order of service startup and shutdown with the depends_on option. Compose always starts and stops containers in dependency order, where dependencies are determined by depends_on, links, volumes_from, and network_mode: "service:...".
However, for startup Compose does not wait until a container is “ready” (whatever that means for your particular application) - only until it’s running.
- エラーログをみると、最初にdbコンテナが起動することだけは守られているが、dbコンテナが完全に通信を受け付ける状態までは待たず、途中でGoのコンテナの起動が始まっている。
- 起動の順番を守ることはできるが、dbコンテナ内部で準備ができたかどうかまではDockerは把握できないため、こういう状態が起きる。
Attaching to my-app-db, my-app-API
my-app-db | Initializing database
my-app-db | 2019-08-08T03:03:46.826882Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
my-app-db | 2019-08-08T03:03:46.829980Z 0 [ERROR] --initialize specified but the data directory has files in it. Aborting.
my-app-db | 2019-08-08T03:03:46.830039Z 0 [ERROR] Aborting
my-app-db |
my-app-API | Server Start...
my-app-API | Initialize config ...
my-app-API | loadconf: /go/src/github.com/tribalmedia/my-app/server/my-app.toml
my-app-API | OpenMySQL: {mysql tcp(my-app-db:3306) root my-app my-app}
my-app-API | connectionString: root:my-app@tcp(my-app-db:3306)/my-app?parseTime=true
my-app-API | 2019-08-08T03:03:47.280Z INFO server/main.go:23 Logger Init.
my-app-API | panic: DB接続に失敗: dial tcp: lookup my-app-db on 127.0.0.11:53: no such host
my-app-API |
my-app-API | goroutine 1 [running]:
my-app-API | github.com/tribalmedia/my-app/server/models.ConnectDB(0xc000064860, 0x5, 0xc000066380, 0x1a, 0xc000064880, 0x4, 0xc0000648a0, 0xd, 0xc0000648e0, 0xd)
my-app-API | /go/src/github.com/tribalmedia/my-app/server/models/db.go:29 +0x424
my-app-API | main.main()
my-app-API | /go/src/github.com/tribalmedia/my-app/server/main.go:37 +0x27b
my-app-db exited with code 1
my-app-API exited with code 2
- 今回の場合、DBコンテナの起動プロセスで初期データの投入をしており、DB起動に時間がかかっていたことも原因となっていた。
対応策
- Supervisor経由でコンテナを起動する。
- Supervisorを使うと、DBコンテナに接続できないせいでGoのコンテナが落ちても、プロセス監視によってまた再起動できるため。
データベースに対する接続が失敗したら、アプリケーションは再接続を試みるように扱わなくてはいけません。アプリケーションは再接続を試みるため、データベースへの接続を定期的に行う必要があるでしょう。http://docs.docker.jp/compose/startup-order.html
その他)コンテナ間通信ができない時にチェックすべきポイント
- 今回該当はしなかったが、コンテナ間通信ができない時に確認すべきポイントがあったのでまとめておく。
ポートの指定がコンテナ内部のポートになっているか?
> It is important to note the distinction between HOST_PORT and CONTAINER_PORT. In the above example, for db, the HOST_PORT is 8001 and the container port is 5432 (postgres default). Networked service-to-service communication use the CONTAINER_PORT. When HOST_PORT is defined, the service is accessible outside the swarm as well. https://docs.docker.com/compose/networking/
> **注意点として、WordPressコンテナからアクセスするMySQLのポート番号は、コンテナの内部で使用しているポートを指定してください。**
> https://knowledge.sakura.ad.jp/16082/
MySQL側でユーザーに対するIP制限がかかっていないか?
ワイルドカード%が表示されていれば、全てのIPアドレスからアクセス可能
mysql> select host, user from mysql.user;
その他 ) Linksオプションについて
- Linksオプションは通信時に使えるエイリアスを定義するためのものであって、コンテナ間通信をするために必須ではない。デフォルトでサービス名で通信可能。また、レガシーで推奨されていないオプション。
リンク機能(links)とは、他のサービスから到達可能なエイリアス(別名)を定義するものです。サービス間で通信するために必要ではありません。すなわち、デフォルトでは、あらゆるサービスはサービス名を通して到達できます。
http://docs.docker.jp/compose/networking.html#links