hidemium's blog

日々学んだことをアウトプットする。

Chef + serverspec + Jenkins でサーバ構築からテストまでを自動化してみた

Chefとserverspecでサーバ構築とテストをプログラマブルにできるようになったため、Jenkinsによりサーバ構築からテストまでを自動化してみました。

やりたいこと

以下のように、Chefのリポジトリの更新をトリガーに、サーバ構築からテストまでをJenkinsにて自動化します。

  1. Chefのレシピをリモートリポジトリへgit pushすると、Jenkinsが通知を検知
  2. JenkinsからChefを実行し、サーバを構築
  3. サーバの構築が正常終了すれば、Jenkinsからserverspecを実行し、サーバの状態をテスト

構成

CentOS 6.5 : Chef、serverspec、Jenkins、Gitをインストール
Ubuntu 12.04 : サーバ構築、テスト対象
※上記2台のサーバはVMware ESXi 5.1上で動作しています。

インストール

以下のソフトウェアをインストールします。
※過去の記事のリンクになります。

設定

各ソフトウェアの設定について説明していきます。
SSH認証の設定は完了しているものとします。

Chef

まず、Chefのリポジトリを作成します。

$ knife solo init chef-repo

サーバ構築用のcookbookを作成します。
今回は、apache2をインストールするレシピを用意します。

$ cd chef-repo/
$ knife cookbook create apache2 -o site-cookbooks
$ vi site-cookbooks/apache2/recipes/default.rb
# Apacheをインストール
# sudo apt-get -y install apache2
package "apache2" do
    action :apache2
end

JSONファイルが作成されるため、実行するapache2のcookbookを指定します。

$ vi nodes/<サーバのIPアドレス>.json
{
  "run_list":[
    "recipe[apache2]"
  ]
}

serverspec

Chefのrootディレクトリに移動し、serverspecの初期設定を行います。

$ cd chef-repo/
$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1  #UN*Xを選択

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1 #SSHを選択

Vagrant instance y/n: n #nを選択
Input target host name: 192.168.xxx.xxx #テスト対象サーバのIPアドレスを指定
 + spec/
 + spec/192.168.xxx.xxx/
 + spec/192.168.xxx.xxx/httpd_spec.rb
 + spec/spec_helper.rb
 + Rakefile

apache2がインストールされているか確認するテストコードを書きます。

$ mv spec/192.168.xxx.xxx/httpd_spec.rb spec/192.168.xxx.xxx/apache2_spec.rb
$ vi spec/192.168.xxx.xxx/apache2_spec.rb
require 'spec_helper'

describe package('apache2') do
  it { should be_installed }
end

Git

Chef・serverspec用のリモートリポジトリを作成します。

$ mkdir /var/lib/git
$ mkdir /var/lib/git/chef-cookbooks.git
$ cd /var/lib/git/chef-cookbooks.git
$ git --bare init

Chef・serverspec用のローカルリポジトリを作成します。
chef-repoのディレクトリに移動し、リポジトリの初期化を行い、ローカルリポジトリを作成します。
リモートリポジトリには何も入っていないため、ローカルリポジトリの内容を追加します

$ cd chef-repo/
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote add origin /var/lib/git/chef-cookbooks.git
$ git push origin master

Jenkins

Jenkinsでのジョブは、jenkinsユーザにより実行されるため、パスワードなしでsudoを実行する設定を入れておきます。

$ visudo
jenkins ALL=(ALL) NOPASSWD: ALL # ←最終行に追加

Jenkinsにログイン後、[新規ジョブの作成]をクリックし、ジョブ名とビルドを設定します。

以下のように設定を入れ、[OK]をクリックします。

  • ジョブ名: chef-cookbooks
  • フリースタイル・プロジェクトのビルド オン

上記で作成したリポジトリと連携するため、[ソースコード管理]を以下のように設定します。

  • ソースコード管理
    • Git オン
    • Repositories
      • Repository URL: /var/lib/git/chef-cookbooks.git
      • Credentials: なし
    • Branches to build
      • Branch Specifier (blank for 'any'): */master

リポジトリのgit pushを検知するために、[ビルド・トリガ]を以下のように設定します。

  • ビルド・トリガ
    • SCMをポーリング チェック
      • スケジュール 下記に記載
      • post-commitフックを無視 チェックオフ

スケジュールはcronのように設定できます。
今回は、15分毎にチェックが走るように、以下のように設定します。

H/15 * * * *

Chefとserverspecのコマンドを実行するために、[ビルド手順を追加]>[シェルの実行]をクリックし、[シェルの実行]を以下のように設定します。

sudo /root/.rbenv/shims/knife solo bootstrap <ユーザ名>@192.168.xxx.xxx
sudo /root/.rbenv/shims/rake spec

※bootstrapはprepareとcookを同時に実行するオプションです。

上記の設定ができたら、[保存]をクリックします。

実行

以下のように、Chefのファイルを修正後、commitし、git pushを実行します。

$ cd chef-repo/
$ vi site-cookbooks/apache2/recipes/default.rb
$ git add .
$ git commit -m "first commit"
$ git push origin master

スケジュールのタイミングによりgit pushの通知が検知されます。
通知を検知すると、JenkinsのワークスペースにChefのリポジトリがコピーされます。

ジョブが実行されたら、[ジョブ名]>[ビルド履歴]>[ビルド番号]へ移動し、[コンソール出力]をクリックします。ジョブが正常終了すれば、コンソール出力の末尾に「Finished: SUCCESS」と表示されます。

以下は、Chefとserverspecの実行が正常終了していることを示しています。

SCMのポーリングが実行
ビルドします。 ワークスペース: /var/lib/jenkins/workspace/chef-cookbooks
Fetching changes from the remote Git repository
Fetching upstream changes from /var/lib/git/chef-cookbooks.git
Checking out Revision 187f8b995de5ddc053a18a3d114f1aa5018f1979 (origin/master)
[chef-cookbooks] $ /bin/sh -xe /tmp/hudson7478251361203469042.sh
+ sudo /root/.rbenv/shims/knife solo bootstrap <ユーザ名>@192.168.xxx.xxx
Running Chef on 192.168.xxx.xxx...
Checking Chef version...
:
Running handlers complete
[0m
Chef Client finished, 1/1 resources updated in 8.041423413 seconds[0m
+ sudo /root/.rbenv/shims/rake spec
/root/.rbenv/versions/2.0.0-p451/bin/ruby -S rspec spec/192.168.xxx.xxx/httpd_spec.rb
.

Finished in 0.10092 seconds
1 example, 0 failures
Finished: SUCCESS

実行が失敗した場合は、以下のようにエラーが表示され、そこでJenkinsのジョブは停止します。

Build step 'シェルの実行' marked build as failure
Finished: FAILURE

Chefの実行で失敗した場合は、serverspecは実行されないため、正常終了すればサーバ構築からテストまですべて終了したことになります。

おわりに

今回構築した環境は、共同でChefのレシピやserverspecのテストコードを書くフローを想定していました。各メンバーは、PCでChefやserverspecのコードを作成後、VirtualBoxでテストし、問題がなければリモートリポジトリへgit pushします。リモートリポジトリへgit pushすれば、後はJenkinsがサーバ構築からテストまでを自動で行い、正常終了すれば本番環境へ適用するといった流れです。ただし、今回の環境ではテスト環境をクリアすることは含まれていないため、スナップショットを手動で戻してやる手間が残ってしまいました。
次回は、このあたりの問題を解決していきたいと考えてます。