データベースマイグレーションツールでフレームワーク非依存のもので有名所といえばflywayだが、Java製のツールであるためJVMを入れておく必要がある。JVMを入れるというのが意外とハードルで、稼働中の環境に入れる場合は影響範囲が怖かったり、CIのたびに容量が小さくないJVMを入れるのはちょっと・・・というケースもある。
そんなときにオススメしたいのがGo製のマイグレーションツールmattes/migrateだ。flywayと同じくCLIツールだが、JVMのようなランタイムは不要だ。ビルド済みのバイナリがGitHubで提供されていて、インストールはcurlやwgetですぐにできる。容量も7.1Mと小さい。MySQLやPostgreSQLなど主要なデータベースに対応している。Go製であるが、マイグレーションファイルは素のSQLで書くから、ユーザにはGo言語の知識は必要ない。
mattes/migrateにぴったりな利用シーン
- Webフレームワークを使ってない。もしくは、Webフレームワークにマイグレーション機能が無い。
- 運用フェーズに入ったシステムに後からマイグレーションの仕組みを導入したい。
- 使っている言語にベストなマイグレーションツールが存在しない。急成長中の比較的マイナーな言語とか。
ちなみに、JVMが入ることで環境が汚れるのだけが気になる、容量はさして重要じゃない、Dockerを使えるという環境であればflywayのDockerコンテナを使うという選択肢もある。dhoer/flyway - Docker Hub
インストール
macOSでのインストール。(Homebrewは対応途中とのこと。今後に期待。)
cd /usr/local/bin
curl -L https://github.com/mattes/migrate/releases/download/v3.0.1/migrate.darwin-amd64.tar.gz | tar xvz
mv migrate.darwin-amd64 migrate
Linuxでのインストール。(apt-getでもインストールできる)
cd /usr/local/bin
curl -L https://github.com/mattes/migrate/releases/download/v3.0.1/migrate.linux-amd64.tar.gz | tar xvz
mv migrate.linux-amd64 migrate
今回紹介するバージョン
v3.0.1を紹介する。
$ migrate --version
3.0.1
ヘルプを見てみる
$ migrate -help
Usage: migrate OPTIONS COMMAND [arg...]
migrate [ -version | -help ]
Options:
-source Location of the migrations (driver://url)
-path Shorthand for -source=file://path
-database Run migrations against this database (driver://url)
-prefetch N Number of migrations to load in advance before executing (default 10)
-lock-timeout N Allow N seconds to acquire database lock (default 15)
-verbose Print verbose logging
-version Print version
-help Print usage
Commands:
goto V Migrate to version V
up [N] Apply all or N up migrations
down [N] Apply all or N down migrations
drop Drop everyting inside database
force V Set version V but don't run migration (ignores dirty state)
version Print current migration version
マイグレーションをやってみる
マイグレーションファイル置き場を作る
mkdir ./sql
cd sql
スキーマのアップグレード用のマイグレーションファイルはN_$ファイル名.up.sql
というファイル名にする。逆にダウングレード用はN_$ファイル名.down.sql
といった命名規則。ここのN
はマイグレーションバージョンを意味する。1から連番で始めてもいいし、20170518のように日付っぽくしてもいい。
cat > 1_Create_user_table.up.sql <<'SQL'
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
SQL
マイグレーションを実施するときは、-database
で変更先DBをURL形式で指定する。URL形式のうち(tcp(...)
はちょっと特殊な書き方で直感的でなかった)。-path
はマイグレーションファイル*.up.sql
が置いてあるディレクトリを指定する。
$ migrate -database 'mysql://root:password@tcp(127.0.0.1:3306)/myapp' -path ./ up
1/u Create_user_table (21.823571ms)
状況を確認してみると、users
テーブルとは別に、マイグレーションステータスを記録するschema_migrations
テーブルが作られている。
$ mysql -uroot myapp -e 'SHOW TABLES'
+-------------------+
| Tables_in_myapp |
+-------------------+
| schema_migrations |
| users |
+-------------------+
このテーブルの中身はこんなかんじ。
$ mysql -uroot myapp -e 'SELECT * FROM schema_migrations'
+---------+-------+
| version | dirty |
+---------+-------+
| 1 | 0 |
+---------+-------+
migrate up
コマンドの操作は冪等性が保証するために、このテーブルが使われている。つまり、マイグレーションファイルに追加がない限り、何度up
してもマイグレーションが重複して適用されることはない。
$ migrate -database 'mysql://root@tcp(127.0.0.1:3306)/myapp' -path ./ up
no change
つぎにALTER TABLE
するマイグレーションファイルを作ってみる。
cat > 2_Add_username_to_users_table.up.sql <<'SQL'
ALTER TABLE `users` ADD `username` VARCHAR(255) NOT NULL DEFAULT '';
SQL
マイグレーションを走らせてみる。
$ migrate -database 'mysql://root@tcp(127.0.0.1:3306)/myapp' -path ./ up
2/u Add_username_to_users_table (25.283128ms)
先程は、schema_migrations
テーブルを覗いてマイグレーションバージョンを確認したが、migrate version
コマンドでも確認できる。
$ migrate -database 'mysql://root@tcp(127.0.0.1:3306)/myapp' -path ./ version
2
運用中のアプリに途中から導入する場合
運用中のアプリに途中からマイグレーションの仕組みを取り入れる場合を「ToDoアプリ」を例に考えてみよう。運用中のシステムには、既にユーザテーブルやToDoデータが登録されている。ここに、チームでToDoを管理できるようなチーミング機能が追加されたとしよう。本番環境にマイグレーションを導入するのもこのチーミング機能のリリースと同時期に行う計画だ。
たとえば、このような状況だ。
- usersテーブル … 運用中・データあり。
- todosテーブル … 運用中・データあり。
- teamsテーブル NEW! … 今回のアップデートで追加されるチーム機能。当然運用中システムにテーブルはない。ここからマイグレーション管理導入。
このようなケースでも、開発環境はゼロからセットアップすることがあるので、1〜2のテーブルについてもマイグレーションファイルにしておきたい。一方で、本番環境では3から適用したい。こういったニーズが想定される。
このような場合、どうやって管理したら良いか?
まずは、それぞれのテーブルをマイグレーションファイルに起こす。
1_Create_users_table.up.sql
2_Create_todos_table.up.sql
3_Create_teams_table.up.sql
これを、migrate
でうまく扱うにはforce
コマンドを使うとよい。force
コマンドは特定のバージョンまでマイグレーションを適用したことにするコマンドだ。マイグレーションは走らない。
本番環境をマイグレーションに乗せる
本番環境は2まで適用したことにしたいので、マイグレーションの仕組み導入時、つまり初回に次のコマンドを走らせる。
migrate -database 'mysql://...' -path ./ force 2
version
コマンドで、マイグレーション状況を確認してみよう。2になっているはずだ。
$ migrate -database 'mysql://...' -path ./ version
2
そして、新機能のv3をリリースするときにはup
コマンドを流すようにする。その後のアップデート時も同様だ。
$ migrate -database 'mysql://...' -path ./ up
3/u Create_teams_table (35.575331ms)
これで、本番環境もマイグレーションに乗ることができる。
まとめ
mattes/migrateは、シングルバイナリで動作するデータベースマイグレーションツールで、運用中のシステムにマイグレーションの仕組みを導入するのに便利。