Docker ComposeでMySQLがスタンバイになる前に他コンテナが接続しエラーとなってしまう場合の対処
はじめに
Web アプリケーションでは全てと言っていいほど Web サーバーと DB サーバーを利用することとなります。
単一の物理サーバーに Web サーバー、DB サーバーの機能を同居していたのも今は昔。
最近では Docker を使った環境構築が主流となっています。
物理的には従来同様単一の物理サーバー上で稼働していたとしても、仮想的には複数のコンテナが稼働しているというイメージです。
そのような環境では、Docker Compose を使って両サービスを稼働させることがあります。
しかし、DB サーバー、例えば MySQL がスタンバイとなる前に他コンテナが接続しようとしてエラーとなってしまう場合があります。
このような場合の対処法を説明します。
検証環境
$ uname -moi
arm64 unknown Darwin
$ bash -version | head -n 1
GNU bash, バージョン 5.2.15(1)-release (aarch64-apple-darwin22.1.0)
$ docker-compose version
Docker Compose version v2.13.0
Docker Compose を使って DB コンテナと DB クライアントを立ち上げた場合に遭遇する問題
再現方法
Docker Compose を使って DB コンテナと DB クライアントを立ち上げた場合、DB クライアントから DB コンテナに接続できない場合があります。
例えば、以下のような MySQL コンテナとそれを利用するクライアントコンテナを立ち上げる docker-compose.yaml
を用意したとします。
YAML ファイルの内容をかんたんに説明しておきます。
- 2 つのコンテナを立ち上げる
server
コンテナは MySQL のサーバーを立ち上げるclient
コンテナはserver
コンテナに接続してデータベースを表示 (show databases
) する
version: "3"
services:
server:
container_name: server
image: mysql/mysql-server:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password
client:
container_name: client
image: mysql/mysql-server:5.7
command: mysql -h server -u test_user -ptest_password -e "show databases;"
この docker-compose.yaml
を実行してコンテナを立ち上げます。
$ docker-compose up
[+] Running 2/0
⠿ Container server Created 0.0s
⠿ Container client Recreated 0.0s
⠋ server The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
⠋ client The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
Attaching to client, server
server | [Entrypoint] MySQL Docker Image 5.7.40-1.2.10-server
client | [Entrypoint] MySQL Docker Image 5.7.40-1.2.10-server
client | mysql: [Warning] Using a password on the command line interface can be insecure.
client | ERROR 2003 (HY000): Can't connect to MySQL server on 'server' (111)
client exited with code 1
server | [Entrypoint] Initializing database
server | 2023-03-22T10:00:16.786716Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
server コンテナはこの後無事に起動しましたが、 client コンテナの立ち上げに失敗していることがわかります。
原因
client コンテナはなぜエラーとなるのでしょうか?
これは server コンテナが起動を待たずに client コンテナが DB アクセスをしているためです。
client と server のコンテナの起動は以下の順序となる必要があります。
- server コンテナを起動する
- server コンテナが起動を完了したら、client コンテナを起動する
このように、server コンテナの起動の完了を待ってから client コンテナを起動する必要があります。
Docker Compose の "depends_on" を使った場合に遭遇する問題
再現方法
起動順序が正しくなるように 先程の YAML ファイルを修正してみましょう。
client コンテナに depends_on
という属性を追加します。
version: "3"
services:
server:
container_name: server
image: mysql/mysql-server:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password
client:
container_name: client
image: mysql/mysql-server:5.7
command: mysql -h server -u test_user -ptest_password -e "show databases;"
depends_on:
- server
このように修正することで、server コンテナが起動の開始を待ってから client コンテナが起動するようになります。
docker-compose down -v
で先程起動していたコンテナたちを一度終了させてから、再度 docker-compose up
を実行してみましょう。
$ docker-compose up
[+] Running 2/0
⠿ Container server Created 0.0s
⠿ Container client Recreated 0.0s
⠋ server The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
⠋ client The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
Attaching to client, server
server | [Entrypoint] MySQL Docker Image 5.7.40-1.2.10-server
client | [Entrypoint] MySQL Docker Image 5.7.40-1.2.10-server
client | mysql: [Warning] Using a password on the command line interface can be insecure.
client | ERROR 2003 (HY000): Can't connect to MySQL server on 'server' (111)
client exited with code 1
server | [Entrypoint] Initializing database
server | 2023-03-22T10:00:16.786716Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
実行した結果、またもやエラーが発生しました。
原因
depends_on
の挙動に注意が必要です。
depends_on
を使えば起動順序を制御できます。しかし、 depends_on
属性を最もシンプルな記述で設定しただけでは問題が解消しません。
先程の設定の意味は、 server コンテナの起動が 「開始」 されたら、 client コンテナを起動する、という意味になります。
ここまで「コンテナの起動の開始」「コンテナの起動の完了」と表現を分けたのには理由があります。
コンテナの起動の開始とは、コンテナが起動した時点です。この時点では、コンテナのサービスがまだ起動していない可能性があります。MySQL サーバーは起動開始直後には、データベースの初期化が行われるため、 client コンテナはまだ server コンテナに接続できません。
コンテナの起動の完了とは、コンテナのサービスが完全に起動した時点です。この時点でようやく、MySQL サーバーが完全に起動しているため、 client コンテナは server コンテナに接続できるようになります。
解決方法
先に触れたように、 「コンテナの起動の開始」 を待つのではなく、 「コンテナの起動の完了」 を待つように設定します。
depends_on
属性のサブ属性である condition
属性を設定することでこの問題を解消できます。condition
属性のデフォルト値は service_startd
です。これを service_healthy
という値に変更します。
condition: service_healthy
を設定する場合、依存先のコンテナの設定に追加しなければいけない属性があります。
healthcheck:
test: mysql -h server -u test_user -ptest_password -e "show databases;"
interval: 5s
timeout: 5s
retries: 3
start_period: 5s
この設定を追加することで、 server コンテナが 「完全に起動した」 状態を待つことができます。
各属性についてもかんたんに触れておきます。
test
: ヘルスチェックを行うコマンドinterval
: ヘルスチェックを行う間隔timeout
: ヘルスチェックを行う時間retries
: ヘルスチェックを行う回数start_period
: 最初のヘルスチェックを開始するまでの時間
修正後の "docker-compose.yaml" 設定
version: "3"
services:
server:
container_name: server
image: mysql/mysql-server:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password
healthcheck:
# test: mysql -u root -proot -e "show databases;" でも良いが、いずれにしてもこれらで外部からの通信が完全に準備完了かというと厳密ではない
test: mysql -h server -u test_user -ptest_password -e "show databases;"
interval: 5s
timeout: 5s
retries: 3
start_period: 5s
client:
container_name: client
image: mysql/mysql-server:5.7
command: mysql -h server -u test_user -ptest_password -e "show databases;"
depends_on:
server:
condition: service_healthy
この設定では、 client コンテナは server コンテナが 「完全に起動した」 状態を待つようになります。これで、 client コンテナが server コンテナに接続できるようになります。
★厳密には内部からの接続ができる状態となったが、外部から接続できる状態かはまだ疑問の余地が残る判定にはなっています。
まとめ
Docker Compose を使用すると、複数のコンテナを簡単に管理できます。
また、コンテナ間の依存関係を設定することで、コンテナ間の連携を簡単に行えます。
これらの機能を活用することで、複数のコンテナを効率的に管理できます。
ただし、複数のコンテナが連携し合うため、起動順序を正しく設定する必要があります。
そのための属性として、 depends_on と condition があります。これらを使用することで、コンテナ間の依存関係を設定し、コンテナ間の連携することができます。
ひとこと
コンテナ技術は、今後もさらなる発展を続けていくでしょう。
Docker Compose を使用することで、複数のコンテナを効率的に管理できます。
コンテナ間の依存関係を設定することで、コンテナ間の連携を簡単に行えます。
今後も、コンテナ技術を活用して、より効率的な開発を行っていきましょう!
ディスカッション
コメント一覧
まだ、コメントがありません