105
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-05-18

データベースマイグレーションツールでフレームワーク非依存のもので有名所といえば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テーブル … 運用中・データあり。
  • 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は、シングルバイナリで動作するデータベースマイグレーションツールで、運用中のシステムにマイグレーションの仕組みを導入するのに便利。

105
74
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
105
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?