初めての Vagrant + Chef zero + Berkshelf 

# はじめに
Macでローカルのテスト環境を自動構築したいなーと思い、手を出してみました。
初心者なので、できる限りシンプルな構成で、とりあえず試してみることにします。

# 事前準備

## まずはインストール
- VirtualBox：VirtualBox 4.3.26  
[Download VirtualBox](https://www.virtualbox.org/wiki/Downloads)
- Vagrant：Vagrant 1.7.2  
[Download Vagrant](http://www.vagrantup.com/download-archive/v1.7.2.html)
- Chef：Chef Development Kit Version: 0.4.0  
[Download Chef](https://downloads.chef.io/chef-dk/mac/#/0.4.0)

## 必要な Vagrant プラグインをインストールしておく
```
$ vagrant plugin install vagrant-omnibus
$ vagrant plugin install vagrant-chef-zero
$ vagrant plugin install vagrant-vbox-snapshot
```

- vagrant-omnibus：ゲスト OS に Chef をインストールするために必要なプラグイン
- vagrant-chef-zero：Vagrant から Chef Zero Server を起動するためのプラグイン
- vagrant-vbox-snapshot：Vagrant のスナップショットを管理するプラグイン

# 構築してみる

### VagrantFile の作成

まずは、ひな形を作成します。
今回は、短時間で仮想環境を作りたいので、Box と呼ばれる仮想マシンのベースイメージの CentOS6.4 を使うことにしました。

```bash
$ mkdir test_centos64
$ cd test_centos64
$ vagrant init test_centos64 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20131103.box
```

そうすると Vagrantfile が作成され、中身はこんな感じになります。

```vim:Vagrantfile
Vagrant.configure(2) do |config|
  ・・・
  config.vm.box = "test_centos64"
  config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20131103.box"
  ・・・
end
```
- config.vm.box：自分の box の名前として box の内容が分かるような文字列を指定します。
- config.vm.box_url：Box の URL を指定します。  
参照：http://www.vagrantbox.es/

これらの記述があると、ローカルの Box に"test_centos64"という Box がある場合、ローカルの Box が使われ、存在しない場合は、指定した URL から Box をダウンロードし、ローカルに配置してくれます。

続いて、Vagrantfile にプライベートネットワークの設定します

```vim:Vagrantfile
Vagrant.configure(2) do |config|
  ・・・
  config.vm.network :private_network, ip:"192.168.33.20"
  ・・・
end
```
これは別に設定しなくてもいいけど、把握しておきたいので、適当に。

ここまできたら、一度仮想マシンが起動できるか確かめてみます。

```bash
$ vagrant up
```

起動できたら、ゲスト OS に接続してみます

```bash
$ vagrant ssh
```

無事に接続OK！
問題なく接続できたら、ゲスト OS からログアウトしておきます。

### いろいろ試すためにも Vagrant のスナップショットとっておく

初期状態をスナップショット。これで困ったら初期状態に戻せます。

```bash
# スナップショット作成
$ vagrant snapshot take test_centos64-init
# 確認
$ vagrant snapshot list
```

### ゲスト OS にChefをインストール

Vagrantfile に最新バージョンをインストールするように下記設定を追加

```vim:Vagrantfile
Vagrant.configure(2) do |config|
  ・・・
  config.omnibus.chef_version=:latest
  ・・・
end
```

起動中のゲスト OS に provisioners を実行

```bash
$ vagrant provision
```

### Chef 設定ファイルの作成

とりあえずファイルの雛形を生成します。

```bash
$ chef generate repo chef-repo
$ tree
.
├── Vagrantfile
└── chef-repo
    ├── LICENSE
    ├── README.md
    ├── Rakefile
    ├── certificates
    │   └── README.md
    ├── chefignore
    ├── config
    │   └── rake.rb
    ├── cookbooks
    │   └── README.md
    ├── data_bags
    │   └── README.md
    ├── environments
    │   └── README.md
    └── roles
        └── README.md
```

### Berksfile を作成

Berkshelf を使って cookbook を管理するために Berksfile というファイルを新規に作成します。

```bash
$ cd chef-repo
$ vi Berksfile
```

今回はお試しということで、github 上に公開されている timezone を変更する cookbook を利用してみます。[^1]

```vi:Berksfile
source "https://supermarket.chef.io"

cookbook 'timezone-ii', git: "https://github.com/L2G/timezone-ii.git"
```

cookbooks 配下に cookbook をインストールします。

```bash
$ berks vendor cookbooks
$ cd ..
$ tree
.
├── Vagrantfile
└── chef-repo
    ├── Berksfile
    ├── Berksfile.lock
    ├── LICENSE
    ├── README.md
    ├── Rakefile
    ├── certificates
    │   └── README.md
    ├── chefignore
    ├── config
    │   └── rake.rb
    ├── cookbooks
    │   └── timezone-ii
    │       ├── Gemfile
    │       ├── Gemfile.lock
    │       ├── LICENSE
    │       ├── README.md
    │       ├── Thorfile
    │       ├── attributes
    │       │   └── default.rb
    │       ├── chefignore
    │       ├── files
    │       │   └── default
    │       │       └── tests
    │       │           └── minitest
    │       │               └── default_test.rb
    │       ├── metadata.json
    │       ├── recipes
    │       │   ├── amazon.rb
    │       │   ├── debian.rb
    │       │   ├── default.rb
    │       │   ├── fedora.rb
    │       │   ├── linux-generic.rb
    │       │   ├── pld.rb
    │       │   ├── rhel.rb
    │       │   └── rhel7.rb
    │       ├── spec
    │       │   ├── attributes_spec.rb
    │       │   ├── recipes
    │       │   │   ├── amazon_spec.rb
    │       │   │   ├── debian_spec.rb
    │       │   │   ├── default_spec.rb
    │       │   │   ├── fedora_spec.rb
    │       │   │   ├── linux-generic_spec.rb
    │       │   │   ├── pld_spec.rb
    │       │   │   ├── rhel7_spec.rb
    │       │   │   └── rhel_spec.rb
    │       │   └── spec_helper.rb
    │       ├── templates
    │       │   ├── amazon
    │       │   │   └── clock.erb
    │       │   ├── centos
    │       │   │   └── clock.erb
    │       │   ├── default
    │       │   │   └── timezone.conf.erb
    │       │   ├── pld
    │       │   │   └── timezone.conf.erb
    │       │   └── redhat
    │       │       └── clock.erb
    │       └── test
    │           └── integration
    │               ├── default
    │               │   └── bats
    │               │       ├── date_output.bats
    │               │       ├── debian.bats
    │               │       └── etc_localtime.bats
    │               └── helpers
    │                   └── bats
    │                       └── helper.bash
    ├── data_bags
    │   └── README.md
    ├── environments
    │   └── README.md
    └── roles
        └── README.md
```

### Vagrant と Chef を連携します

調べてみたけど、いまいちどう管理するのがベストプラクティスなのかわからない。
共通系の cookbook は roles で run_list しちゃう方針として作ってみる。

```bash
$ vi chef-repo/roles/common.json
```

```vim:common.json
{
  "name": "common",
  "chef_type": "role",
  "json_class": "Chef::Role",
  "default_attributes": {
    "tz": "Asia/Tokyo"
  },
  "run_list": [
    "recipe[timezone-ii::rhel]"
  ]
}
```
ただ、roles で run_list はアンチパターンに該当してしまうのかも。。。[^2]

Vagrantfile に下記を追加します

```bash
$ vi Vagrantfile
```

```vim:Vagrantfile
Vagrant.configure(2) do |config|
  ・・・
  config.vm.provision "chef_zero" do |chef|
    chef.cookbooks_path = "./chef-repo/cookbooks"
    chef.roles_path = "chef-repo/roles"
    chef.add_role "common"
  end
  ・・・
end
```

起動中のゲスト OS を再起動して provisioners を実行

```bash
$ rm .vagrant/machines/default/virtualbox/synced_folders
$ vagrant reload --provision
```
バグらしく、**通常の provisioners を実行だとエラーが発生してしまうので注意してください。**

これで、ゲスト OS 起動時に recipe が実行され、timezone が"Asia/Tokyo"に設定されます。

****
まだまだ初心者なので、
今後、自作 recipe を作ってみたり、knife 使ってみたり、クライアント/サーバー構成にしてみたり、最終的には Docker あたりにも手を出して、より理解を深めていければなと。

****
[^1]: 2015/03/20時点では、https://supermarket.chef.io には最新の timezone-ii がアップされていなくて、 **CentOs だと /etc/sysconfig/clock は更新されないみたいです。**

[^2]: [[和訳]ChefConf 2013: Beginner Chef Antipatterns](http://www.creationline.com/lab/3080)