今からでも始められる Chef 入門

# はじめに
* おおまかな Chef の概要からテストまでを記載
* また knife-solo を使用した環境構築から、Serverspec および test-kitchen を使ったテストのサンプルもまとめた

# Chef
## 概要
* ミドルウェアの設定やアプリケーションのインストールなどのサーバ構築を自動化するためのツールで、今はやりの Infastructure as Code
* サーバの増強などに合わせたインフラの構築の自動化やデプロイ時の環境整備にも有用
* 従来の手順書がそのままコードになるため、メンテナンスもしやすい
    * バージョン管理やテストができる
* 複数の同一環境を構築することに向いているため、Vagrant などと組み合わせることでプロジェクトメンバー間で開発環境を統一することにも利用できる

# Chef の構成
* Chef には大きく "Chef-Server/Client 構成" と "Chef-Solo 構成" がある
* 規模にもよるが、個人的には "Chef-Solo 構成" で十分なイメージ (そして Chef-Solo しか使ったことありません)

## Chef-Server/Client 構成
* Chef-Server, Node, Workstation の 3 コンポーネントから成る
* [公式ページ 構成図](http://www.getchef.com/chef/#how-works)

### Chef-Server
* Node 情報の管理
* Cookbook の管理
* Client の認証

### Node
* 管理対象サーバで Chef-Client が動く
* Chef-Server に問い合わせ、Cookbook を適用

### Workstation
* Cookbook の開発はここで行う
* Cookbook を Chef-Server にアップロード

## Chef-Solo 構成
* Server も Client も必要としないスタンドアローン版
* 対象のマシンにコマンドとして Chef を適用する

# Chef-solo
## インストール

```bash
$ curl -L https://www.opscode.com/chef/install.sh | sudo bash
# インストール確認
$ chef-solo -v
Chef: 11.14.0.alpha.3
```

## 簡単な用語説明

### Recipe (レシピ)
* Resource の定義(サーバの設定)が書かれるファイル
* 従来の手順書にあたるもの
* Ruby で記述でき、次のようなファイルに成る
    * これは、git-core というパッケージを apt なり yum なりでインストールするということを示す
    * 正確には、対象のサーバに対し "git-core が入った状態であること" という状態を記載している
    * OS の差分は Chef が吸収するため、基本的には考えなくて良い(例外はある)

```rb:default.rb
package "git-core" do
  action :install
end
```

### cookbook (クックブック)
* Recipe を含め、その他必要なファイルやデータがまとまったディレクトリ

```bash
# cookbook のディレクトリ構成
.
├── attributes
├── definitions
├── files
│   └── default
├── libraries
├── providers
├── recipes
├── resources
└── templates
    └── default
```

### kitchen (キッチン)
* 単にリポジトリとも呼ばれる
* Chef の実行に必要な関連ファイル群

```bash
# kitchen のディレクトリ構成
# 自分で cookbook を作成した場合は、基本的に site-cookbooks/ 下に置くことになる
.
├── cookbooks
├── data_bags
├── environments
├── nodes
├── roles
└── site-cookbooks
```


# knife-solo
* knife コマンドを使用することで、ローカル環境とは別の指定した環境に対し Chef-Solo を実行することができる

## ユースケース例
* ホスト OS 上に Vagrant で仮想環境を用意し、その仮想環境を Chef で構築したい場合
* ホスト OS で作成した cookbook をゲスト OS にログインせずに knife コマンドで Chef-Solo を実行できる

## インストール
```bash
# インストールはホスト OS 側で行う
$ gem install knife-solo
# インストール確認
$ gem list | grep knife
knife-solo (0.4.2)
```

## kitchen の作成
```bash
$ knife solo init <kitchen 名>
```

## cookbook の作成
```bash
# 作成した kitchen 下でおこなう
$ knife cookbook create <cookbook 名> -o site-cookbooks
```

## Recipe の作成
* cookbook を作成すると site-cookbooks/<cookbook 名>/recipes/default.rb というファイルができるのでそれを編集する(これが Recipe になる)

## Node オブジェクトの作成
* Node オブジェクトとは、管理対象サーバ(Node) にどの Recipe を適用するかを設定するファイルのこと
* sample1 というホスト名を持ったサーバに対し、git という Recipe を適用しようとした場合、次のように記述する

```json:nodes/sample1.json
{
  "run_list":[
    "recipe[git]"
  ]
}
```

* 先に sample1 に対し、ホスト OS から Chef-Solo をインストールしておくと、Node オブジェクトのひな形が作られる
* ホスト OS からの Chef-Solo インストールコマンドは以下のとおり

```bash
$ knife solo prepare sample1
```

* また、 Node オブジェクトを編集後に実際に Recipe を適用する場合は下記コマンドを入力する

```bash
$ knife solo cook sample1
```


# テスト
* Chef を使い構築した環境が意図したものになっているかを確認するためにテストを行う必要がある
* 直接サーバに SSH 接続をして確認するのもよいが、便利なテストツールがある

## Serverspec
* インフラテストのためのライブラリ(Chef 専用ではない)
* RSpec 形式で記述する

### インストール
```bash
$ gem install serverspec
# インストール確認
$ gem list | grep server                          
serverspec (1.7.0)
```

### 記述例
* 例として "git-core" パッケージがインストールされていることを期待するテストは次のように書く

```rb:git_spec.rb
require 'spec_helper'

describe package('git-core') do
  it { should be_installed }
end
```

## test-kitchen
* 前述した Serverspec などと組みわせて、仮想マシンの作成 -> Recipe の適用 -> テストまでをひと通り行う統合テストフレームワーク

### インストール
```bash
$ gem install test-kitchen
# インストール確認
$ gem list | grep kitchen
test-kitchen (1.2.1)
```



# 実際に試してみる
* Vagrant で仮想環境を用意し、vim のインストールと .vimrc の配置を knife-solo で行い、それをテストする

## kitchen から Recipe の作成まで
* kitchen の作成

```bash
# kitchen 名は "qiita0611" とする
$ knife solo init qiita0611
Creating kitchen...
Creating knife.rb in kitchen...
Creating cupboards...
```

* cookbook の作成

```bash
# cookbook 名は vim
$ knife cookbook create vim -o site-cookbooks
** Creating cookbook vim
** Creating README for cookbook: vim
** Creating CHANGELOG for cookbook: vim
** Creating metadata for cookbook: vim
```

* Recipe の編集

```rb:site-cookbooks/vim/recipes/default.rb
package "vim" do
  action :install
end

template "/home/vagrant/.vimrc" do
  source "vimrc.erb"
  owner  "vagrant"
  group  "vagrant"
  mode 0644
end
```

* 配置したい .vimrc は site-cookbooks/vim/templates/default/vim.rc.erb として置いておく
* 今回は test-kitchen を使って動作を確認するため(実際に仮想マシンを用意しないため)、Node オブジェクトの作成はスキップ

## test-kitchen の準備
* test ディレクトリの作成

```bash
$ kitchen init
      create  .kitchen.yml
      create  test/integration/default
         run  gem install kitchen-vagrant from "."
Successfully installed kitchen-vagrant-0.15.0
Parsing documentation for kitchen-vagrant-0.15.0
Done installing documentation for kitchen-vagrant after 0 seconds
```

* .kitchen.yml の編集
    * suites:run_list を Node オブジェクトの用に編集

```yaml:.kitchen.yml
---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: ubuntu-12.04

suites:
  - name: default
    run_list:
      - recipe[vim::default]
    attributes:
```

## Serverspec の用意

* test/integration/default/ 下に移動し、下記コマンドを入力

```bash
serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: y
Auto-configure Vagrant from Vagrantfile? y/n: n
Input vagrant instance name: 
 + spec/
 + spec//httpd_spec.rb
 + spec/spec_helper.rb
 + Rakefile
```

* spec_helper.rb の編集

```rb:spec_helper.rb
require 'serverspec'

include Serverspec::Helper::Exec
include Serverspec::Helper::DetectOS

RSpec.configure do |c|
  c.before :all do
    c.path = '/sbin:/usr/sbin'
  end
end
```

* spec/httpd_spec.rb のリネームおよび編集
    * httpd_spec.rb -> vim_spec.rb に変更

```rb:spec/vim_spec.rb
require 'spec_helper'

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

describe file('/home/vagrant/.vimrc') do
  it { should be_file }
end
```

## テストの実行
* test/integration/default/spec/ を test/integration/default/serverspec/ にリネームする
* qiita0611/ ディレクトリまで戻り、テストコマンドを実行

```bash
$ kitchen test

....
Finished in 0.08399 seconds       
2 examples, 0 failures       
       Finished verifying <default-ubuntu-1204> (0m1.42s).
-----> Destroying <default-ubuntu-1204>...
       [default] Forcing shutdown of VM...
       [default] Destroying VM and associated drives...
       Vagrant instance <default-ubuntu-1204> destroyed.
       Finished destroying <default-ubuntu-1204> (0m2.45s).
       Finished testing <default-ubuntu-1204> (2m28.58s).
-----> Kitchen is finished. (2m28.85s)
```

# 作成したサンプル
* https://github.com/kasaharu/sample_for_testkitchen

# 参考
* [Chef](http://www.getchef.com/chef/)
* [Kitchen CI](http://kitchen.ci/)
* [Serverspec](http://serverspec.org/)
* Chef 実践入門