今更ですがChefとVagrantを勉強してみた

# はじめに
今更ですが、サーバ構成(プロビジョニング)手順を自動化するプロビジョニングフレームワークChefを使ってみたという話。
ですので、本記事の対象読者はChefとVagrantの初心者です。
本記事を読むと、

* Chef/Vagrantとは何か？について
* 作成したクックブックの対象ノードへの適用方法
* serverspecを使ったサーバ構築プロセスのテスト方法

を学ぶことができる(と思う)。

# Chefとは
Opscodeによって開発されているRubyで書かれたOSS。 **レシピ** と呼ばれるファイルに記述した設定内容に応じてユーザの作成やパッケージのインストールなどを自動的に行ってくれる。

# Vagrantとは
簡単なコマンドで任意の構成の仮想マシンを自動的に構築してくれるツール。


# 準備
Chefの検証環境構築に必要なツールを準備する。

## VirtualBoxのインストール
[ここ](https://www.virtualbox.org/wiki/Downloads)から使用しているOSのパッケージをダウンロードして、インストーラの指示に従ってインストールを行う。

## Vagrantのインストール
[ここ](http://downloads.vagrantup.com/)からダウンロードして、インストーラの指示に従ってインストールを行う。

# 仮想イメージの取得
初めてVagrantを利用する場合、利用したいOSのイメージ( **box** )を取得する必要がある。boxは[ここ](http://www.vagrantbox.es)から取得する。例えばubuntuというラベルでイメージを取得するにはターミナルで下記コマンドを入力する。

```
$ vagrant box add ubuntu http://cloud-images.ubuntu.com/vagrant/quantal/current/quantal-server-cloudimg-amd64-vagrant-disk1.box
```

イメージの取得が完了すれば、準備は終わり。

# 仮想サーバの起動/停止/破棄
適当な作業ディレクトリを用意して

```
$ vagrant init ubuntu
```

を実行し、Vagrantファイルを生成する。続いて

```
$ vagrant up
```

を実行して、仮想サーバを起動する。仮想サーバが起動したら

```
$ vagrant ssh
```

でsshログインする。仮想サーバを停止させたい場合は

```
$ vagrant halt
```

仮想サーバを破棄したい場合は

```
$ vagrant destroy
```

を実行する。haltでは次回仮想サーバ起動時にイメージの内容はhalt時点のものに復元されるが、destroyはまっさらな状態となる。

# SSHの設定

```
$ vagrant ssh-config --host HOSTNAME >> ~/.ssh/config
```

を実行すると、```ssh HOSTNAME```でログインできる様になる。

# knife-soloでChefを実行する
sshログインしてchef-soloを直接実行する方法には、

* いちいちsshログインするのは面倒
* リモート編集では使い慣れたエディタが使えない

といった煩わしさがある。
そこで、knifeのプラグインであるknife-soloを使うとローカル環境で編集したクックブックをrsyncで **ノード** (構成管理対象のホスト/サーバのこと)に転送してchef-soloを実行できる。

## knife-soloのインストール
下記GemfileをVagrantファイルと同じディレクトリに作成する。

```Gemfile
source 'https://rubygems.org'
gem "knife-solo", "~> 0.3.0.pre3"
```

作成したら 

```
$ bundle
```

でインストールする。インストールが完了したら下記コマンドを実行してknifeの初期化を行う。

```
$ bundle exec knife configure
```

## knife-soloでChefリポジトリを作る
Gemfileのあるディレクトリで下記コマンドを実行し、Chef用ディレクトリを作成する。

```
$ bundle exec knife solo init chef-repo
```

## knife-solo prepareでノードにChef Soloを入れる
* 対象ノードにChef Soloが入っていない
* 対象ノードにChef Soloが入っているが、バージョンが古いためバージョンアップしたい

場合は下記コマンドを実行する(NODENAMEには自分のノード名を指定する)。

```
$ bundle exec knife solo prepare NODENAME
```

knife solo prepareはChefのインストールの他、knife-solo用のrsync関連の設定も調整してくれるらしいので、一度実行すると良いと思う。

## クックブックの作成
chef-repoディレクトリへ移動し、下記コマンドを実行して新規クックブックを作成する。

```
$ bundle exec knife cookbook create nginx -o site-cookbooks
```

続いて下記ファイルをchef-repo/site-cookbooks/nginx/に作成する。

```/chef-repo/site-cookbooks/nginx/default.rb
#
# Cookbook Name:: nginx
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
package "nginx" do
    action :install
end
```

続いて対象ノードに今作成したクックブック(nginx)を適用させる命令を、設定ファイル( **Nodeオブジェクト** )に書く。設定ファイルは/chef-repo/nodes/NODENAME.jsonで(NODENAMEは自分が指定したノード名)、内容は下記の通り。

```/chef-repo/nodes/NODENAME.json
{
    "run_list":[
        "recipe[nginx]"
    ]   
}
```

以上の作業が完了したら、下記コマンドを実行してクックブックを対象ノードに適用させる(2200の部分は/.ssh/configに記載されているPort番号を指定する)。

```
$ bundle exec knife solo cook -i ~/.vagrant.d/insecure_private_key -p 2200 vagrant@127.0.0.1
```

上記コマンドの実行が完了したら、対象ノードへsshログインし、

```
$ rpm -qa | grep nginx
```

で実際にnginxがインストールされていることを確認できればOK。

## 補足

### リソース
default.rbで書いたpackageは、リソースと呼ばれる(ちなみにactionは属性と呼ばれる)。他にどんなリソースがあるのか知りたい方は[ここ](http://docs.opscode.com/resource.html)を参照。

### クックブック
今回はクックブックを自作したが、[ここ](http://community.opscode.com/)に他の方が作成したクックブックが公開されているので、それをダウンロードして利用することも可能。

# serverspecによるテスト
Chefを適用したあと、正しく変更が反映されたかどうかを目視するのは大変。ということで、serverspecという、サーバテストフレームワークを使って変更が反映されたかどうかの確認を自動化しよう。

## serverspecとは
サーバ構成のテストを行うRuby製フレームワーク。テスト自体はRSpecを使って書く。
serverspecはChefやその他プロビジョニングフレームワークから独立しているため、Chefを使っていない環境のテストのテストにもChefで構成されている環境にも使えるというメリットがある(つまり、将来Chefで構成されたサーバを別のフレームワークで構成し直したい(またはその逆の)場合、テストコードを修正する必要はなく、そのまま流用できる)。

## 準備

### 検証用サーバの用意
任意のディレクトリに移動し、検証用サーバの初期化・起動を行う。

```
$ vagrant init ubuntu
$ vagrant up
```

任意のホスト名も設定しておく(ここではexampleとする)。

### serverspecのインストール
gemでインストールできる。serverspecはssh経由で対象ホスト(今回はexample)にログインしてテストを行うツールなので、 **Vagrantで起動したホストではなく、ローカル環境にインストールする** 必要がある。
下記Gemfileを作成し```$ bundle```でインストールする。

```Gemfile
source 'https://rubygems.org'
gem 'serverspec'
gem 'rake'
```

インストールが完了したら

```
$ bundle exec serverspec-init
```

でテストの初期化を行う。その際いくつか質問されるが、

* backend typeはSSH
* Vagrant instanceはno(ホスト名にexampleを指定)

を選択する。
全質問に回答を終えると、spec/example/にhttpd_spec.rbが生成される。

### テストの実行
下記コマンドを実行してテストを実行する(検証用サーバは起動したばかりで、Apacheがインストールされていないため、テストは失敗する)。

```
$ bundle exec rake spec
```

### テストを通す
exampleにsshログインして、Apacheインストール・有効化・ServerNameの置き換え・Apacheの起動を行い、再度テストを実行すると、テストが通る(はず)。