Vagrant + Chef Solo + serverspec + Jenkins でサーバー構築を CI

Jenkins おじさんと戯れること半日、うまくいったので備忘録を残しておく。

やりたかったのは Chef で構築したサーバーを Jenkins で CI する、というもの。このときサーバーはテストが終わる度に破棄して、テスト開始時に再度真っ新な状態から立ち上げたい。(こういうサーバーを壊して作ってというテストはなんという名前で呼ばれるのだろう?)

という構成になっている。ひとまず Jenkins や Vagrant はローカルの OSX に入れている。

レポジトリ

プロビジョニング用レポジトリのディレクトリは、knife solo で作ったもの。Vagrantfile もその中に入れる。

% bundle exec knife solo init jenkins-vagrant-test
% cd jenkins-vagrant-test
% vagrant init centos

この辺は Chef Solo + Vagrant を使う際のいつもの様子。

Gemfile

レポジトリ内に Gemfile を作って CI に必要な gem を列挙しておく。

source 'https://rubygems.org'

gem "knife-solo", "~> 0.3.0.pre3"
gem 'serverspec'
gem 'rake'
gem 'ci_reporter'

RSpec の出力を Jenkins 用にカスタマイズするため ci_reporter も入れておこう。

serverspec でテストを書いてレシピを書く。

特に変わったことはしていない。いつも通り

ci_reporter で出力を出すためには serverspec の Rakefile

require 'ci/reporter/rake/rspec'

を追記しとく。これで rake ci:setup:rspec spec で Jenkins が読める XML でのレポートが、テスト実行時に spec/reports/*.xml に吐き出される。

SSH 周りをどうするか

細かい話なのだけど、この構成で CI を回そうとするときに問題になるのが SSH 周りの設定。knife-solo も serverspec も Vagrant で立ち上げた仮想サーバーに ssh してごにょごにょするツールですが全部自動化しようとすると vagrant up したサーバーをどうやって名前解決するかというところで困る。

幸い

  • Vagrant には vagrant ssh-configssh 設定を出力するオプションがあり
  • knife solo には ssh 設定ファイルを指定するオプション (-F) があり
  • serverspec は serverspec 本体を弄らなくても Net::SSH のオプションを触れる構成になっている

ので、vagrant ssh-config の出力をファイルに吐き出してそれを両者に読み込ませるようにすれば、課題は解決できる。つまり、Jenkins のビルド設定のシェルスクリプトは以下になる。

# 仮想サーバー起動
vagrant up

# ssh 設定を出力
vagrant ssh-config --host=jenkingrant > vagrant-ssh.conf

# gem 入れる
bundle

# bootstrap = prepare + cook : Chef入れてクックブック適用
bundle exec knife solo bootstrap jenkingrant -F vagrant-ssh.conf

# serverspec でテスト実行
bundle exec rake ci:setup:rspec spec

# 設定ファイル削除
rm -f vagrant-ssh.conf

# 仮想サーバー破棄
vagrant destroy -f

serverspec の ssh 設定周りは spec/spec_helper.rb で Net::SSH::Config.for を読んでるところを

options = Net::SSH::Config.for(c.host, files=["vagrant-ssh.conf"])

とする。これで引数に与えたファイルも ssh の設定として使われる。

Jenkins 周りの設定

Git プラグインを入れて、ビルドの際に github からコードを取ってくるように設定し、ビルドの設定に先のシェルを入れている。特に変わったことはしていない。

これでビルドを実行すると Github のコードが pull されて、vagrant up で仮想サーバーが立ち上がり、Bundler で必要な gem が入って、knife solo でノードの Chef が最新になり、クックブックが適用されたノードに serverspec が走り、テストに問題なければサーバーが破棄される。この一連のテストは仮想サーバーの上げ下げそのほかがあって時間がかかるので CI に任せるのにうってつけ。

普段クックブックを調整してその場でテストを回すのにはあらかじめ仮想サーバーを立てておいたものに、差分のクックブックを適用して serverspec でテスト、サーバーは破棄しない・・・みたいなのを Guard や Grunt なんかを使ってテスト保存のたびカジュアルに回す。一方、調整が終わったクックブックは他のクックブック含め真っ新なサーバーに適用してがっちりインテグレーションテストしてみないと予想外のことが起こるかもしれない。そのための CI をしたかった、というわけでした。

サーバー構築も継続的インテグレーションする時代です。