コンテナ化しているプロダクトをローカルで動作させる場合、MySQLのコンテナを用意しdocker compose
で管理することが多いかと思います。
スキーマのマイグレーションは別スクリプトになっていて初回セットアップ時の手順で「docker compose up
をした後、マイグレーションスクリプトを実行してください」のようなケースも多いかと思います。しかし、手順を正しく書かなければならなかったり、手順を飛ばしてしまいうまくセットアップがせきないなどの問題があります。
この記事ではMySQLのdockerイメージで使えるdocker-entrypoint-initdb.d
という機能の中でマイグレーションを実行する方法を紹介します。
環境
- データベース: MySQL
- イメージ:
mysql:8.0-debian
- イメージ:
- マイグレーションツール: golang-migrate/migrate: Database migrations. CLI and Golang library.
- 実際に使っているリポジトリ: cateiru/cateiru-sso
docker-entrypoint-initdb.d とは?
- MySQLのDockerイメージでは、
/docker-entrypoint-initdb.d/
の中にshスクリプトやddlを記述することでMySQLをセットアップすることができます。 - ディレクトリ内のファイルは上から実行するので
1_hoge.sql
,2_foo.bar
のようにprefixをつけることで順番を定義することが可能です。 - 実行されるのは初回のセットアップ時で、MySQLのデータをボリュームで外に定義している場合、初回のみ実行され次回以降は
down -> up
しても実行されません。
マイグレーションを実行する
コンテナ内にgo-migrateを入れる
docker-entrypoint-initdb.d
はコンテナ内で実行されるのでコンテナ内にgo-migrateを入れる必要があります。Dockerfileを用意してもいいですが、今回はcompose.yaml
内に直接定義できるdockerfile_inline
を使用します。
services: db: build: context: .. dockerfile_inline: | FROM mysql:8.0-debian RUN apt update && apt install -y curl # Install https://github.com/golang-migrate/migrate RUN curl -s https://packagecloud.io/install/repositories/golang-migrate/migrate/script.deb.sh | bash \ && apt install -y migrate container_name: db environment: MYSQL_USER: docker MYSQL_ROOT_PASSWORD: root MYSQL_PASSWORD: docker MYSQL_DATABASE: cateiru-sso TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - ../db/.data:/var/lib/mysql - ../db/my.cnf:/etc/mysql/conf.d/my.cnf - ../db/setup:/docker-entrypoint-initdb.d - ../db/migrations:/migrations # マイグレーションで使う ports: - "127.0.0.1:3306:3306"
この例では、MySQLの定義を../db/.data
に、docker-entrypoint-initdb.d
を../db/setup
にて定義しています。
マイグレーション用のディレクトリも忘れずにマウントしておきます。
マイグレーションスクリプトを書く
次にマイグレーションを書きます。DSNで接続先をTCPで接続するとアクセスできないので注意が必要です。unixソケットを使ってあげるとうまくいくようです。
-path /migrations
はマウントしたマイグレーションディレクトリを指定します。
#!/bin/bash MYSQL_USER="docker" MYSQL_PASSWORD="docker" OP_DATABASES=( "cateiru-sso" "cateiru-sso-test" ) for OP_DATABASE in "${OP_DATABASES[@]}" ; do echo "Migrate ${OP_DATABASE}..." MYSQL_DSN="mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@unix(/var/run/mysqld/mysqld.sock)/${OP_DATABASE}" migrate -path /migrations -database "${MYSQL_DSN}" up done
...
..
.
これで、初回セットアップ時にはdocker compose up
を実行するだけで自動でマイグレーションも実行してくれるようになります。
ちなみに、追加でマイグレーションする場合はdocker compose exec bash -c "..."
でマイグレーションを実行すると良いかと思います。