Test KitchenでAmazonLinuxのレシピ開発効率化1

サーバー環境の構築にOpsWorksを利用しています。OpsWorksとても便利です。ただ、不満もあって、これはOpsWorksというかAmazonLinuxに対する不満ですが

AmazonLinux用のレシピをローカルでテストできない

AmazonLinuxは基本的にAWSの中でしか使えないようで、開発上いろいろ不都合があったりします。そこでChefのテストハーネスであるTest Kitchenを使ってレシピ開発を効率化できないか試してみました。長くなるので2回に分けます。

OpsWorksのリポジトリ構成

OpsWorksを利用する場合はたとえば下記のような感じでルートにクックブックが複数並ぶ形になっていると思います。

$ tree -L 1
.
├── mysql
├── nginx
├── php
├── wordpress
└── yum-repo

OpsWorksのクックブックリポジトリ

今回はこの中のひとつのクックブックの中でTest Kitchenを走らせてみたいと思います。いつもはローカルでレシピを開発するときにはCentOSを利用しているので今回利用するのはVagrantのCentOSのイメージです。AmazonLinuxは次回出てきます。

Test Kitchenの用意

下記のコマンドTest-Kitchenをインストールします。

$ gem install test-kitchen

その後、目的のクックブックのディレクトリに移動して、kitchen initします。

$ kitchen init
      create  .kitchen.yml
      create  chefignore
      create  test/integration/default

Test Kitchenの設定は案外簡単で.kitchen.ymlを設定するだけです。

.kitchen.ymlは最初はこうなってると思います。

---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: ubuntu-14.04
  - name: centos-7.1

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

まずTest KitchenをVagrantで動かしてみる

たとえば下記のような感じでそのクックブックのレシピを走らせるための設定を書いてみます。

.kitchen.yml

---
driver:
  name: vagrant
  network:
    - ["private_network", {ip: "192.168.33.33"}]
  customize:
    memory: 2048
    natdnsproxy1: "off"
    natdnshostresolver1: "off"

provisioner:
  name: chef_solo

platforms:
  - name: centos-6.5
    driver:
      box: "centos6.5"
      box_url: "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box"

suites:
  - name: default
    run_list:
      - recipe[nginx::default]
    attributes: {
      nginx: {
        root: "/var/www/wordpress",
        conf:  "fpm-default.conf",
        ssl: {
          key: "-----BEGIN RSA PRIVATE KEY-----...",
          cert: "-----BEGIN CERTIFICATE-----..."
        }
      }
    }

kitchen listを実行すると今のインスタンス状態が表示されます。

$ kitchen list
Instance           Driver   Provisioner  Verifier  Transport  Last Action
default-centos-65  Vagrant  ChefSolo     Busser    Ssh        <Not Created>

インスタンスを作ってみます。この段階ではレシピは実行されてません。

$ kitchen create

インスタンスが作られるまでしばし待ちます。

$ kitchen list
Instance           Driver   Provisioner  Verifier  Transport  Last Action
default-centos-65  Vagrant  ChefSolo     Busser    Ssh        Created

インスタンスが作られてCreatedに変わりました。

次にレシピを実行します。kitchen convergeを実行しましょう。

$ kitchen converge

       [2016-01-23T05:53:25+00:00] ERROR: Cookbook yum-repo not found. If you're loading yum-repo from another cookbook, make sure you configure the dependency in your metadata
       [2016-01-23T05:53:25+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
>>>>>> Converge failed on instance <default-centos-65>.
>>>>>> Please see .kitchen/logs/default-centos-65.log for more details
>>>>>> ------Exception-------
>>>>>> Class: Kitchen::ActionFailed
>>>>>> Message: SSH exited (1) for command: [sh -c '

sudo -E /opt/chef/bin/chef-solo --config /tmp/kitchen/solo.rb --log_level auto --force-formatter --no-color --json-attributes /tmp/kitchen/dna.json
']
>>>>>> ----------------------

エラーになりました。

ERROR: Cookbook yum-repo not found. If you’re loading yum-repo from another cookbook, make sure you configure the dependency in your metadata

今テストしようとしているクックブックが依存している他のクックブックが見つからないようです。

Test Kitchenの「make sure you configure the dependency in your metadata」というエラー

対象のクックブックのmetadata.rbをチェックすると下記のようになっていて依存関係は記述されています。また、knife soloやOpsWorksでもこれで正常に動きます。なぜ、Test Kitchenでは動かないのか?依存性を解決するにはどうするのか…?

name "nginx"
depends "yum-repo"

これを解決するのに試行錯誤が必要になりました。

おそらくこのエラーって、OpsWorksでChefを使い始めてちょっと慣れてきたのでServerspec使いたいからじゃあTest Kitchenだ、みたいな人はみんなハマるんじゃないかと思いました。ぼくがまさにそうなんですが。こちらを参考にしました。

How to use hand-written cookbooks when using berkshelf in chef?

解決の鍵はBerksfileの記述でした。下記のファイルを目的のクックブックのルートにおきます。

Berksfile

source 'https://api.berkshelf.com'

# 全てのcookbookのパスを宣言する
Dir['../**'].each do |path|
    if File::ftype(path) == 'directory'
        cookbook File.basename(path), path: path
    end
end

もう1度kitchen convergeするとうまくいくと思います。

$ kitchen converge

無事レシピが実行されてkitchen listすると、Convergedになっているのが分かります。

$ kitchen list
Instance           Driver   Provisioner  Verifier  Transport  Last Action
default-centos-65  Vagrant  ChefSolo     Busser    Ssh        Converged

Serverspecでテストする

Serverspecで簡単なテストを書いてみます。

test/integration/serverspec/default/nginx_spec.rb

require 'serverspec'

set :backend, :exec

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

describe service('nginx') do
  it { should be_enabled }
  it { should be_running }
end

describe port(80) do
  it { should be_listening }
end

# sendfile が off でないとCSS等を変更しても反映されない
describe file('/etc/nginx/nginx.conf') do
  it { should be_file }
  it { should contain('off;').after(/sendfile/) }
end

# fpmとの連携はHTTP経由ではなくSocket経由で行う
describe file('/etc/nginx/conf.d/default.conf') do
  it { should be_file }
  it { should contain 'unix:/var/run/php-fpm/php-fpm.sock;' }
end

その後、kitchen verifyを実行します。

$ kitchen verify

       ...

       Package "nginx"
         should be installed
       
       Service "nginx"
         should be enabled
         should be running
       
       Port "80"
         should be listening
       
       File "/etc/nginx/nginx.conf"
         should be file
         should contain "off;"
       
       File "/etc/nginx/conf.d/default.conf"
         should be file
         should contain "unix:/var/run/php-fpm/php-fpm.sock;"
       
       Finished in 0.13445 seconds (files took 0.35896 seconds to load)
       8 examples, 0 failures
       
       Finished verifying <default-centos-65> (0m53.10s).

テストはサンプルなので、自分の環境に読み替えて試してみて下さい。テストが終わったらkitchen destroyでインスタンスを破棄します。

$ kitchen destroy

ここまでで、OpsWorksを使ってる人がVagrantベースでTest Kitchenを利用できるところまでの設定を行いました。次回はこれをAmazonLinuxで実行できるようにしたいと思います。

続きはこちらです。

参考になるページ

test-kitchenのつかいかた
Test KitchenではじめるChef入門
test-kitchenでインフラのTDDに挑戦[Rails/Nginx/MySQL/rbenv]