ローカル環境で初回コンテナ起動時にMySQLのマイグレーションをする

コンテナ化しているプロダクトをローカルで動作させる場合、MySQLのコンテナを用意しdocker composeで管理することが多いかと思います。

スキーマのマイグレーションは別スクリプトになっていて初回セットアップ時の手順で「docker compose upをした後、マイグレーションスクリプトを実行してください」のようなケースも多いかと思います。しかし、手順を正しく書かなければならなかったり、手順を飛ばしてしまいうまくセットアップがせきないなどの問題があります。

この記事ではMySQLのdockerイメージで使えるdocker-entrypoint-initdb.dという機能の中でマイグレーションを実行する方法を紹介します。

環境

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"

cateiru-sso/docker/docker-compose.db.yaml at c6f5571cd54097047d004408a5fefd8a3b51d338 · cateiru/cateiru-sso

この例では、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

cateiru-sso/db/setup/003_migration.sh at c6f5571cd54097047d004408a5fefd8a3b51d338 · cateiru/cateiru-sso

...
..
.

これで、初回セットアップ時にはdocker compose upを実行するだけで自動でマイグレーションも実行してくれるようになります。

ちなみに、追加でマイグレーションする場合はdocker compose exec bash -c "..."でマイグレーションを実行すると良いかと思います。