critical alertのブログ

普段はサーバのお世話係をしています

cookbookを書くときにtest-kitchenを使うと捗る

ちょっとcookbook書いてちゃんと動くか試したいってときにいちいち新しくVagrantfileを用意してnode.json書いて、knife solo cookしてserverspecでテストして...とかやりたいことはrecipeを試したいだけなのに準備が大変なんですよね。

そんなときはその辺を一気に面倒みてくれる便利なtest-kitchenを使ってcookbookの開発をします。

test-kitchenとはchefのcookbookを任意の環境でテストするツールで、vagrantやec2、docker(LXC)上でcookbookを適用し、minitestやserverspecでテストできます。

もとはchefだけでしたが、プラグインを入れることでpuppetやansibleとも連携できるようです。

設定ファイル

test-kitchenの設定ファイルは.kitchen.ymlです。 kitchen initコマンドで生成できます。

$ bundle exec kitchen init

こんな感じのymlが生成されます。

vi .kitchen.yml

---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: ubuntu-12.04
  - name: centos-6.4

suites:
  - name: default
    run_list:
    attributes:

設定ファイルの用語

driver

  • テストする仮想環境のこと
  • vagrant
  • EC2
  • GCE
  • docker 等

provisioner

  • chefだけじゃなくansibleとかも使用できる(要追加プラグイン)
  • chef-solo
  • ansible
  • puppet

platforms

suites

  • テストスイート
  • runlistやattributeを記述する

instance

テストに使うVMinstanceと呼ばれます。 命名規則は suitesとplatforms に定義された名前になります。

例えばsuitesにapp01、platformsにcentos6.5と定義してあるとする。 この場合instanceの名前は app01-centos6.5 となります。

コマンド

$ kitchen create : インスタンスの起動を行う

$ kitchen setup : インスタンスにchefとbusserをインストールする

$ kitchen converge : 収束、なのでchefのrecipeを適用させる

$ kitchen verify : テストを実行

$ kitchen destroy : インスタンスを破棄

$ kitchen test : 上記の全ステップを一気に実行する

テスト

serverspecを使います。testを置くファイルパスは

CHEF_REPO/test/integration/SUITES_NAME/TESTING_FRAMEWORK/

なので、

CHEF_REPO/test/integration/app01/serverspec/

となり、このディレクトリ以下の、*_spec.rbが読み込まれます。

流れ

全体的な流れは以下のような感じ。 今回はDriverはvagrant、Provisionerはchef-solo、OSはcentos 6.5を使い、テストはserverspecにします。

まずは仮想マシンの準備。

$ vi .kitchen.yml

---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: centos-6.5

suites:
  - name: default
    run_list:
    attributes:

以下のコマンドで状態を確認できます。

$ bundle exec kitchen list

Instance           Driver   Provisioner  Last Action
default-centos-65  Vagrant  ChefSolo     <Not Created>

Last Action でマシンがどんな状態なのが把握できます。

仮想マシンを立ち上げます。 vagrantでいうところのvagrant upです。

$ bundle exec kitchen create default-centos-65

-----> Starting Kitchen (v1.3.1)
-----> Creating <default-centos-65>...
       Bringing machine 'default' up with 'virtualbox' provider...
       ==> default: Importing base box 'opscode-centos-6.5'...
       ==> default: Matching MAC address for NAT networking...
       ==> default: Setting the name of the VM: default-centos-65_default_1422170919471_91104

中略

       Vagrant instance <default-centos-65> created.
       Finished creating <default-centos-65> (0m57.51s).
-----> Kitchen is finished. (0m58.05s)
$ bundle exec kitchen list

Instance           Driver   Provisioner  Last Action
default-centos-65  Vagrant  ChefSolo     Created

Last ActionCreatedになりました。

これで仮想マシンの準備ができたのでrecipeを書きます。

$ vi cookbooks/zsh/recipes/default.rb

package 'zsh'

.kitchen.ymlにrecipeを追記

$ vi .kitchen.yml

---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: centos-6.5

suites:
  - name: default
    run_list:
      - recipe[zsh::default]
    attributes:

converge でrecipeを仮想マシンに適用させます。chefでいうところのcookです。

$ bundle exec kitchen converge default-centos-65

-----> Starting Kitchen (v1.3.1)
-----> Converging <default-centos-65>...
       Preparing files for transfer
       Preparing dna.json
       Preparing cookbooks from project directory
       Removing non-cookbook files before transfer
       Preparing data_bags
       Preparing environments
       Preparing roles
       Preparing solo.rb
-----> Installing Chef Omnibus (install only if missing)
       downloading https://www.chef.io/chef/install.sh
         to file /tmp/install.sh
       trying wget...
       Downloading Chef  for el...
       downloading https://www.chef.io/chef/metadata?v=&prerelease=false&nightlies=false&p=el&pv=6&m=x86_64
         to file /tmp/install.sh.2231/metadata.txt
       trying wget...
       url      https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-12.0.3-1.x86_64.rpm
       md5      3634d1a3b6ae2e5977361075da0f44cc
       sha256   0ec6162b9d0ca2b2016ff02781d84905f712d64c7a81d01b0df88f977832f310
       downloaded metadata file looks valid...
       downloading https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-12.0.3-1.x86_64.rpm
         to file /tmp/install.sh.2231/chef-12.0.3-1.x86_64.rpm
       trying wget...
       Comparing checksum with sha256sum...
       Installing Chef
       installing with rpm...
       警告: /tmp/install.sh.2231/chef-12.0.3-1.x86_64.rpm: ヘッダ V4 DSA/SHA1 Signature, key ID 83ef826a: NOKEY
       準備中...                ########################################### [100%]
          1:chef                   ########################################### [100%]
       Thank you for installing Chef!
       Transferring files to <default-centos-65>
       Starting Chef Client, version 12.0.3
       Compiling Cookbooks...
       Converging 1 resources
       Recipe: zsh::default
           - install version 4.3.10-9.el6 of package zsh
       Running handlers:
       Running handlers complete
       Chef Client finished, 1/1 resources updated in 2.158589696 seconds
       Finished converging <default-centos-65> (2m57.70s).
-----> Kitchen is finished. (2m58.26s)
  • chefが対象のホストに入っていなければomnibusインストールしてくれます
$ bundle exec kitchen list

Instance           Driver   Provisioner  Last Action
default-centos-65  Vagrant  ChefSolo     Converged

Convergedになりました。

次にテストを追加します。

$ mkdir -p test/integration/default/serverspec/
$ vi test/integration/default/serverspec/zsh_spec.rb

require 'serverspec'

set :backend, :exec

describe package 'zsh' do
  it { should be_installed }
end

verifyでテストを実行します。

$ bundle exec kitchen verify default-centos-65

-----> Starting Kitchen (v1.3.1)
-----> Verifying <default-centos-65>...
       Removing /tmp/busser/suites/serverspec
       Uploading /tmp/busser/suites/serverspec/zsh_spec.rb (mode=0644)
-----> Running serverspec test suite
       /opt/chef/embedded/bin/ruby -I/tmp/busser/suites/serverspec -I/tmp/busser/gems/gems/rspec-support-3.1.2/lib:/tmp/busser/gems/gems/rspec-core-3.1.7/lib /opt/chef/embedded/bin/rspec --pattern /tmp/busser/suites/serverspec/\*\*/\*_spec.rb --color --format documentation --default-path /tmp/busser/suites/serverspec

       Package "zsh"
         should be installed

       Finished in 0.12178 seconds (files took 0.37162 seconds to load)
       1 example, 0 failures
       Finished verifying <default-centos-65> (0m2.73s).
-----> Kitchen is finished. (0m3.23s)
  • これはtestディレクトリを対象ホストへアップロード後、busser経由でserverspecをインストールしテストを実行してくれます。
$ bundle exec kitchen list

Instance           Driver   Provisioner  Last Action
default-centos-65  Vagrant  ChefSolo     Verified

状態がVerifiedになりました!

この後はconvergeverifyを繰り返してrecipeを作成し、完成したらdestroy仮想マシンを破棄します。

$ bundle exec kitchen destroy default-centos-65

-----> Starting Kitchen (v1.3.1)
-----> Destroying <default-centos-65>...
       ==> default: Forcing shutdown of VM...
       ==> default: Destroying VM and associated drives...
       Vagrant instance <default-centos-65> destroyed.
       Finished destroying <default-centos-65> (0m6.84s).
-----> Kitchen is finished. (0m7.87s)

よいと思ったところ

  • テスト対象のマシンの面倒を見てくれるのが良い
  • 設定ファイルが.kitchen.ymlひとつ
    • nodes/hostname.json を用意しなくていい
    • Vagrantfileを用意しなくていい
  • どのdriverやprovisionerを使ってもコマンドは同じ
    • 今回はvagrantとchefを利用しましたが、ec2やpuppetを使ってもcreateconverge

あとはこれをそのままjenkinsやCircleCI上で実行できるようにすればcookbook単位のCIも可能ですね。

逆にあるserviceのサーバ構成全体をこれで管理しようとすると、
nodes/hostname.json.kitchen.ymlにrecipeの定義が分散してしまって二重管理になりあまり良くない感じでした。

参考にしたもの