Jenkinsの使い勝手をよくするための見直し6点
今回の課題
こんにちは植木和樹です。7月にserverspecを使ったChefの自動テストのエントリを書きました。
【AWS】JenkinsとserverspecでChefのテストを自動化する
このエントリは初めてJenkinsを触った時に書いたので、いろいろと流儀がわかっていませんでした。その後弊社にJenkinsマイスターの渡辺修司さんが入社したということで、Jenkinsの設定について見てもらいました。その時に次の6点を見直すよう指摘がありました。
- ジョブは意味ある単位で1つにまとめるべし
- ジョブで実行するシェルスクリプトもgitから取得すべし
- EC2の起動に失敗したら後続処理を停止させるべし
- serverspecの実行結果はJUnit(XML)形式で出力すべし
- 実行結果のXMLをJenkinsで読み込んで統計グラフを出力すべし
- 定時実行でなくgit push hookを入れるべし
なるほど参考になります。それでは1つずつ見直していきたいと思います。
ジョブは意味ある単位で1つにまとめるべし
いまのJenkinsの設定では「git pull」「EC2起動」「chef適用 + serverspec実行」「EC2終了」をそれぞれ別のジョブとして設定し、先行ジョブ完了後に「ビルド後の処理」として後続ジョブをキックしています。
しかしこの構成だとジョブが無闇に増えてしまい管理しづらくなってしまうそうです。それと「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しておく必要があります。
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と入力して保存します。
ビルドを実行した後にジョブのページを開くと最近のテスト件数と成功数・失敗数がグラフで表示されるようになりました。
定時実行でなくgit push hookを入れるべし
gitリポジトリにpushされた時にJenkinsのビルドがキックされるようにフックを設定します。
これまではJenkinsにgitリポジトリのURLとブランチ名を指定していましたが、gitフックを使うとgitリポジトリからJenkinsに対してgitリポジトリのURLと取得させるリビジョンを渡すことができます。つまりJenkins側ではリポジトリの設定が不要になります。
次にgitリポジトリ側を設定します。クラスメソッドではStashを使用しているため、リポジトリの[Settings]-[Hooks]で"Stash Post-Receive Webhook to Jenkins"フックを有効にします。
設定画面でJenkinsのビルド実行URLと、Jenkinsに伝えるgitリポジトリのURLを設定します。
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すると、「ビルド履歴」のところが待機状態に変わります。しばらくしてジョブが実行されれば設定完了です。
まとめ
今回はJenkinsの使い勝手をよくするために、いろいろと設定を見直してみました。特にこれまで定期的にEC2インスタンスを立ち上げてテストを実行していた箇所をpushフックを使うようにしたため、無駄なインスタンス起動がなくなりAWSのインスタンス課金が減ってコスト削減にもなっています。
Jenkinsをいろいろ触っていると本当によくできたツールだなと思います。設定画面は一見シンプルですが、他システムとの連携やレポート機能、セキュリティやバックアップといったメンテナンス機能など、プラグインも含めてやりたいことができる仕組みが一通りそろっていて便利です。
今回の見直しでは手をつけませんでしたが、Jenkinsにはテストに失敗した時に通知したり、チケット管理システムと連携したりする機能もあるそうです。今後はその辺の見直しも行ってみたいですね。