運用中のシステムにも導入可なGo製のマイグレーションツール「mattes/migrate」

  • 41
    いいね
  • 0
    コメント

データベースマイグレーションツールでフレームワーク非依存のもので有名所といえば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を管理できるようなチーミング機能が追加されたとしよう。本番環境にマイグレーションを導入するのもこのチーミング機能のリリースと同時期に行う計画だ。

たとえば、このような状況だ。

  1. usersテーブル … 運用中・データあり。
  2. todosテーブル … 運用中・データあり。
  3. 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は、シングルバイナリで動作するデータベースマイグレーションツールで、運用中のシステムにマイグレーションの仕組みを導入するのに便利。