みなさん、Dockerを知っていますか?DockerはLinuxコンテナの使用を劇的に簡単にしてくれるツールです。最近、じわじわと人気が出てきてて、QiitaでもいくつかDockerについて記事を書いている人もいます。
DockerはコンテナベースのVM(のようなもの)を提供してくれます。VMが手軽に欲しいならVagrant使えば?とか考えると思いますが、コンテナはVMよりも軽量という利点があります。
軽量の利点は主にふたつあります。
瞬間起動: マシンの起動/停止がめちゃくちゃ早いです。VMなら停止した状態からアプリケーションを実行するまでOSをブートする時間がかかりますが、Linuxコンテナはそんなの全然ありません。マシンを起動した瞬間にアプリケーションを走らせることができます。
複数同時起動: 一度に沢山のインスタンスを起動することができます。VMであれば、いくら動かすアプリケーションを限定してもOSはロードされなければいけないので、それなりにメモリを食います。だから、ひとつのホストで沢山VMを同時に走らせることはできません。Linuxコンテナならできます。もちろん、ホストマシンの性能にもよりますが、VMよりは遥かに沢山です。
Dockerの使いどころ
これらの利点を何に活かすことができるかな??と考えた時、テストの実行環境に使うことを真っ先に思いつきました。テストはできるだけ独立した環境で実行されるのが望ましいので、VMを生成 -> テスト実行 -> 削除 という方法をしている人もいると思いますが、欠点はVMの生成/削除は重いので時間がかかってしまいます。なので、並列でやりたくなりますが前述したようにリソースを食うので沢山のマシンが必要になってしまいます。Dockerなら二つの問題を一気に解決してくれます。
CucumberテストをDockerで並列実行してみよう
Dockerのインストールやイメージの作成は他の人も書いているので省略します。ここでは、イメージからコンテナを起動して、各コンテナにひとつのcucumber featureを実行させるというのをシェルスクリプトをつかってやる方法を紹介します。
前提
- kimh/ruby-baseというイメージが存在する
- kimh/ruby-baseにはテストと実行環境が整っている。(つまり、ログインしてbundle exec cucumberとやればテストを実行できる)
- ローカルマシンの ./features ディレクトリと同じ内容のファイルが kimh/ruby-baseの/git/your_app/features ディレクトリ配下にある
- 1コンテナ / 1feature で実行する
以下のような簡単なシェルスクリプトで実現できます。
DID=""
container="kimh/ruby-base"
dir="/git/your_repo"
for feature in `find ./features/`
do
DID=$DID" "`docker run -d $container /bin/bash -c "
source /etc/profile
cd $dir
export LC_CTYPE="ja_JP.UTF-8"
export RAILS_ENV=test
bundle exec cucumber $feature -r features/
"`
done
docker wait $DID
やっていることをまとめると
1. ./featutes以下にあるファイルを各featureファイルに分割して、feature変数に入れてイテレートする
for feature in `find ./features/`
2. コンテナを起動して、feature変数のcucumber featureを実行するコマンドを実行して、コンテナIDを変数に格納します。
DID=$DID" "`docker run -d $container /bin/bash -c "
source /etc/profile
cd $dir
export LC_CTYPE="ja_JP.UTF-8"
export RAILS_ENV=test
bundle exec cucumber $feature -r features/
"`
-d
はバックグランドでコンテナを起動します。docker run
の戻り値はコンテナのIDです。後から状態を確認するのに必要になります。イテレートが終わった時点でDID変数はスペースで区切られたコンテナIDを複数もっています。こんな感じ e106b9fea6c8 20a5059fea048 llff6orfea0dkd
3. すべてのコンテナが終了するまでブロックします
docker wait $DID
docker wait
コマンドは与えられたコンテナIDが停止するまで処理をブロックします。全部停止すると各コンテナの最終ステータスコードを表示します。こんな感じ。
$ docker wait e106b9fea6c8 20a5059fea048 llff6orfea0dkd
0
1
0
cucumberはテスト結果をステータスコードで返すので、これだけでどのコンテナのテストが成功/失敗したのかがわかりますね。
詳細を見る
しかし、ステータスコードだけじゃ何が失敗したのかわかりません。
詳細を見るのはdocker logs コンテナID
です。
コンテナの出力を全部見れるので、cucumberの実行ログを見ることができます。今回の例にはないですが、ステータスコードが0でなければ、そのコンテナのログを見るような処理を書けば、何が失敗したのかすぐわかるので便利だと思います。
まとめ
プリミティブな例でしたが、Dockerを使った並列テスト実行のイメージは掴んでもらえたでしょうか?
今回はシェルスクリプトを使ったので沢山のことはできませんが、DockerはRemote APIがあるのでこれを使えばもっと高度なこともできるます。
というわけでbaleenというruby gemを作ってみました。今のバージョンではcucumberのテストを並列実行するだけですが、将来的には他の種類のテストも実行することができる、並列テスト実行ツールを目指して開発しています。
次の記事ではbaleenについて書きたいと思います。
Enjoy Docker!!