Flyway で MySQL のバージョン管理をしてみる in docker-compose

## 背景

デプロイやらローカル環境やらでいくつか不便なことが発生している今日この頃。

1. アプリをデプロイするたびに、DBの変更差分をSVNのログから目視で探していた。
1. viewの更新を手作業で実行しており、稀に誤って全tableを初期化する事態が発生していた。
1. それぞれの環境でどこまでDBの変更が適用されているのかよくわからない。
1. 作業者の開発環境のDBに差分を適用することが面倒。
1. デプロイ作業の自動化を進めたい。

といった問題の対応策として `flyway` を検討してみる。
※ ビルドツールなんてものは存在しない環境なのでプラグインは使えません。

最終的な姿は[こちら](https://github.com/RichterAstell/sample-flyway)


## 構成

* DB
	* MySQL 8.0.15
* Flyway
	* Flyway Community Edition 5.2.4 by Boxfuse

* 最終的なDirectory構成

```
sample-flyway
│  docker-compose.yml
│
├─flyway
│  ├─conf
│  │      flyway.conf
│  └─sql
│          V0.000__Baseline.sql
│          V1.001__Create_table_test.sql
│          V1.002__Insert_table_test.sql
│          V2.001__Create_person_table.sql
└─mysql
    └─initdb.d
            schema.sql
```



## DBの準備

まずは MySQL 単体で動作確認する。

```yml:docker-compose.yml
version: '3.4'

services:
  mysqldb:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: pass
```

起動して接続できることを確認する。

```
[sample-flyway]$ docker-compose up -d mysqldb
Creating network "sampleflyway_default" with the default driver
Creating sampleflyway_mysqldb_1 ... done

[sample-flyway]$ docker-compose ps
       Name                     Command             State          Ports
-------------------------------------------------------------------------------
sampleflyway_mysqldb_1   docker-entrypoint.sh mysqld   Up      3306/tcp, 33060/tcp

[sample-flyway]$ docker-compose exec mysqldb mysql -u root -p -e 'show databases;'
Enter password:
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sample_db          |
| sys                |
+--------------------+
```

DBが動くことを確認。



## Flywayの準備

`docker-compose.yml` に `flyway` の記述を追加する。

```yml:docker-compose.yml
version: '3.4'

services:
  mysqldb:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: pass
  # === ココカラ ===
  migratedb:
    image: boxfuse/flyway:latest
    volumes:
      - ./flyway/sql:/flyway/sql
      - ./flyway/conf:/flyway/conf
    depends_on:
      - mysqldb
    command: migrate
  # === ココマデ ===
```



設定ファイルを用意する。

```flyway.conf
flyway.url=jdbc:mysql://mysqldb:3306/sample_db
flyway.user=root
flyway.password=pass
```

差分のSQLを用意する。

```sql:V1.001__Create_table_test.sql
CREATE TABLE v1_difference_application (id INT);
```

```sql:V1.002__Insert_table_test.sql
INSERT INTO v1_difference_application VALUES(100);
INSERT INTO v1_difference_application VALUES(101);
```

```sql:V2.001__Create_person_table.sql
CREATE TABLE person (id INT NOT NULL, name VARCHAR(100) NOT NULL);
```

マイグレーションしてみる。

```
[sample-flyway]$ docker-compose run --rm migratedb
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Successfully validated 3 migrations (execution time 00:00.039s)
Creating Schema History table: `sample_db`.`flyway_schema_history`
Current version of schema `sample_db`: << Empty Schema >>
Migrating schema `sample_db` to version 1.001 - Create table test
Migrating schema `sample_db` to version 1.002 - Insert table test
Migrating schema `sample_db` to version 2.001 - Create person table
Successfully applied 3 migrations to schema `sample_db` (execution time 00:00.358s)
```

成功した。DBを確認する。

```
[sample-flyway]$ docker-compose exec mysqldb mysql -u root -p -D sample_db -e 'select * from v1_difference_application;'
Enter password:
+------+
| id   |
+------+
|  100 |
|  101 |
+------+
```

マイグレーションできた。よかった。
`docker-compose down` で停止しておく。



## 新規作成以外の場合はどうする？

既存システムの場合は既にテーブルやデータが作られてしまっているので、上記手順では「既にデータベースが作られてるよ」と言われマイグレーションできない。
そのため、ベースラインとして既にある構造の分を設定することで、そこからの差分を管理してもらうことができる。

`docker-compose up -d mysqldb` でDBを起動しておく。

既存DBのSQLを追加

```sql:V0.000__Baseline.sql
CREATE TABLE init_table (id INT);
```

`migratedb` サービスの `command` を `info` で上書きして初期状態を確認する。

```
[sample-flyway]$ docker-compose run --rm migratedb info
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Schema version: << Empty Schema >>

+-----------+---------+---------------------+------+--------------+---------+
| Category  | Version | Description         | Type | Installed On | State   |
+-----------+---------+---------------------+------+--------------+---------+
| Versioned | 0.000   | Baseline            | SQL  |              | Pending |
| Versioned | 1.001   | Create table test   | SQL  |              | Pending |
| Versioned | 1.002   | Insert table test   | SQL  |              | Pending |
| Versioned | 2.001   | Create person table | SQL  |              | Pending |
+-----------+---------+---------------------+------+--------------+---------+
```

`State` が `Pending` になっている。
ベースラインを設定してみる。

```
[sample-flyway]$ docker-compose run --rm migratedb baseline -baselineVersion=0_000 -baselineDescription=Baseline
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Creating Schema History table: `sample_db`.`flyway_schema_history`
Successfully baselined schema with version: 0.000


[sample-flyway]$ docker-compose run --rm migratedb info
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Schema version: 0.000

+-----------+---------+---------------------+----------+---------------------+----------+
| Category  | Version | Description         | Type     | Installed On        | State    |
+-----------+---------+---------------------+----------+---------------------+----------+
|           | 0.000   | Baseline            | BASELINE | 2019-03-26 06:57:18 | Baseline |
| Versioned | 1.001   | Create table test   | SQL      |                     | Pending  |
| Versioned | 1.002   | Insert table test   | SQL      |                     | Pending  |
| Versioned | 2.001   | Create person table | SQL      |                     | Pending  |
+-----------+---------+---------------------+----------+---------------------+----------+
```

Baseline が設定できた。
マイグレートしてみる。

```
[sample-flyway]$ docker-compose run --rm migratedb
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Successfully validated 4 migrations (execution time 00:00.065s)
Current version of schema `sample_db`: 0.000
Migrating schema `sample_db` to version 1.001 - Create table test
Migrating schema `sample_db` to version 1.002 - Insert table test
Migrating schema `sample_db` to version 2.001 - Create person table
Successfully applied 3 migrations to schema `sample_db` (execution time 00:00.241s)


[sample-flyway]$ docker-compose run --rm migratedb info
Starting sampleflyway_mysqldb_1 ... done
Flyway Community Edition 5.2.4 by Boxfuse
Schema version: 2.001

+-----------+---------+---------------------+----------+---------------------+----------+
| Category  | Version | Description         | Type     | Installed On        | State    |
+-----------+---------+---------------------+----------+---------------------+----------+
|           | 0.000   | Baseline            | BASELINE | 2019-03-26 06:57:18 | Baseline |
| Versioned | 1.001   | Create table test   | SQL      | 2019-03-26 06:57:59 | Success  |
| Versioned | 1.002   | Insert table test   | SQL      | 2019-03-26 06:57:59 | Success  |
| Versioned | 2.001   | Create person table | SQL      | 2019-03-26 06:57:59 | Success  |
+-----------+---------+---------------------+----------+---------------------+----------+
```

`State` が `Success` になった。よかった。



## +alpha

1. k8sの初期化コンテナにするとか
1. CI/CDツールでコミット検知して自動マイグレーションするとか

してみると楽しいかもしれない。