こんにちは。リスペクトの木村です。
雪もすっかり解け、いよいよ春が待ち遠しい季節となりましたがいかがお過ごしでしょうか。暖かくなると共にそろそろ花粉も飛び始めますので、長年花粉症を患っている身としては少し憂鬱になってきました・・・。
そんな今回は、VagrantのProvision機能を利用して、仮想環境内のDBにマイグレーションを行ってみた、という話題をお送りします。
想定している読者
- Vagrantでの開発に慣れている
※Vagrantって何?という人は、以下のサイトがわかりやすく説明してますのでご一読ください。
http://weblabo.oscasierra.net/vagrant/ - SQLについての知識が多少ある
- マイグレーションは少し面倒だと思っているけど、そろそろ本腰入れてやらないと・・・とも思っている
という方です。(要は自分です・・・。)
概要
弊社は最近までレガシーな環境(バージョン管理を使用していなかったり、テスト環境を複数人で共有していたり・・・)だったのですが、少しずつGitやVagrantといった開発環境を利用しつつあります。
DBもそろそろ構成管理をしなければと思っていたのですが、これも以下のような理由でなかなか進みませんでした。
- スクラッチで開発したアプリケーションの場合は、マイグレーションが標準で備わっていないため、一から準備する必要がある
- フレームワークを利用している場合は、マイグレーション機能が備わってたりプラグインで実現できる場合が殆どですが、フレームワーク毎に学習コストがかかってしまう。
導入にコストがかかると、検討だけして実施しないパターンに陥ってしまうので、いかに「手軽に」かつ「言語やフレームワークに依存しない」マイグレーションを実現できるかを考え・・・
- VagrantのProvision機能なら、起動中の環境へsshで入らなくても、必要なコマンドをシェルスクリプト化しておけば任意のタイミングや初回のvagrant up時に実行できる
- スタンドアロンで動作するマイグレーションツールと組み合わせれば、言語やフレームワークに依存せず使える
という所に目を付け、今回はVagrantに「Flyway」を組み合わせて、実装を行ってみました。
Flywayとは
今回はマイグレーションツールとして、内部で「Flyway( http://flywaydb.org/ )」を使用します。
基本的にJavaで動くように作られていますが、Java APIやMaven、ANTからだけではなくコマンドラインからでも実行できるため、Java以外でも利用できます。
その為、Javaが利用できる環境であれば言語やフレームワークに依存せずにマイグレーション作業を行えます。
また、マイグレーション時に使うファイルの中身はSQLクエリをそのまま書けば良いため、新たに構文を覚える必要があまり無いのも嬉しい所です。
難点として、生のSQLを使っているせいかマイグレーションのロールバックができませんので、そこは割り切って使う必要があります。
LiquibaseとFlyway
Flywayと同じようなツールとして、「Liquibase( http://www.liquibase.org/ )」というのもあります。(こちらもJava製)
簡単に比較してみるとこんな感じです。
Liquibaseと比べてFlywayが・・・
勝っている点
- 手軽に始められる(QuickStartを読み比べてみると、必要な設定やステップ数が異なる)
- SQLのみを書けば良いというシンプルさ
- 1ファイル1バージョンなので、分割して書ける(LiquibaseのChangelogは1ファイルに書いていく)
- ドキュメントが(比較的)分かりやすい
- AzureのSQLServer/Google Cloud SQLといったクラウド環境にも対応
- Callbackが利用できるので、処理の前後にフックを掛ける事ができる
劣っている点
- ロールバックはできない
- 異なる種類のDBサーバ間でも互換性が保てないので、移植などの際にはクエリの書き直しが発生する場合がある
- コミュニティによるサポートが主で、エンタープライズ向け商用サポートは無い
- テーブル定義書や更新履歴は出力不可能
- DBインスタンス間のdiffは取れない
(http://qiita.com/scova0731/items/a0c634f4a374ec8b1c9a も参考にどうぞ)
比較したのを見てみると、対象となるシステムや環境がそこそこ違っているように見えます。
Flywayは小~中規模のシステム向きでシンプルに扱え、Liquibaseは大規模なシステム向きで高度なマイグレーションを行えるが若干じゃじゃ馬、といった所でしょうか。
今回はLiquibaseまでの機能を求めていないなかったり覚える事も多いため、とっかかりやすそうなFlywayを採用しています。
構築環境
今回は次のような環境で構築しています。
- ホストOS: Windows7 Pro x64
- Vagrant: 1.6.5
- Vagrant Box: smallhadroncollider/centos-6.5-lamp ( https://atlas.hashicorp.com/smallhadroncollider/boxes/centos-6.5-lamp )
- MySQL Server: 5.5.36
- Flyway: 3.1
今回はMySQLを使用します。他のRDBMSの場合は、別途JDBCのドライバが必要だったり、DBへ接続する時の構文が少し異なります。
また、LAMP環境が構築済みのBoxを使っているので、他のBoxを使う場合は必要に応じて導入して下さい。
手順
では、実際に導入〜実行までやってみます。
予め、適当なDBを作成しておいて下さい。今回は「test_migration」というテーブルを作成しましたので、これを利用します。
Flywayの導入
Flywayを利用するため、次のものを導入します。
- Java … 入っていない場合は導入する必要があります。
- Flyway本体( http://flywaydb.org/getstarted/download.html ) … 今回の主役です。Command-line Toolを使用します。
- MySQLのJDBCドライバ( http://dev.mysql.com/downloads/connector/j/ ) … FlywayからMySQLを制御するために導入します。
まずはJavaとFlywayを導入します。
$ sudo yum install java-1.8.0-openjdk $ wget http://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/3.1/flyway-commandline-3.1.tar.gz $ tar zxvf flyway-commandline-3.1.tar.gz
ここまで導入したら、一旦
$ flyway-3.1/flyway
と入力してFlywayを実行して、コマンド一覧が出てくるか確認します。
[vagrant@localhost ~]$ flyway-3.1/flyway Flyway (Command-line Tool) v.3.1 ******** * Usage ******** flyway [options] command (以下略)
出てくるのを確認したら、次はJDBCドライバを導入します。
$ wget http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.34.tar.gz $ tar zxvf mysql-connector-java-5.1.34.tar.gz $ cp mysql-connector-java-5.1.34/mysql-connector-java-5.1.34-bin.jar flyway-3.1/drivers/
これで導入完了です。
クエリの作成
続いて、マイグレーションファイル(クエリ)を作成します。
デフォルトの命名規則は「V(バージョンナンバー)__(概要).sql」なので、「V1__add_testtable.sql」というファイル名で作成し、flyway-3.1内にある「sql」ディレクトリに置いておきます。
(詳しい規則は http://flywaydb.org/documentation/migration/sql.html に記載してあります。)
sqlディレクトリにあるマイグレーションファイルが、バージョンに沿って順次実行されます。
ファイルの中身は普通のSQL文を書けば良いので、今回はCREATE TABLE文を書きます。
CREATE TABLE testtable ( id int(11) NOT NULL AUTO_INCREMENT, data varchar(32), PRIMARY KEY (id) ) ENGINE=innodb;
単独で実行
試せる環境は整いましたので、Flyway単独でマイグレーションを実行してみます。
今回は検証なのでパスを通さずに実行していますが、今後常用するようであれば別途パスを通しておくと良いかもしれません。
$ cd flyway-3.1 $ ./flyway migrate -X -url=jdbc:mysql://localhost/test_migration -user=root -password=hogehoge
今回指定したオプションはこんな中身です。
- -X … 詳細な出力を表示するデバッグモードです。
- -url … JDBCの接続文字列です。MySQLの場合は、基本的に jdbc:mysql://(ホスト)/(DB名) という書き方で記述します。
- -user … 接続に使用するユーザーです。
- -password … ユーザーに紐付くパスワードです。
実行してみるとズラズラと長ーく出力されますが、-Xでデバッグモードが有効なためです。
(付いてない場合は、「DEBUG:」から始まっていない行のみが出力されるので、短くなります。)
[vagrant@localhost ~]$ ./flyway migrate -X -url=jdbc:mysql://localhost/test_migration -user=root -password=hogehoge Flyway (Command-line Tool) v.3.1 DEBUG: Adding location to classpath: /home/vagrant/flyway-3.1/drivers/postgresql-9.3-1102-jdbc4.jar DEBUG: Adding location to classpath: /home/vagrant/flyway-3.1/drivers/sqlite-jdbc-3.7.15-M1.jar (略) DEBUG: Executing SQL: CREATE TABLE testtable ( id int(11) NOT NULL AUTO_INCREMENT, data varchar(32), PRIMARY KEY (id) ) ENGINE=innodb DEBUG: Successfully completed and committed migration of schema `test_migration` to version 1 DEBUG: MetaData table `test_migration`.`schema_version` successfully updated to reflect changes DEBUG: Locking table `test_migration`.`schema_version`... DEBUG: Lock acquired for table `test_migration`.`schema_version` Successfully applied 1 migration to schema `test_migration` (execution time 00:00.272s).
最後に「Successfully applied 1 migration to schema」と表示されれば成功です。
実際にmysqlでshow tablesすると、V1__add_test_table.sqlでcreateした「testtable」と、マイグレーション管理テーブルである「schema_version」が作成されています。
mysql> show tables; +--------------------------+ | Tables_in_test_migration | +--------------------------+ | schema_version | | testtable | +--------------------------+ 2 rows in set (0.00 sec)
ここまでで、一通りの動作確認ができました。
一旦、test_migrationテーブルにあるtesttableとschema_versionはdropして、最初の状態に戻しておきます。
mysql> drop table schema_version; Query OK, 0 rows affected (0.02 sec) mysql> drop table testtable; Query OK, 0 rows affected (0.00 sec)
シェルスクリプトの作成
続けて、Vagrantと連動するのに必要なシェルスクリプトを作成します。
今回はVagrantで立ち上げた仮想環境内に作成せず、ローカル環境のVagrantfileがあるディレクトリ下に色々作成するようにします。
Vagrantfileがある所は/vagrant/としてデフォルトでマウントされ、ローカル環境とのやりとりがしやすいためです。
必要な作業は・・・
- Vagrantfileのあるディレクトリに「migrate」ディレクトリを作成
- migrate内に「script」「sql」ディレクトリを作成
- scriptにはメインのスクリプトを、sqlにはマイグレーションするSQLファイルを設置
構成図にするとこんな感じです。
root_directory/ ├ Vagrantfile └ migrate/ ├ script/ │ └ run.sh └ sql/ ├ V1__add_test_table.sql └ ...
ただ、実運用では別の場所にGitリポジトリがありそこにsynced_folderでマウントしているという場合も多いと思います。
その場合は、上記のroot_folderをマウント先にしたり、スクリプトは上の構成に沿って作成して、マイグレーション用SQLだけsynced_folderでマウントした先を参照するようにする事も可能です。
run.shの中身はFlywayを実行するだけですので、こんな感じです。先ほど実行したスクリプトと若干オプションが変化しています。
#!/bin/bash echo "---- Migration ----" /home/vagrant/flyway-3.1/flyway migrate -url=jdbc:mysql://localhost/test_migration -user=root -password=hogehoge -locations=filesystem:/vagrant/migrate/sql/
違いは、-Xオプションを使用していない所と、1つオプションが増えている所です。
- -locations … マイグレーションファイルがある場所を指定します。今回はゲスト側から見た場合の/vagrant/migrate/sql/に作成したので、「/vagrant/migrate/sql」を指定しています。
SQLディレクトリを別にマウントした場合は、引数の「filesystem:」以降のパスをsynced_folderでマウントした先にある管理ディレクトリのパスに置き換えて下さい。
Vagrantfileへの設定
provisionでコマンドを実行させるにはVagrantfileにスクリプトのパスを指定する必要があるので、指定します。
1行追加するだけですので、一番下の「end」の手前の行に記述しておきます。
config.vm.provision :shell, :path => "./migrate/script/run.sh"
Vagrantfileのあるディレクトリ下に置いて無い場合は、パスをスクリプトがある先に書き換えると、同じように動作します。
vagrant provisionで実行
準備が整いましたので、早速vagrant provisionで実行してみます。
==> default: Running provisioner: shell... default: Running: C:/Users/~/vagrant-shell20150221-31152-1w0bvxu.sh ==> default: ---- Migration ---- ==> default: Flyway (Command-line Tool) v.3.1 ==> default: Database: jdbc:mysql://localhost/test_migration (MySQL 5.5) ==> default: Validated 1 migration (execution time 00:00.018s) ==> default: Creating Metadata table: `test_migration`.`schema_version` ==> default: Current version of schema `test_migration`: << Empty Schema >> ==> default: Migrating schema `test_migration` to version 1 ==> default: Successfully applied 1 migration to schema `test_migration` (execution time 00:00.172s).
同じように、「Successfully applied 1 migration to schema」と表示され、テーブルが2つ作成されていれば成功です。
まとめ
という訳で、Vagrant環境上のDBマイグレーションがお手軽に行えました。
今後はSQLファイルを追加してvagrant provisionを実行するだけでマイグレーションされるようになりますし、destroyしてupした場合はprovisionが自動で実行されるため、自動で最新の状態になってくれます。
そのため、複数人で作業する時は、ある程度同じようなDB環境で作業できるのではないかと思います。
今回はcreate tableしか使っていませんが、もちろんinsertやupdate/deleteも使えますので、テストデータの追加や擬似的なロールバック的な事も行えます。
簡易的でもいいからマイグレーションに手を出してみたいという方や、複数人の環境でDBの状況を保ちたいなと考えている方は是非お試し下さい。
現場からは以上です。