結論
MySQL docker コンテナを、テスト用途等で同じ初期データで何度も起動する場合、
初期データを実行時でなくビルド時に反映する方が起動が高速になります。
このWebページ の前半に方法が示されています。
公式イメージの docker-entrypoint.sh の一部を sed で置き換える素朴な方法です。
FROM mysql as builder
COPY (初期化用の.sqlファイル等) /docker-entrypoint-initdb.d/
# ここでスクリプトの特定の行をおきかえ
RUN ["sed", "-i", "s/exec \"$@\"/echo \"not running $@\"/", "/usr/local/bin/docker-entrypoint.sh"]
# 置き換え済みのスクリプトを呼び出し、初期データを反映
# 本家では一旦--data-dirを別に指定していますが、しなくても動きます
RUN ["/usr/local/bin/docker-entrypoint.sh", "mysqld"]
FROM mysql
COPY --from=builder /var/lib/mysql /var/lib/mysql
置き換え対象になっているスクリプトファイルは Github のここにあります(8.0系)。
この行が変更された場合、想定通りに動作しない可能性があります。
tmpfsを設定してメモリ上にデータベースを構築する方法と同時に使用し、更に高速にすることも出来そうです。
背景
MySQL の docker コンテナを、テスト用に毎回 docker compose down (docker container rm ...) して使っています。
ログを見ていると、コンテナを削除せず使い回す場合とは起動時の挙動が異なることに気付きました。
-
毎回コンテナを削除(毎回初期データ入力):
- Temporary server なるものが起動
- /docker-entrypoint-initdb.d に入れたスクリプト類の反映
- Temporary server のシャットダウン
- 通常のmysqlサーバが起動
-
コンテナ削除無し(既にデータベース関連のファイル有り):
- 通常のmysqlサーバが起動
コンテナを削除しない場合の方が圧倒的に起動が早いです。
また、コンテナを毎回削除するか否かは本当は重要ではなく、/var/lib/mysql にきちんと初期化されたデータベース関連のファイルが既に存在するかの差だと思われます。
(volume 設定を行い永続化した MySQL Docker コンテナも後者と同じ挙動をするので)
前者の初期化を同じデータに対して毎回行うのは無駄ではないかと考え始めました。テスト環境を docker 化しているので、テストの度に10秒程度の時間が無駄に消費されるのは避けたいです。
結果、上記の方法にたどり着きました。
余談
docker-compose 設定には healthcheck という項目が有り、適切に設定すれば「コンテナ内の目的のプログラムが応答可能になるまで別のコンテナの起動を待機」することが出来ます。
ただしMySQLコンテナは初期化時にTemporary serverが起動する時間が10秒程度あるので、healthcheck の test コマンドの指定次第では Temporary server の起動を検出してしまい、思ったように動作しないという問題が起こります。
参考...
https://github.com/docker-library/mysql/issues/930https://stackoverflow.com/questions/42567475/docker-compose-check-if-mysql-connection-is-ready
https://genzouw.com/entry/2023/03/23/083027/3325/
↑最後の例の様に、ユーザ名とパスワードとデータベース指定し、データを引っ張ってくるコマンドが成功するか否か判断するのが最も確実そうです。
(極端な話、select 1; でも有効なはずです)
他の例も試しましたが、Temporary server の起動だけで誤検出している場合が有りました。
ここで紹介する方法では Temporary server はコンテナ起動時には動作しませんので、この対策をもう少しシンプルに出来るかもしれません。
それでは皆様、良い Docker ライフをお送り下さい。