Vagrant + Chef Solo + serverspec + Jenkins でサーバー構築を CI
Jenkins おじさんと戯れること半日、うまくいったので備忘録を残しておく。
やりたかったのは Chef で構築したサーバーを Jenkins で CI する、というもの。このときサーバーはテストが終わる度に破棄して、テスト開始時に再度真っ新な状態から立ち上げたい。(こういうサーバーを壊して作ってというテストはなんという名前で呼ばれるのだろう?)
- 仮想サーバーを破棄/作成をプログラマブルにやるのはもちろん Vagrant
- プロビジョニングは Chef
- Chef の環境を整えるのに knife-solo 0.3.0.pre3
- テストは serverspec
- コードは Github に上げる (https://github.com/naoya/jenkins-vagrant-test)
- CI は Jenkins
という構成になっている。ひとまず 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 でテストを書いてレシピを書く。
特に変わったことはしていない。いつも通り
- serverspec は https://github.com/mizzy/serverspec のドキュメントなどを
- Chef Solo は 入門Chef Solo - Infrastructure as Code この辺がオススメですコポォ
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-config
で ssh 設定を出力するオプションがあり - 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 をしたかった、というわけでした。
サーバー構築も継続的インテグレーションする時代です。