TL;DR
MySQLの公式Dockerイメージのヘルスチェックには-h 127.0.0.1
オプションを追加してTCP/IP経由で行う。これにより、初期化用の一時サーバではなく本番サーバの起動を確実に検出できる。
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
問題の背景
MySQLの公式イメージでは、起動時に以下のプロセスを実行する:
- 一時サーバを立ち上げて初期化プロセスを実行
- 一時サーバを停止
- 本番サーバを起動
docker-entrypoint.shの該当部分:
# Do a temporary startup of the MySQL server, for init purposes
docker_temp_server_start() {
# For 5.7+ the server is ready for use as soon as startup command unblocks
if ! "$@" --daemonize --skip-networking --default-time-zone=SYSTEM --socket="${SOCKET}"; then
mysql_error "Unable to start server."
fi
}
発生する問題
ドメインソケット経由でのヘルスチェックでは、以下の問題が発生する可能性がある:
- ヘルスチェックのタイミングが一時サーバの稼働中と重なると、一時サーバに接続してしまう
- 一時サーバへの接続によりヘルスチェックが成功したように見える
- その後、一時サーバが停止すると接続が切断される
- この現象は起動タイミングに依存するため、断続的に発生する
解決方法
一時サーバには--skip-networking
オプションが設定されているため、TCP/IP接続ができない。したがって、TCP/IP経由でヘルスチェックを行えば、本番サーバが確実に起動していることを確認できる。
docker-composeの設定例
version: "3.8"
services:
mysql:
image: mysql:8.0
healthcheck:
- test: ["CMD", "mysqladmin", "ping"]
+ test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
interval: 30s
timeout: 5s
retries: 3
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data: {}
-h 127.0.0.1
を追加することで、TCP/IP経由での接続確認となり、本番サーバの起動を確実に検出できる。