More than 1 year has passed since last update.

目的

Test KitchenをつかってChefへ入門します。

Chefを使うとサーバの設定を自動化することができます。

Test KitchenはChefで利用するクックブックの統合テストを行うことができます。

Chefに入門する環境として初めからTest Kitchenを使うのが良いと感じたのでこの文書を作成しました。

関連: LT駆動開発10でTest KitchenではじめるChef入門という話をした。 - そんなこと覚えてない

環境構築

Chef DKのインストール

Chef DKはChefのクックブック開発する上で便利なものがまとめられています。
DKはDevelopment Kitの略です。
今回利用するTest KitchenはChef DKに含まれています。

Chef DKは https://downloads.chef.io/chef-dk/ よりダウンロードできます。
brewcaskを使用しているのであれば

$ brew cask install chefdk

でインストールすることが可能です。

$ chef -v
Chef Development Kit Version: 0.3.5
$ which chef
/usr/bin/chef

$ kitchen -v
Test Kitchen version 1.2.1
$ which kitchen
/usr/bin/kitchen

補足ですが、/usrにインストールされて気持ち悪いかもしれませんが、実体は/opt/chefdkにインストールされます。

$ ls -ll /usr/bin/chef
lrwxr-xr-x  1 root  wheel  20  1  3 14:22 /usr/bin/chef -> /opt/chefdk/bin/chef

vagrantのインストール

Test KitchenのdriverはデフォルトでVagrantを利用します。
つまり、普通に使うとVagrantも使うことになります。

Vagrantのダウンロード
https://www.vagrantup.com/downloads.html

brewcaskを使用しているのであれば

$ brew cask install vagrant

でもインストールすることが可能です。

$ vagrant -v
Vagrant 1.7.2

プロジェクトの作成

Chefではクックブックという単位で開発していきます。
Chef DKに添付されているchefコマンドのgenerateサブコマンドを使用すると雛形が作成できます。
hello-chefというクックブックを作成してそこで作業してみましょう。

$ chef generate cookbook hello-chef
$ cd hello-chef

hello-chefというクックブックができたので、このディレクトリで作業を進めていきます。

Test Kitichenの設定

Testk Kitchnの設定は.kitchen.ymlに書きます。
generateコマンドで自動的に生成されています。

少し変更して、今回は下記のようにしました。

kitchen.yml
---
driver:
  name: vagrant

provisioner:
  name: chef_zero

platforms:
  - name: ubuntu-14.10

suites:
  - name: default
    run_list: recipe[hello-chef::default]
    attributes:

platformのcentosを削って、ubuntuの14.10を指定しました。

インスタンスの作成

Test Kitchenのインスタンスを作成してみましょう。
chef-clientを実行するための仮想マシンが生成されます。

Chef DKに開発を支援するchefコマンドがあるため、紛らわしくならないように、Chefを実行することを実際にサーバの設定をしてくれるコマンドのchef-clientと呼んでいきます。

インスタンスの作成にkitchen verifyコマンドを使用してみます。
このコマンドの実行は長いので、じっくり待つことになります。通信環境によりますが、15分かかりました。

$ kitchen verify

このコマンドを実行すると

  1. インスタンスを作成(setup)
  2. chef-clientを実行(converge)
  3. テストコードの実行(verify)

をしてくれます。
もしインスタンスを作成済みでchef-clientを実行であれば、テストコードの実行のみが実行されます。

また、作成したインスタンスはlistサブコマンドで確認できます。

$ kitchen list
Instance             Driver   Provisioner  Last Action
default-ubuntu-1410  Vagrant  ChefZero     Verified

Last Actionでどこまで実行されているのか確認することができます。
作成するだけであればkitchen setupコマンドがあります。
今回はなるべく全体像が把握できるようにverifyコマンドを利用しました。

作成したインスタンスにログインする

loginサブコマンドで作成したインスタンスにログインできます。
ログインしてchef-clientがインストールされているのかどうか確認してみます。

$ kitchen login default-ubuntu-1410
default-ubuntu-1410 $ /opt/chef/bin/chef-client -v
Chef: 12.0.3

Chefは/opt/chefにインストールされています。

kitchen loginして実行するコマンドの戦闘にはdefault-ubuntu-1410 $と記述しておきます。

chef-applyで遊んでみる

chef-clientは引数などが複雑なので、はじめはchef-applyを使ってChefを体験していきます。

まずはファイルを作成してみましょう。

chef-applyをつかってhello.txtを作成するには以下のコマンドを実行します。

default-ubuntu-1410 $ /opt/chef/bin/chef-apply -e 'file "hello.txt" do content "Hello, World\n" end'

chefは/opt/chefにインストールされていて、環境変数PATHが設定されていないので、フルパスでコマンドを実行しています。

Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[hello.txt] action create
    - create new file hello.txt
    - update content in file hello.txt from none to 77df26
    --- hello.txt       2015-01-03 02:34:39.707849381 +0000
    +++ ./.hello.txt20150103-2182-1a8z2qa       2015-01-03 02:34:39.707849381 +0000
    @@ -1 +1,2 @@
    +Hello, World

Hello.txtが作成できています。中身を確認してみます。

$ cat hello.txt
hello,world

レシピファイルの作成

ワンライナーでファイルを作成しましたが、見通しが悪いのでファイルに書いてみます。

ローカルマシンのrecipe/hello.rbに書いてみましょう。
このhello.rbをレシピファイルと呼びます。

hello.rb
file 'hello.txt' do
  content "Hello, World\n"
end

recipe/hello.rbを仮想マシンにアップロードするにはkitchen convegeコマンドを利用します。

$ kitchen converge

本来はchef-clientを実行するコマンドですが、クックブックの同期が行なわれるのを利用しています。
このコマンドを実行すると/tmp/kitchen/cookbooks/hello-chef/recipesにファイルが転送されています。実行してみましょう。

default-ubuntu-1410 $ /opt/chef/bin/chef-apply /tmp/kitchen/cookbooks/hello-chef/recipes/hello.rb
Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[hello.txt] action create (up to date)

長いですが、パスを省略してかけばchef-apply hello.rbをしているだけです。

変更がないと、面白みがないのでファイルの中身を変更してみましょう。

recipes/hello.rb
file 'hello.txt' do
  content "Hello, Chef\n"
end

contentに指定した文字列がファイルの中身になります。
さて、レシピファイルを転送しましょう。

$ kitchen converge

レシピファイルを実行します。

default-ubuntu-1410 $ /opt/chef/bin/chef-apply /tmp/kitchen/cookbooks/hello-chef/recipes/hello.rb
Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[hello.txt] action create
    - update content in file hello.txt from 8663ba to c634c3
    --- hello.txt       2015-01-03 06:59:34.007560036 +0000
    +++ ./.hello.txt20150103-5116-1bfkldm       2015-01-03 07:07:02.527169431 +0000
    @@ -1,2 +1,2 @@
    -Hello, World
    +Hello, Chef

実行できました。中身がHello, Chefに変更されています。

ところで、hello.rbはhello-chefクックブックのレシピなります。
このレシピを指すのにrecipe[hello-chef::hello]と表現することができます。
省略してhello-chef::helloレシピと呼ぶことがあります。

ランリストの設定

convergeサブコマンドを実行した際に実行するレシピを設定することができます。
この設定は.kitche.ymlに設定します。
「実行するレシピの一覧」をランリストと呼びます。

.kitchen.ymlrun_listという項目があります。

kitchen.ymlより抜粋
    run_list:
      - recipe[hello-chef::default]

hello-chef::defaultが設定されています。
hello-chef::helloも加えてみます。

kitchen.yml変更後
    run_list:
      - recipe[hello-chef::default]
      - recipe[hello-chef::hello]

変化がわかるようにhello.rbも書き換えておきましょう。

recipes/hello.rb
file 'hello.txt' do
  content "Hello, Cookbook\n"
end

変更したらconvergeサブコマンドを実行してみます。

$ kitchen converge

hello.txtを確認してみます。

default-ubuntu-1410 $ cat hello.txt
Hello, Chef

変化していません。
ktichen convergeコマンドを使用した際には/ディレクトリが基準になるので作成したファイルは/hello.txtになります。

default-ubuntu-1410 $ cat /hello.txt
Hello, Cookbook

仮想マシン上でレシピを実行したい場合は、下記コマンドを実行します。

$ sudo chef-client -z -j /tmp/kitchen/dna.json -c /tmp/kitchen/client.rb

何が起きているのか詳細を知りたい場合は、各ファイルの中身やオプションの意味を調べてみてください。

蛇足ですが、Chefの入門が難しく感じるのは、初めて挑戦する時に指定するオプションが多いのが原因な気がします。

テストコードを書いてみる

Test Kitchenは統合テストを行うツールなので、テストコードを記述するとテストを実行することができます。
折角なので書いてみましょう。

今回はServerspecを使います。

test/integration/default/serverspecディレクトリにhello_spec.rbを作成してみます。
ファイルを作成する位置に意味があるので、同じファイル名にすることをおすすめします。

$ mkdir -p test/integration/default/serverspec
test/integration/default/serverspec/hello_spec.rb
require 'serverspec'

set :backend, :exec

describe 'Hello Chef' do
  describe file('/hello.txt') do
    it { should contain('Cookbook') }
  end
end

/hello.txtの中に「Cookbook」という文字列が含まれているか確認するテストを書きました。

Serverspecをここまで利用していなかったので、仮想マシンがServerspecを使えるように設定されていません。
setupサブコマンドを実行すると必要なものがインストールできます。

$ kitchen setup

verifyサブコマンドを実行した際に一度setupサブコマンドは自動で実行されていますが、その際はServerspecが不要だったためインストールされていませんでした。明示的にkitchen setupコマンドを呼ぶことで再度セットアップすることができます。

テストを実行するにはverifyサブコマンドを実行します。

$ kitchen verify
Hello Chef
  File "/hello.txt"
           should be file

       Finished in 0.09 seconds (files took 0.27225 seconds to load)
       1 example, 0 failures
       Finished verifying <default-ubuntu-1410> (0m1.99s).

Attributesを使う

レシピファイルの中で変数を使いたいことがあります。
変数のように値を差し替えるような機能があるとレシピを変更せずに作成するファイルの内容を調整することができるようになります。
この機能がAttributesです。

Test Kittchenでは.kitchen.ymlファイルでattributeを設定しておくことができます。messageという値を設定してみます。

kitchen.yml抜粋
    attributes:
      message: "World!!"

レシピファイルの中でmessageの値を利用するにはnode.messageになります。
実際に使ってみます。

recipes/hello.rb
file 'hello.txt' do
  content "Hello, #{node.message}\n"
end

レシピを実行します。

$ kitchen converge

テストを実行してみます。

$ kitchen verify
Hello Chef
  File "/hello.txt"
    should contain "Cookbook" (FAILED - 1)

Failures:

  1) Hello Chef File "/hello.txt" should contain "Cookbook"
     Failure/Error: it { should contain('Cookbook') }
       expected File "/hello.txt" to contain "Cookbook"
       /bin/sh -c grep\ -qs\ --\ Cookbook\ /hello.txt\ \|\|\ grep\ -qFs\ --\ Coo
kbook\ /hello.txt

     # /tmp/busser/suites/serverspec/hello_spec.rb:7:in `block (3 levels) in <to
p (required)>'

Finished in 0.10098 seconds (files took 0.28348 seconds to load)
1 example, 1 failure

/hello.txtの中身が変更されてテストが失敗します。
ファイルに「Cookbook」という文字がなくなったためです。
hello_spec.rb`を修正しておきます。

test/integration/default/serverspec/hello_spec.rb
require 'serverspec'

set :backend, :exec

describe 'Hello Chef' do
  describe file('/hello.txt') do
    it { should contain('World') }
  end
end
$ kitchen verify

なれてきたらテストファーストでやってみると良いと思います。

通しでテストする

仮想マシンをいろいろ操作した上でテストを実行しています。
仮想マシンを起動してレシピを実行するだけで本当にテストが成功するのでしょうか?
testサブコマンドを利用すると一旦すべて破棄して仮想マシンをつくるとこらか確認してくれます。

$ kitchen test

終了したら仮想マシンが削除されてしまいます。
残したい場合は--destroy=neverオブションを使います。

より詳細は、test-kitchenのつかいかた - Qiitaを参照してください。

補足

インスタンスが停止しているとき

「マシンを再起動しちゃってインスタンスが止まちゃってるよ!」なんてことがあると思います。
.kitchen/kitchen-vagrant/default-ubuntu-1410/にVagrantfileがあるので、

$ cd .kitchen/kitchen-vagrant/default-ubuntu-1410/
$ vagrant up

とすれば大丈夫です。

すっきりさせたい場合はkitchen destroyしてしまう荒技もあります。もちろんsetupからやりなおすことになります。

まとめ

Chefに挑戦する際にTest Kitchenを使うと仮想マシンのセットアップをしてくれるのでChef入門しやすくなるのではないかと思って書いてみました。
しかも、Test Kitchenを使ってChefを始めるとテストコードを書く環境も整っています。

レシピに書いたfileなどのサーバに変化を与える命令はResoucesと言います。
その他の命令を確認したい場合はResources and Providers Reference — Chef Single-page Topicsを見てください。

Chefを使っていく上で知っておいたほうが良いことは他にもありますが、まずは最初の敷居を下げることができたら良いと思います。

作成したクックブックを実際のサーバにknife zeroで適用するところも書きたいですが長くなりそうなので持ち越します。

参考文献