はじめに
社内で現在担当しているプロジェクトではローカル環境にはdocker compose
、CI環境にはcodebuild
を使用しています。
今回はcodebuild
内でもdocker compose
を流用していて、自動テストを回す際にAPサーバーとDBサーバーの疎通確認をする必要があり、そこでハマった事や最終的に採用した方法を共有したいと思います。
あくまで多くある解決策の内の一つですので、オプションとして見ていただければ嬉しいです🙇♂️
この記事でわかる事
- PHPやRailsなどのAPサーバーとして使うコンテナから、MySQLなどのDBサーバーとして使うコンテナへ確実に疎通できる事を保証する方法(connectionがないとか怒られない)
今回解決したい事
- CI環境でテストを自動実行している時に、DBサーバー(MySQL)のヘルスチェックが終了する前にAPサーバー(PHP)がテストを回してしまって、CIが失敗してしまうのを防ぎたい。
検証環境
- docker compose(version 3.9)
- APサーバー
- PHP 8.0
- DBサーバー
- mysql/mysql-server:5.7
- APサーバー
実装
検証その1 depends_onを使う
とりあえず最初は何も制御せずに試しました笑
とはいえ、何も考えなしと言う訳でもなくdocker comopose
のdepends_on
と言う記載をしていれば制御してくれていると思っていたのでそのままの状態で問題ないだろうと言う考えでした。
下記のようなコードです↓
php:
depends_on:
- db
ただ、この考えは間違っていました。
depends_on
の正しい挙動は、コンテナの起動順序とシャットダウンの順序を制御してくれると言うものでした。
詳しくは下記の公式ドキュメントに譲りますが、上記の例だと
- docker comopse up した時はdbコンテナ => PHPコンテナの順番で立ち上がる
- docker compose down した時はPHPコンテナ => dbコンテナの順でコンテナが停止する
この2つの挙動のみなので、今回の目的であるコンテナ同士の通信が正常に疎通するか という部分は depends_on
では制御できませんでした。なのでこの案は没です。
depends_on
のドキュメントはこちら↓
https://docs.docker.com/compose/startup-order/
検証その2 depends_on + condition
さて次はどうしようかという事で、docker composeのみで何か制御する方法があるのではと思い公式ドキュメントを漁っていました。するとなんとやりたい事とバッチリ一致しているのが、出てきました..!
どうやら、depends_on
のcondition
というオプションと、helthcheck
と言う機能を使えば実装できるという事です。コードは下記になります↓
php:
depends_on:
db:
condition: service_healthy
簡単ですね!condition
が増えただけです!
service_healthy
というのは、dockerの持つhelthcheck機能でhealthyになっているかどうかを待ってから起動してくれると言うものです。めちゃ便利です..!
今回は、mysql/mysql-server:5.7
のdockerイメージにhealthcheckの機能が含まれていたので、その起動確認をしています。こんなやつです↓
はい!ちゃんとhealthy
になっているのでこれで間違いなく疎通できますね!
ただしこの方法は、docker compose公式ドキュメントのversion2.1には記載があるものの、version3では削除されていました。。
心配になりググってみると
Version 3 no longer supports the condition form of depends_on.
と言う文言がかなりの数出てきました。
公式ドキュメントではその記述は現時点では発見できなかったのですが、ドキュメントに載っていないものを使用するのは気が引けるなと思い、この方法は断念しました。
検証その3 シェルスクリプト(採用)
結構時間を潰しましたが、最終的に答えは公式ドキュメントにも載っていました。
wait-for-it.sh
と言うものを使えという事でどうやらシンプルにシェルスクリプトを使えと言う感じらしいです。
いくつかリポジトリが紹介されていましたが、そこまで大それた制御を求めていなかったので、今回は自作しました。
リポジトリが気になる方は以下で紹介されてます↓
実際に使用したコードは↓です
#!/usr/bin/env ash
set -e
MAX_ATTEMPTS=10
for ((i=1; i <= $MAX_ATTEMPTS; i++)); do
SLEEP_TIME=$(( 6 * $i ))
HELTHCHECKTSTATE=$(docker inspect --format='{{json .State.Health}}' {dbコンテナ名} | jq -r '.Status')
echo $HELTHCHECKTSTATE
if [ "$HELTHCHECKTSTATE" = "healthy" ]; then
echo "ready for test!!!"
break
fi
sleep $SLEEP_TIME
done;
ポイントと言うほどのことはありませんが、最大60秒間待ってつながらなければ、流石に何かがおかしいだろうと言うところで、最大60秒間まで、6秒ずつ待ち時間を増やしていくシェルスクリプトを記載しました。
また、healthcheckの値は、docker inspect
で確認ができるので、それを jq
で加工して取り出しています。
実際のcodebuildの結果は、↓になります
しっかりhealthy
になって処理が終了していますね!
おわりに
最終的には弊社では最後の方法を採用しましたが、おそらく他にも色々な方法があると思います!
もっといい方法があるよ!とか、このオプションを使えば、シェルスクリプトなしでもできる!とかあればどんどんコメントなど頂ければと思います🙇♂️
ありがとうございました!