注目の記事

Jenkinsの使い勝手をよくするための見直し6点

2013.09.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今回の課題

こんにちは植木和樹です。7月にserverspecを使ったChefの自動テストのエントリを書きました。

【AWS】JenkinsとserverspecでChefのテストを自動化する

このエントリは初めてJenkinsを触った時に書いたので、いろいろと流儀がわかっていませんでした。その後弊社にJenkinsマイスターの渡辺修司さんが入社したということで、Jenkinsの設定について見てもらいました。その時に次の6点を見直すよう指摘がありました。

  1. ジョブは意味ある単位で1つにまとめるべし
  2. ジョブで実行するシェルスクリプトもgitから取得すべし
  3. EC2の起動に失敗したら後続処理を停止させるべし
  4. serverspecの実行結果はJUnit(XML)形式で出力すべし
  5. 実行結果のXMLをJenkinsで読み込んで統計グラフを出力すべし
  6. 定時実行でなくgit push hookを入れるべし

なるほど参考になります。それでは1つずつ見直していきたいと思います。

ジョブは意味ある単位で1つにまとめるべし

いまのJenkinsの設定では「git pull」「EC2起動」「chef適用 + serverspec実行」「EC2終了」をそれぞれ別のジョブとして設定し、先行ジョブ完了後に「ビルド後の処理」として後続ジョブをキックしています。

20130914_jenkins_001

しかしこの構成だとジョブが無闇に増えてしまい管理しづらくなってしまうそうです。それと「git pull」というのはそれ自体に意味のあるジョブの単位ではありません。ジョブは意味ある単位にまとめ、その中のひとくくりの処理は「ビルド手順」という単位で分けるのが良いようです。

ということで、これまで4つに分かれていたジョブを1つにまとめることにします。変更後の内容は次のシェルスクリプトの見直しとあわせて紹介します。

ジョブで実行するシェルスクリプトもgitから取得すべし

これまではJenkinsの[ビルド]-[シェルの実行]のところにシェルスクリプトを直接記述していたわけですが、このままだとこのシェルスクリプト自体の変更が記録できません。また修正するたびに毎回Jenkinsの画面をブラウザで開くのも面倒です。

そこで、テキストエリアに書いていたシェルスクリプトをそのままファイルに保存し、chefやserverspecと同じgitリポジトリに保存します。Jenkinsではこのシェルスクリプトを実行するだけコマンドを記述します。

$ ls
README.md       cfn             node.json       site-cookbooks  spec
Rakefile        jenkins-shell   nodes           solo.rb

$ ls -1 jenkins-shell/
0101_serverspec_run-instance.sh
0102_serverspec_bootstrap.sh
0103_serverspec_terminate-instance.sh

シェルスクリプトをJenkinsから実行するにあたって注意点が2つあります。これまでは実行したシェルスクリプトがそのまま「コンソール出力」に表示されていました。これをシェルスクリプトファイルを実行するだけにすると「シェルスクリプトの呼び出した処理」だけが出力されます。これだとシェルスクリプト内部でどこまで実行されたかが分からず、あとから結果を見なおした際に原因が分からず困ります。そこで-xオプションをつけてシェルの実行経過も出力されるようにしておきましょう。

2つ目はJenkinsが設定する環境変数をシェルスクリプト内で扱う場合です。例えばワークスペースのディレクトリパスを保持する$WORKSPACEを使う場合、そのままではシェルスクリプト内で参照できません。使う環境変数は[シェルの実行]のテキストエリアでexport WORKSPACEしておく必要があります。

20130914_jenkins_002

EC2の起動に失敗したら後続処理を停止させるべし

Jenkinsは呼び出したシェルスクリプトが失敗すると、後続のシェルスクリプトは実行せずジョブ自体は失敗にします。ただEC2が問題なく起動したかどうかの処理はシェルからみると正常にコマンドが終了しているため、起動に失敗した時には非ゼロを返すようにします。

STATUS=0
(EC2起動処理)
if [ (EC2起動失敗) ]; then
  STATUS=1
fi
exit ${STATUS}

serverspecの実行結果はJUnit(XML)形式で出力すべし

JenkinsはJUnitが出力するテスト結果が記述されたXMLを読み込み、整形して出力する機能を標準で持っています。ところがserverspec(RSpec)は標準でJUnit形式のXMLを出力することができません。そこで次のページで紹介されているrubyスクリプトを読み込んでJUnit形式で出力できるようにします。

RSpec JUnit Formatter for Jenkins

このページにあるjunit.rbをRakefileと同じディレクトリに保存します。そしてserverspecを実行しているシェルスクリプトを修正し、rakeにrspecのコマンドオプションを指定します。

jenkins-shell/0102_serverspec_bootstrap.sh

rake SPEC_OPTS="--require ./junit.rb --format JUnit --out results.xml"

junit.rbではrequire 'builder'しているため、Jenkinsを動かすEC2にgemをインストールしてください。

$ sudo gem install builder --no-ri --no-rdoc

ジョブを実行するとRakefileと同じディレクトリにresults.xmlというファイルが出力されます。次にジョブ終了後にこのXMLをJekinsで読み込む設定をしましょう。

実行結果のXMLをJenkinsで読み込んで統計グラフを出力すべし

Jenkinsの設定は簡単です。[ビルド後の処理]で"JUnitテスト結果の集計"を選択し、[テスト結果XML]にresults.xmlと入力して保存します。

20130914_jenkins_007

ビルドを実行した後にジョブのページを開くと最近のテスト件数と成功数・失敗数がグラフで表示されるようになりました。

20130914_jenkins_003

定時実行でなくgit push hookを入れるべし

gitリポジトリにpushされた時にJenkinsのビルドがキックされるようにフックを設定します。

これまではJenkinsにgitリポジトリのURLとブランチ名を指定していましたが、gitフックを使うとgitリポジトリからJenkinsに対してgitリポジトリのURLと取得させるリビジョンを渡すことができます。つまりJenkins側ではリポジトリの設定が不要になります。

20130914_jenkins_006

次にgitリポジトリ側を設定します。クラスメソッドではStashを使用しているため、リポジトリの[Settings]-[Hooks]で"Stash Post-Receive Webhook to Jenkins"フックを有効にします。

20130914_jenkins_004

設定画面でJenkinsのビルド実行URLと、Jenkinsに伝えるgitリポジトリのURLを設定します。

20130914_jenkins_005

Jenkins URL

http://jenkins.example.com:8080/job/01_serverspec/build

Git Repo URL

ssh://git@gitserver.example.com/path/ops-cookbooks.git

Jenkinsの画面を開きながらgitリポジトリにpushすると、「ビルド履歴」のところが待機状態に変わります。しばらくしてジョブが実行されれば設定完了です。

20130914_jenkins_008

まとめ

今回はJenkinsの使い勝手をよくするために、いろいろと設定を見直してみました。特にこれまで定期的にEC2インスタンスを立ち上げてテストを実行していた箇所をpushフックを使うようにしたため、無駄なインスタンス起動がなくなりAWSのインスタンス課金が減ってコスト削減にもなっています。

Jenkinsをいろいろ触っていると本当によくできたツールだなと思います。設定画面は一見シンプルですが、他システムとの連携やレポート機能、セキュリティやバックアップといったメンテナンス機能など、プラグインも含めてやりたいことができる仕組みが一通りそろっていて便利です。

今回の見直しでは手をつけませんでしたが、Jenkinsにはテストに失敗した時に通知したり、チケット管理システムと連携したりする機能もあるそうです。今後はその辺の見直しも行ってみたいですね。