Jenkins入門 ― Rubyプログラマー向け連載(4)

テストを分散しよう(Mac OS X&Linux)

2014年3月24日

テストの分散は、環境を分けたい場合や速度を上げたい場合に役立つ。Vagrantで複数マシンのテスト環境を構築し、Jenkinsから複数マシンにまたがるテストジョブを実行してみよう。また連載最終回として、お勧めの便利なプラグインを紹介。

山本 和久(Hatena::Diary
  • このエントリーをはてなブックマークに追加

 連載第3回「アプリケーションをデプロイしてみよう」では、Jenkinsからのさまざまな通知方法、およびテストの分割、Herokuへのデプロイを解説した。Jenkins以外についても多くのことを解説したが、いきなり全てを実践する必要はない。どのプロセスの優先順位が高いのか、プロジェクトメンバーで話し合って、着手する順番を決めていこう。

テストの分散とは?

 さて、今回は「テストの分散」の話である。「前回の連載で複数のジョブに分けたのが分散なのでは?」と気付いたあなたは鋭い。今回の「分散」とは、ジョブに分割することではなく、「複数マシンでのジョブの実行」を意味している。

 複数マシンでジョブを実行する理由は次の2つが挙げられる。

  • 環境を分けたい
  • 速度を上げたい

 ではそれぞれのパターンを解説しよう。

環境を分けたい場合

 例えばPostgreSQLなどのRDB(リレーショナルデータベース)やMongoDBなどのKVS(Key-Valueストア)へのアクセスを考えてみよう。複数のジョブで同じDBにアクセスしてしまうと、期待したタイミングで値の更新および内容の確認ができない。当然、実行するたびにテスト結果がおかしなものになってしまうだろう。

速度を上げたい場合

 単一のマシンで複数のジョブを動作させた場合、内容にもよるが、1つのジョブを単独で実行したときと比べて処理速度が遅くなる。並列性を高めすぎてビルドパイプライン全体の実行時間が遅くなってしまっては本末転倒である。こういった場合は複数のマシンを用意して分散処理を行う。

ノードについて

 Jenkinsにはこうした問題を解決するため、複数のマシンを操る仕組みが備わっている。「ノード」という概念だ(次の図)。

ノードの概念
ノードの概念

 Jenkinsを動かしているマシンがマスターとなり、他のマシンはスレーブとして動作する。一般的にスレーブにはSSHを利用して接続する。特筆すべき点は、スレーブにJenkinsをインストールする必要はないという点だ。SSH接続できるマシンにJavaランタイムさえ導入すれば、すぐさま複数のマシンでテストが実行できるようになる。

 それでは設定手順を見ていこう。

Vagrantでslaveの作成

 MacやLinuxなどのSSH接続ができるUNIX系マシンを持っている方は、「ノードの設定」まで読み飛ばしてもよい。1台しかPCを持っていない方は仮想環境を作って代用しよう。

 「Jenkinsの実験のためだけに仮想環境を構築するのは面倒だ……」という心の声が聞こえてきそうだが、最近では「Vagrant」(ベイグラント)という仮想環境を手軽に構築できる手段がある。今回はその方法でスレーブを作成してみよう。

VagrantとVirtualBoxのインストール

 Vagrantとは、仮想化ソフトウェアの「Oracle VM VirtualBox」を利用して、簡単に環境の構築を行うためのツールだ。まずは使っているマシンにVirtualBoxをインストールしよう。

VirtualBoxのダウンロード

 次のページにアクセスし、利用している環境に合ったVirtualBoxをダウンロードしてインストールしておこう。

VirtualBoxのダウンロードページ(英語)

Vagrantのダウンロード

 VirtualBoxと同じく次のページにアクセスし、利用している環境に合ったVagrantをダウンロードしてインストールしておこう。

Vagrantのダウンロードページ(英語)

slaveマシンの構築

 ここまでくればあとは簡単だ。次のコマンドを入力して、VirtualBox上にLinuxの「CentOS 6.5」を構築しよう。

Shell
$ mkdir vagrant 1
$ cd vagrant
$ vagrant box add centos https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box 2
$ vagrant init centos 3
$ vagrant up 4
仮想環境を構築しているところ
  • 1Vagrant用ディレクトリの作成。
  • 2ディスクイメージのダウンロード。
  • 3初期化。
  • 4起動。

Javaのインストール

 作成した仮想マシンにログインしてJavaをインストールしておこう。

Shell
$ vagrant ssh 1
[vagrant]$ sudo yum install java-1.7.0-openjdk.x86_64 -y 2
[vagrant]$ exit 3
Javaをインストールしているところ

Vagrant上のプロンプトは「[vagrant]$」で表現している。

  • 1仮想環境へのログイン。
  • 2Javaをインストールするためのコマンド。
  • 3仮想環境からログアウト。

ノードの設定

 それではJavaがインストールされたslave用マシンにJenkinsから接続してみよう。

SSHユーザー名とパスワードの設定

 Jenkinsの左のメニューから[Jenkinsの管理]-[認証情報の管理]を選択する。

初回入力時は次のような画面が表示されるはずだ。
SSHユーザー名とパスワードを設定しているところ
SSHユーザー名とパスワードを設定しているところ

この画面のように、[認証情報の追加]欄で「ユーザー名とパスワード」を選択する。
これにより表示される設定画面で、下記の内容を入力し、最後に[保存]ボタンを押す。

  • [スコープ]: グローバル(もしくは「Global」)
  • [ユーザー名]: vagrant *1
  • [パスワード]: vagrant *1
  • *1 Vagrantで構築したサーバー以外に接続するときは、そのサーバーに設定されているユーザー名とパスワードを入力すること。

ノードの追加

 左のメニューから[Jenkinsの管理]-[ノードの管理]-[新規ノード作成]を選択しよう。

 次に、これにより表示される次の設定画面で[ノード名]に「slave1」と入力し、[ダムスレーブ]をチェックして[OK]ボタンを押す。

新規ノード作成

 次に表示される設定画面では、下記のように入力しよう。

ノードの設定を行っているところ

この画面では、下記のように入力しよう。

  • [ノード名]: slave1
  • [同時ビルド数]: 2 1
  • [リモートFSルート]: /home/vagrant/jenkins 2
  • [起動方法]: SSH経由でUnixマシンのスレーブエージェントを起動
  • [ホスト]: localhost 3
  • [認証情報]: vagrant 4
  • [ポート]: 2222 5
  • 1設定したスレーブマシンで同時にいくつのジョブを実行できるかの設定を行う。
  • 2Jenkinsの作業領域として利用するディレクトリを設定する。
  • 3スレーブとして使用するマシンの名前、もしくはIPアドレスを入力する。Vagrantの場合は「localhost」を指定しよう。
  • 4「SSHユーザー名とパスワードの設定」で作成したユーザー名を選択する。
  • 5[高度な設定]ボタンを押すと入力できる(この画面例はすでに押した後なので、このボタンは表示されていない)。Vagrantの場合はSSHポートとして「2222」が割り当てられているのでその値を入力しよう。

 入力できたら[保存]ボタンを押す。

 設定が正しければ、スレーブに接続されるはずだ(次の画面のようにして確認できる)。

slave1に接続されているのが確認できる
slave1に接続されているのが確認できる

Clone Workspace SCM Plugin

ソースの取得をどこから行うか?

 スレーブの準備は整った。ここで連載第3回「アプリケーションをデプロイしてみよう(Mac OS X&Linux)」で作成したビルドパイプラインを思い出してみてほしい。

 次の順番でジョブが実行されていたはずだ。

  • circle_unit_test
  • circle_integration_test
  • circle_deploy

 今までは1つのマシン上で動作させていたため、テスト前にローカルのディレクトリからgit cloneしてソースを取得することができた。

 しかし次の図に示すように、slave1は別マシンであるため、最新のソースをローカルディスクから取得できなくなってしまった。

複数のマシンでの、ジョブの実行: マシンが異なるためにgit cloneできない!(Build Pipeline View)

 この解決方法としては、circle_integration_testの実行前にcircle_unit_testのディレクトリの内容を全てコピーしてしまえばよい。そのためのプラグインとして「Clone Workspace SCM」が用意されている。

プラグインのインストール

 プラグインのインストールは連載第2回「Jenkinsで小さなテストを実行してみよう(Mac OS X&Linux)」で解説している。画面左のメニューから[Jenkinsの管理]-[プラグインの管理]と進んで、「Clone Workspace SCM」をインストールしよう。

Clone Workspace SCMを検索したところ

ジョブの設定の変更

 それではジョブの設定を変更していこう。

1. circle_unit_test

 circle_unit_testジョブの[設定]を開き、[ビルド後の処理の追加]欄で「Archive for Clone Workspace SCM」を選択する。次の画面のような設定項目が表示されるが、何も入力せず[保存]ボタンを押そう。

ビルド後の処理でArchive for Clone Workspace SCMを設定しているところ
ビルド後の処理でArchive for Clone Workspace SCMを設定しているところ

 これでビルド後に現在のワークスペース(作業領域)の内容が固められる。

2. circle_integration_test

 circle_integration_testジョブの[設定]ページで[ソースコード管理]の項目を[Git]から[Clone Workspace]に変更する。

ソースコード管理の設定を変更しているところ

 これで上位のジョブのワークスペースを引き継ぐことができる。

Join Plugin

GitコミットIDを引き継げない

 ご存じの通り、HerokuへのデプロイはGitリポジトリのpushを利用して行う。そのためデプロイ用のジョブ「circle_deploy」はGitのリポジトリでないといけない。

 ところが、今回、「circle_integration_test」のソースコード管理をGitからClone Workspaceにしてしまったことで「Parameterized Trigger」を利用してGitのコミットIDを引き継ぐことができなくなってしまった(次の図)。

より複雑なビルドパイプラインの構築(Build Pipeline View)

 この問題を回避するために「Join Plugin」を利用する。このプラグインを利用すれば、下位に指定したジョブが全て成功したときのみ、特定のジョブを起動できる(次の図)。

Join Pluginの動作の概念図
Join Pluginの動作の概念図

 つまり「circle_unit_test」を起点として「circle_integration_test」を実行し、成功すれば、GitのコミットIDを引き継ぎつつ「circle_deploy」を起動する設定が可能だ。

プラグインのインストール

 画面左のメニューから[Jenkinsの管理]-[プラグインの管理]と進んで、「Join Plugin」をインストールしよう。

Join Pluginを検索したところ
Join Pluginを検索したところ

ジョブの設定の変更

 それではジョブの設定を変更していこう。

1. circle_unit_test

 circle_unit_testジョブの[設定]で、[ビルド後の処理の追加]欄から「Join Trigger」を選択する。これに表示される設定画面で、次のように設定する。

Join Triggerを設定しているところ
Join Triggerを設定しているところ
  • 1[Run post-build actions at join]にチェックを付ける。
  • 2[Trigger parameterized build on other projects]にチェックを付ける。
  • 3[Projects to build]に最終的に実行したいジョブとして「circle_deploy」を入力する。
  • 4[Trigger when build is]は「Stable」を選択する。この設定により、テストが成功したときのみ「circle_deploy」が実行されるようになる。
  • 5[Add Parameters]をクリックして「Pass-through Git Commit that was built」を選択する。これで「circle_deploy」にコミットIDが引き継がれる。
2. circle_integration_test

 circle_integration_testジョブの[設定]で、[ビルド後の処理]に設定されている「Trigger parameterized build on other projects」を削除する。

ノードを指定してテストを実行

 スレーブの準備は整った。さっそくノードを指定してテストを実行してみよう。

実行するノードの指定

 [circle_integration_test]ジョブの[設定]を開き、次の画面の通りに[実行するノードを制限]にチェックを入れ、[ラベル式]欄に先ほど作成したノード名である「slave1」を設定する。

ラベル式にノード名を設定しているところ
ラベル式にノード名を設定しているところ

ビルドパイプラインの実行

 ノードの設定が完了したら、ビルドパイプラインを実行してみよう。

ビルドパイプラインの実行(Build Pipeline View)

 順番に実行されることが確認できるはずだ。

 なお、初回実行時はslave1でRVMおよびRubyのインストール、bundle installコマンドが実行される。少し時間がかかるので注意してほしい。

うまくいかない時は?

 環境によってはテスト実行中にエラーが発生することがある。

bundle install時に失敗する

 必要なモジュールが入っていないことが考えられる。Vagrantで作成したスレーブをノードとして指定しているのであれば、次のコマンドでPostgreSQLとSQLiteをインストールしよう。

Shell
$ vagrant ssh
[vagrant]$ sudo yum install  postgresql-devel sqlite-devel -y
[vagrant]$ exit
必要なモジュールをインストールしているところ

rake db:migrateが失敗する

 環境によっては次のようなエラーが発生することがある。

Shell
+ bundle exec rake db:migrate
rake aborted!
Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes.
テスト実行時のエラー

 この場合はRailsプロジェクトのGemfileを修正しよう。

Gemfile
……略……
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
gem 'therubyracer', platforms: :ruby この行のコメントを外す
……略……
テスト実行時のエラー

 修正が完了したら、bundle updateを行い、Gitリポジトリにコミットしておこう。

より多くのジョブを処理したいときは?

ジョブとノードを増やせばよい

 今回はユニットテストとデプロイをmasterで、インテグレーションテストをslave1で実行した。今後、プロジェクトが大きくなりテストが増えていった場合は、テストを分割してジョブを作成し、「Parameterized Trigger」と「Join Plugin」でまとめていこう。

マシンを増やすと、もっとハッピー

 1台のマシンでさばききれなくなったときは、テスト用マシンを追加して新たなノードを作成し、ジョブを割り振ればよい。

 ん? 「会社が新しいテスト用マシンの購入を許可してくれない」だって!?

 そんなときは、一日中文句も言わずひたすらテストを実行してくれるバイト君を雇う費用と、新しいPCを購入する費用のどっちがお得であるか、上司によく考えてもらおう。決して「余ったPCをテストに使おう」なんて思ってはいけない。テストに使うマシンは速ければ速いほど良い。「人件費」と「マシンの価格」を比較することが重要だ。検討を祈る!

便利なプラグインたち

 さて、連載も終わりに近づいてきた。最後に筆者が使ってみて便利に感じたプラグインを紹介して締めくくろうと思う。

AnsiColor Plugin

 AnsiColor Pluginは「コンソール出力」に色を付けてくれるプラグインだ。色分けされるのでテストの実行結果が見やすくなる。

色付きでテストを出力した結果
色付きでテストを出力した結果

 ちなみにRSpecの出力結果がうまく色付きで出力されない人は、spec_helper.rbファイルを次のように修正しよう。

Ruby
RSpec.configure do |config|
  ……略……
  config.tty = true   # 追加する
end
spec_helperに設定を追加する(spec/spec_helper.rb)

Ci Skip Plugin

 テストコードが大規模になってくると、コメントの編集や軽微な設定ファイルの修正で自動テストを動かしたくないことがある。そんなときはCi Skip Pluginを利用しよう。

 Gitのコメントに「[ci skip]」と書くことで、自動ビルドが停止するようになる。

Green Balls

 Jenkinsにおいてビルドに成功したジョブは、なぜか「青い玉」で表現される。これを「緑の玉」に変更してくれるのがGreen Ballsだ。「RSpecの出力結果と同じ色じゃなきゃいやだ!」という人はすぐに導入しよう。

Green Ballsを導入すると玉が緑色になる

Configuration Slicing plugin

 テストの分散化を行い、似たようなジョブが増えてくると、1つずつ設定を変更するのが面倒になることがある。そんなときはConfiguration Slicing pluginを利用しよう。インストールすると、[Jenkinsの管理]-[Configuration Slicing]で全てのジョブを一括して編集できる。ジョブのシェルスクリプトをまとめて変更したいというときに有効だ。

Xvfb Plugin

 Capybara-webkitを利用してインテグレーションテストを行う際は、GUIを立ち上げておく必要がある。しかしxvfbコマンドで仮想ディスプレイを起動しておけば、GUIをインストールしていないレンタルサーバーでもテストを実行できる。

 Xvfb Pluginを利用すれば、ジョブの実行前にxvfbコマンドで仮想ディスプレイを起動することが可能だ。xvfbコマンドの場所は[Jenkinsの管理]-[システムの設定]-[Xvfb installation]から変更できる。Mac OS XとLinuxがノードに混在する場合などは、コマンドのパスが同じになるようにシンボリックリンクを張って調整しよう。

最後に

 全4回にわたってJenkinsの魅力について解説してきたが、いかがだっただろうか? より実践的な解説にするため、テストフレームワークや、Herokuへのデプロイ方法、Vagrantによる仮想環境の構築など、直接、Jenkinsに関係ない話も書かせていただいた。この情報がそれぞれの分野への取っ掛かりになってくれれば幸いだ。

 ここ数年でJenkinsのアプリケーションとしての完成度は非常に高くなっており、もはや起動さえしてしまえば誰でも使えるものになってきている。テストだけではなく「高機能なcron」として活用すれば、非エンジニアをもハッピーにできる可能性を秘めていると思う。

 さて、次はあなたが型にはまらないJenkinsの活用方法を考える番だ。Jenkinsによる自動化の恩恵を多くの人に広めていこう!

1. Jenkinsを使ってみよう(Mac OS X&Linux)

Rubyプログラマーに向けて、Jenkinsの使い方を基礎から説明する連載スタート。今回は、Jenkinsの概要と、Mac OS X&Linux環境へのインストール手順、簡単なジョブの登録方法を解説。

2. Jenkinsで小さなテストを実行してみよう(Mac OS X&Linux)

作成したテストをJenkinsを使って自動実行して、開発スピードを飛躍的に向上させよう。また、Rubyの標準的なテストフレームワークのRSpecと、最新のインテグレーションテスト環境であるTurnipを使ったテストの書き方も解説。

3. アプリケーションをデプロイしてみよう(Mac OS X&Linux)

継続的インテグレーションの手順のうち、デプロイに焦点を当てて、テストの実行から、GitによるHeroku環境へのデプロイまでを自動化する方法を解説。Mac向けのGrowlを使って実行結果を通知する方法も説明。

4. 【現在、表示中】≫ テストを分散しよう(Mac OS X&Linux)

テストの分散は、環境を分けたい場合や速度を上げたい場合に役立つ。Vagrantで複数マシンのテスト環境を構築し、Jenkinsから複数マシンにまたがるテストジョブを実行してみよう。また連載最終回として、お勧めの便利なプラグインを紹介。

Twitterでつぶやこう!

Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています。

プラチナレベル

  • 日本マイクロソフト株式会社
  • 株式会社gloops

ゴールドレベル

  • グレープシティ株式会社
  • 株式会社セカンドファクトリー