概要
docker-composeでdockerコンテナを起動時、「たまに」以下のエラーが出る状態でした。
なぜこれが「たまに」だったのか、またどうすればでなるくなるかわかったので紹介します。
MySQLdb._exceptions.OperationalError: (2002, "Can't connect to MySQL server on 'db' (115)")
とか
sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (2002, "Can't connect to MySQL server on 'db' (115)")
(Background on this error at: https://sqlalche.me/e/14/e3q8)
原因
原因は、アプリサーバのサービスがMySQLサービスよりも先に起動してしまい、MySQLサーバーがまだ準備ができていないため。
Docker Composeはサービスを並行して起動するのですが、毎回このエラーが出るわけではないのは、MySQLサービスが先に起動してくれることもあるからなのでしょう。
docker-compose.yaml
の以下の箇所を追加したら、エラーが出なくなりました。
version: "3.8"
services:
sample_server:
build:
context: ../../
dockerfile: ./docker/Dockerfile
volumes:
- ../../web_app:/xxx/web_app
image: sample_server
container_name: sample_server
hostname: sample-server
privileged: true
restart: always
tty: true
ports:
- "80:80"
networks:
fixed_compose_network:
aliases:
- backend
depends_on: # 追加
mysql: # 追加
condition: service_healthy # 追加
mysql:
image: mysql:8.0
container_name: mysql-container
restart: always
volumes:
- ./database/sql:/sql
ports:
- "3306:3306"
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
networks:
fixed_compose_network:
aliases:
- db
healthcheck: # 追加
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] # 追加
interval: 10s # 追加
timeout: 5s # 追加
retries: 5 # 追加
networks:
fixed_compose_network:
最初はdepends_on
オプションでmysql
を指定したのですが、それだけではエラーが解決しませんでした。起動の順番は制御できるようですが、正常起動したかどうかまではみてくれない?ようです。
depends_on expresses startup and shutdown dependencies between services.
引用元:https://docs.docker.com/compose/compose-file/05-services/#depends_on
そこで、healthcheck
を組み合わせて、依存先のサービスがhealthy(=操作可能)になるまで待つように追加してみました。これで実行したところ、エラーが出ることは無くなりました。
healthcheck
とは
healthcheck
キーは、サービスが正常に動作しているかどうかを定期的に確認するもの。
test: ...
がヘルスチェックのテストコマンド。このコマンドが成功(終了コード0)を返すと、サービスはhealthy
と見なされます。
interval: 10s
は、ヘルスチェックが10秒ごとに実行されますよ、という間隔。
timeout: 5s
は、ヘルスチェックがタイムアウトするまでの時間。
retries: 5
は、ヘルスチェックが連続して失敗するとサービスがunhealthy
と見なされるまでの回数を定義します。
以前postgresでも似た事象があったのでご参考までに。