初めに
コンテナ起動後、毎回SQLを実行するのめんどくさいですよね。
ということで、Docker起動時に自動でSQLを実行してくれる方法を調べました。
やり方
ディレクトリ構成は以下です。
init.sql
がコンテナ起動時に実行したいSQLファイルになります。
workspace
├── docker-compose.yml
└── init
└── init.sql
create database initDB;
docker-compose.yml
にて、ホストPCの./init
と、コンテナの/docker-entrypoint-initdb.d
の二つを共有させるようにバインドマウントさせます。
これにより、コンテナ起動時に/docker-entrypoint-initdb.d
の中にinit.sql
作成されます。
version: '3'
services:
mysql:
image: mysql:latest
container_name: my-mysql-container
environment:
MYSQL_ROOT_PASSWORD: your-root-password
MYSQL_DATABASE: your-database-name
MYSQL_USER: your-username
MYSQL_PASSWORD: your-password
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d <- 追加
volumes:
mysql-data:
これで準備完了です。
docker compose up -d
でコンテナを作成します。
$ docker compose up -d
[+] Running 3/3
✔ Network mysqltest_default Created 0.0s
✔ Volume "mysqltest_mysql-data" Created 0.0s
✔ Container my-mysql-container Started 0.0s
$
docker exec
でコンテナの中に入ってMySQL
にログインします。
DBの一覧を見ると、init.sql
で定義したSQLが実行されていることがわかります。
$ docker exec -it my-mysql-container bash
bash-4.4#
bash-4.4#
bash-4.4# mysql -u root -p
Enter password:
mysql>
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| initDB | <- init.sqlで作成されたデータベース
| mysql |
| performance_schema |
| sys |
| your-database-name |
+--------------------+
6 rows in set (0.01 sec)
初回実行される仕組み
dockerイメージには、docker初回起動時に呼び出されるdocker-entrypoint.sh
というシェルスクリプトが定義されています。
MySQLのdocker-entrypoint.sh
には/docker-entrypoint-initdb.d
の内のsqlファイルを実行するという処理が書いてあるようです。
MySQL 8.0のdocker-entrypoint.sh
は以下。
https://github.com/docker-library/mysql/blob/master/8.0/docker-entrypoint.sh
疑問点
sqlファイル以外は実行できないの?
結論:SQLファイルとshファイルが実行可能
docker-entrypoint.shには以下のようなソースがあります。
docker_process_init_files() {
# mysql here for backwards compatibility "${mysql[@]}"
mysql=( docker_process_sql )
echo
local f
for f; do
case "$f" in
*.sh)
# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
# https://github.com/docker-library/postgres/pull/452
if [ -x "$f" ]; then
mysql_note "$0: running $f"
"$f"
else
mysql_note "$0: sourcing $f"
. "$f"
fi
;;
*.sql) mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
*.sql.bz2) mysql_note "$0: running $f"; bunzip2 -c "$f" | docker_process_sql; echo ;;
*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
*.sql.xz) mysql_note "$0: running $f"; xzcat "$f" | docker_process_sql; echo ;;
*.sql.zst) mysql_note "$0: running $f"; zstd -dc "$f" | docker_process_sql; echo ;;
*) mysql_warn "$0: ignoring $f" ;;
esac
echo
done
}
このソースによると、shファイルとsqlファイルの場合は実行、sqlファイルの圧縮ファイルであれば解凍してから実行しているようです。このため、shファイルとsqlファイルであれば実行可能になります。
複数ファイルがある場合はどれから実行されるの?
結論:ファイル名の昇順で実行される
docker-entrypoint-initdb.d
にあるファイルは名前の昇順で実行されます。
依存関係のあるファイルを実行させる場合、ファイル名の頭に数字をつけるなどして工夫することが必要がありそうです。
$ ls
1_init.sql 2_init.sql 3_init.sql
最後に
今回はコンテナ起動時にファイルを実行する方法を紹介しました。
便利なのでぜひ試してみてください。
参考
https://qiita.com/moaikids/items/f7c0db2c98425094ef10
https://zenn.dev/re24_1986/articles/978801ae092498
https://kakakakakku.hatenablog.com/entry/2017/11/08/193031
https://qiita.com/michikun06/items/24e99618dc59807b98c8