Hatena::ブログ(Diary)

naoyaのはてなダイアリー

May 20, 2013

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 でテストを書いてレシピを書く。

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

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 からコードを取ってくるように設定し、ビルドの設定に先のシェルを入れている。特に変わったことはしていない。

f:id:naoya:20130520215201p:image:w480

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

f:id:naoya:20130520215649p:image:w480

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

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

tomotaka-itotomotaka-ito 2013/09/20 18:26 こんにちは、いつも参考にさせて頂いております。
自分もどうようにVagrantをJenkinsと併せて使ったCI環境を構築しようと思っていろいろ試してみたのですが、
naoyaさんはこちらのエントリにある内容はMacの上で実行されていらっしゃるのでしょうか?
自分でもいろいろやってみたのですが仮想化されたLinuxサーバ上ではなかなかうまくVagrantが動いてくれなかったのでご質問です....

naoyanaoya 2013/09/20 18:31 Mac です。

Vagrant、というか、Virtualbox が仮想環境で動かないかもですね

tomotaka-itotomotaka-ito 2013/09/20 18:36 さっそくコメントいただきありがとうございます^^
なるほど、Macなのですね。
VirtualBoxはXenで仮想化されたLinuxの上では動きませんでしたが、KVMで仮想化されたLinux上ではいちおうゲストが立ち上がるようでした。(ネットワーク設定などがうまくいかないようでした....)
vagrant-kvmでvagrantが使う仮想化システムをVirtualBoxではなくKVMに変えて試してみようと試みてみましたが、手元の環境ではちょっとうまくいきませんでした、ただlibvirtやqemu-kvmのバージョンなど条件が揃えばひょっとしたら動くかも?という雰囲気はありました。
ご参考まで。

naoyanaoya 2013/09/21 08:43 なるほどなるほど。

自分の知人が同じように仮想化環境でやってみたけどうまくいかず、物理サーバーにしたら解決したといってたのでその線を疑ったのでした。たしかそれは Xen だったかもです。