Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Jenkins入門【2.0対応】 - オープンソースCIツール(4)

Jenkins入門【2.0対応】 - オープンソースCIツール(4)

Jenkins+Vagrantでテストを分散しよう

2016年6月29日 改訂(初版:2014/03/24)

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

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

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

テストの分散とは?

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

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

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

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

環境を分けたい場合

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

速度を上げたい場合

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

ノードについて

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

図1 ノードの概念

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

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

Vagrantでslaveの作成

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

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

VagrantとVirtualBoxのインストール

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

VirtualBoxのダウンロード

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

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

Vagrantのダウンロード

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

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

slaveマシンの構築

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

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

Javaのインストール

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

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

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

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

ノードの設定

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

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

 Jenkinsの左のメニューから[認証情報]-[System]を選択し、中央の[グローバルドメイン]をクリックする。次に表示されるページで、左側のメニューから[認証情報の追加]をクリックすると次のような画面が表示されるはずだ。

図4 SSHユーザー名とパスワードを設定しているところ

図4 SSHユーザー名とパスワードを設定しているところ

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

[スコープ]: グローバル(もしくは「Global」)
[ユーザー名]*1 vagrant
[パスワード]*1 vagrant

  • *1 Vagrantで構築したサーバー以外に接続するときは、そのサーバーに設定されているユーザー名とパスワードを入力すること。

ノードの追加

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

これにより表示される設定画面で[ノード名]に「slave1」と入力し、[Permanent Agent]をチェックして[OK]ボタンを押す(図5)。

図5 新規ノード作成
図5 新規ノード作成

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

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

この画面では、下記のように入力しよう。
[ノード名]: slave1
[同時ビルド数]1 2
[リモートFSルート]2 /home/vagrant/jenkins
[起動方法]: SSH経由でUNIXマシンのslaveエージェントを起動
[ホスト]3 localhost
[認証情報]4 vagrant
[ポート]5 2222

  • 1設定したslaveマシンで同時にいくつのジョブを実行できるかの設定を行う。
  • 2Jenkinsの作業領域として利用するディレクトリを設定する。
  • 3slaveとして使用するマシンの名前、もしくはIPアドレスを入力する。Vagrantの場合は「localhost」を指定しよう。
  • 4「SSHユーザー名とパスワードの設定」で作成したユーザー名を選択する。
  • 5[高度な設定]ボタンを押すと入力できる(この画面例はすでに押した後なので、このボタンは表示されていない)。Vagrantの場合はSSHポートとして「2222」が割り当てられているのでその値を入力しよう。

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

 設定が正しければ、slaveに接続されるはずだ(次の画面のように左側に「slave1」が表示されるのが確認できる)。

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

Clone Workspace SCM Plugin

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

 slaveの準備は整った。ここで連載第3回「Jenkinsでアプリケーションをデプロイしてみよう」で作成したビルドパイプラインを思い出してみてほしい。

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

  1. circle_unit_test
  2. circle_integration_test
  3. circle_deploy

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

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

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

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

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

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

図9 Clone Workspace SCMを検索したところ

ジョブの設定の変更

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

1. circle_unit_test

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

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

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

2. circle_integration_test

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

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

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

Join Plugin

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

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

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

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

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

図13 Join Pluginの動作の概念図

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

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

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

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

ジョブの設定の変更

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

1. circle_unit_test

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

図15 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」を削除する。

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

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

実行するノードの指定

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

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

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

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

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

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

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

うまくいかない時は?

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

bundle install時に失敗する

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

Bash
$ vagrant ssh
[vagrant]$ sudo yum install  postgresql-devel sqlite-devel -y
[vagrant]$ exit
リスト3 必要なライブラリをインストールしているところ

rake db:migrateが失敗する

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

Bash
+ 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.
リスト4 テスト実行時のエラー

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

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

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

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

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

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

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

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

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

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

便利なプラグインたち

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

AnsiColor Plugin

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

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

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

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

Ci Skip Plugin

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

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

Green Balls

 Jenkinsにおいてビルドに成功したジョブは、なぜか「青い玉」で表現される。これを「緑の玉」に変更してくれるのがGreen Balls Pluginだ。「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とLinuxがノードに混在する場合などは、コマンドのパスが同じになるようにシンボリックリンクを張って調整しよう。

最後に

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

 次回はいよいよ連載最終回として、Jenkins 2.0の新機能について解説とする。もちろん、ここまでの解説で基本的なことはできるはずなので、ぜひ今から実践に踏み出してほしい。

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

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

Jenkins入門【2.0対応】 - オープンソースCIツール(4)
1. Jenkinsをインストールして使ってみよう[Mac/Linux/Windows]

継続的インテグレーションツール「Jenkins」の使い方を基礎から解説する連載がスタート。初回は、Jenkinsの概要とインストール手順、簡単なジョブの登録方法を説明する。

Jenkins入門【2.0対応】 - オープンソースCIツール(4)
2. Jenkinsでテストを実行してみよう+Rubyテストの基礎(RSpec&Turnip使用)

Jenkinsを使って小さなテストを自動実行して、開発スピードを飛躍的に向上させよう。また、MacでのRuby/Rails環境の構築方法から、テストフレームワーク「RSpec」とインテグレーションテスト環境「Turnip」を使ったテストの書き方までを解説する。

Jenkins入門【2.0対応】 - オープンソースCIツール(4)
3. Jenkinsでアプリケーションをデプロイしてみよう

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

Jenkins入門【2.0対応】 - オープンソースCIツール(4)
4. 【現在、表示中】≫ Jenkins+Vagrantでテストを分散しよう

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

Jenkins入門【2.0対応】 - オープンソースCIツール(4)
5. Jenkins 2の新機能「Pipeline」を使ってみよう

何をやっているか分からない「Jenkinsおじさん」の作業を見える化しよう。Jenkins 2に新搭載されたPipelineを使えばパイプラインをコードで記述できるようになる。その基本的な使い方を解説。

サイトからのお知らせ

Twitterでつぶやこう!