mixi engineer blog

*** 引っ越しました。最新の情報はこちら → https://medium.com/mixi-developers *** ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

Jenkins で任意のブランチをテストする

PHP よりは Xslate が好きな加藤和良です。プログラミング言語のなかでは Scala が好き です。

さて今回も

に引き続き開発者テストの話をします。

テストをリモートで実行したい

最近になって何度か「このブランチのテストを Jenkins で一度だけ実行してもらえませんか?」という相談をうけました。最初はなにか勘違いをされているのかと思い「いや Jenkins がやっている仕事はテストを実行しているだけで、テストは手元でも実行できますよ」と説明していたのですが、話を聞いていると「テストの実行が遅いので Jenkins のホストを使いたい」という意図だということに気づきました。 以前に何度か説明している通り mixi における「ブランチ」は開発版です。安定版にいれる前にテストをリモートのホストで実行したい、というのはそれほど珍しいニーズではなく、公開されている事例もいくつかあります。ここでは、そのいくつかを見た後に、mixi で採用した Jenkins を使った方法を紹介したいと思います。

TAP::Harness::Remote

TAP::Harness::Remote は rsync と ssh を使い、テストをリモートで実行するためのライブラリです。YAPC::Asia 2008 Tokyo の Everything but the secret sauce でも紹介されているので、ご存知の方も多いと思います。 TAP::Harness::Remote では ホストの設定を ~/.remote_test に記述し、prove の引数 --harness に TAP::Harness::Remote を指定するだけで

  • rsync によるファイルの転送
  • sshを使ったテストの実行

が裏側で実行されます。 YAPC のスライド には Hiveminder の開発における事例と、テストを実行するホストとして Amazon Elastic Compute Cloud を使う Test::Harness::Remote::EC2 も紹介されています。

remote rspec

同様の取り組みとして、クックパッド株式会社様の remote spec があります。

大江戸 RubyKaigi 01 での発表 によれば、Core i7 + メモリ 16G + SSD という高速なマシンを4台用意し、そこでテストを実行することで、全テストの実行速度を以前の 19.2 倍、50秒にまで短縮されているそうです。

残念ながら実装は公開されていませんが、資料からは rsync + ssh を使った TAP::Harness::Remote と似た様な設計になっていることが伺えます。

Try Server

Web アプリケーション以外の事例も見てみましょう。Mozilla には Try Server と呼ばれるリモートでテストを実行する仕組みがあります。

Web ブラウザのようなクロスプラットフォームなソフトウェアを開発する場合は、テストをリモートで実行する動機として「テストの実行が遅い」だけではなく「自分の手元にない環境での動作を確認したい」というのが加わります。Try Server に与えられる --platform 引数 からは、その一端が伺えると思います。

Try Server といままでの事例との、もうひとつの違いは通信方法です。Mozilla では、ソースコードの管理に Mercurial を使っています。Try Server との通信も、ワーキングコピー の rsync + ssh ではなく Mercurial レポジトリへの push で実現されています。

なお、Chromium にも同様の Try Sever という仕組みが存在します。

mixi の場合: それ Jenkins で出来るよ

mixi の場合、Jenkins をカスタマイズして、任意のブランチをテストする仕組みを作って使っています。 Jenkins では、通常ジョブごとにどこからチェックアウト (Git などの場合クローン) するかをあらかじめ指定します。しかしここでは、ジョブの「ソースコード管理システム」を「なし」に設定し、代わりに「ビルドのパラメータ化」という機能を使って外から与えられるようにします。

またここで「可能であれば並列してビルド」にもチェックを入れておくと、前回のビルドの完了を待たずに次のビルドが走るようになるので、待ち時間を減らすことが出来ます。 そして、実際のビルドプロセスがはじまってからチェックアウトを行います。パラメータ "branch" はシェルからは $branch として参照できるので、それを引数にシェルスクリプトを実行します。

checkout-branch svn://repos.example.com/foobar/branches/$branch

この checkout-branch は実際には svn switch を呼び出しています。これでブランチを切り替えてから、テストを走らせます。

ここまで設定すれば、以下のようなコマンド一発で、チェックアウトとテストを実行できるようになります。

% curl 'http://ci.example.com/job/try/buildWithParameters?branch=new-feature'
%

新しいビルドを作るのが GET なのはきれいじゃない気がしますが、社内サーバーなので見なかったことにしています。

なぜブランチ?

テストで実行できるコードを、ワーキングコピーの rsync +ssh ではなく、ブランチにしぼった理由は2つあります。

一つは安全性です。「リモートでテストが実行できるようにする」ことは「リモートで任意のコマンドが実行できる」ということと、ほぼ同じ意味です。なにかあった場合に、開発者の手元とテスト実行のためのマシン上に一時的に存在するものではなく、レポジトリ上にあるコードが追えたほうが安心できると思います。

もうひとつは運用上の理由です。mixi ではデータセンターにあるホストで root を持てるのは運用関係の部署の方に限られています。筆者は Jenkins のマシンや、そのほかのマシンにログイン出来るユーザーを増やせません。そのため、まずブランチを使う方法を試しはじめたところ、比較的問題なく動作しているので、そのまま運用を続けています。

まとめ

リモートでのテストの実行について

  • TAP::Harness::Remote (rsync + ssh) を使った事例
  • remote spec (rsync + ssh) を使った事例
  • ブラウザ開発における Try Server (クロスプラットフォーム, hg push)

に触れたのちに

  • Jenkins を使った実現方法
  • ワーキングコピーの rsync ではなくブランチを使う理由

を説明しました。それではまた。