The Environment Cookbook Patternに従ったCookbook作成記録



はじめに
======
ChefのCookbookを作成するにあたって，スケーラブルやメンテンナス性といった観点からどんなCookbookを作成すればいいか悩むことがある．
これの解決策として，[The Environment Cookbook Pattern](http://blog.vialstudios.com/the-environment-cookbook-pattern/)にしたがってCookbookを作成する方法があります．

この記事の和訳やより詳しくかかれているのに以下があります．

- [大規模にchefを使い倒すためのcookbook pattern](http://qiita.com/cazador/items/e0db714555f2f36d98c6)
- [Chef Cookbook の管理方針、The Environment Cookbook Pattern について](http://easyramble.com/the-environment-cookbook-pattern.html)

そしてすごくありがたいことに，２つ目の記事のなかに実際のCookbooksのStructureに関する件まで載せてくださっていました．

```tex:
environment-cookbook
    |_ .chef
       |_ knife.rb
       |_ cookbooks
       |_ environments
       |_ data_bags
       |_ roles
       |_ nodes
    |_ Berksfile
    |_ Berksfile.lock
    |_ metadata.rb
    |_ Vagrantfile
    |_ recipes
    |_ attributes
    |_ etc...
```

```ruby:knife.rb
cookbook_path    [".chef/cookbooks"]
node_path        ".chef/nodes"
role_path        ".chef/roles"
environment_path ".chef/environments"
data_bag_path    ".chef/data_bags"
#encrypted_data_bag_secret "data_bag_key"

knife[:berkshelf_path] = ".chef/cookbooks"
```

でも，実際にこれらのパターンを実際に実装してみたという記事が見つけられませんでした.
なので今回は，これらを元に実際にnginxをインストールするCookbookを作成したので，記録がてら残しておきます．

環境
====
- Chef development Kit version 0.6.0
- Vagrant 1.7.2

前提
===
- The Environment Cookbook Patternについて知っている
	- 詳しくは上記のURLを見てください 

実際にやったこと
=============
流れとしては
1. supermarketにあるcookbookをwrapするcookbookの作成
2. environment cookbookを作成する
3. environment cookbookにberksを用いてvendor(install)し，cookする
となっております．

今回は，nginx cookbookをwrapするcookbookを作成し(templateファイルを追加するためにwrap)，それを適用してみました．

このcookbookの作成がめんどくさい方は，[Github](https://github.com/Kztka/test-nginx)に以下の作業でつくれるcookbookがあるので適当にRepository URLだけコピーして飛ばしてください．

```sh:shell
$ knife cookbook create test-nginx -o .
$ cd test-nginx
$ vim metadata.rb  # 依存関係はきっちり明記しとく
$ vim templates/default/test.erb  # 適当に各自環境に合わせてください
$ vim recipes/test.erb
$ git remote add origin [Repository URL]
$ git push -u origin master
```

```ruby:metadata.rb
depends 'nginx'  # metadataに書いておくと個別にインストールしなくても勝手にやってくれるので便利
```

```ruby:templates/default/test.erb
server {
    listen 80 default_server;

    root /usr/share/nginx/www;
    index index.html index.htm;

    server_name localhost;

    location / {
        #try_files $uri $uri/ /index.html;
        proxy_pass http://localhost:3000;
    }
}
```

```ruby:recipes/test.erb
# recipes
include_recipe 'nginx::default'

# for nginx
template "/etc/nginx/sites-available/test" do
    source "test.erb"
    owner "root"
    group "root"
    mode 0644
end

link "/etc/nginx/sites-enabled/test" do
    to "/etc/nginx/sites-available/test"
    notifies :reload, "service[nginx]"
end

```
以上でwrap用のcookbookが完成しました．次にこれをberkshelf使ってEnvironment Cookbookにインストールし，cookしていきたいと思います．
---
ちなみにですが，一般的にChefでcookbookを作成するには，まずChef Repositoryを作成します．しかし，「The Environment Cookbook Pattern」に従った方法だとおそらくですが，Chef Repository = Environment Cookbookとなるので，いきなりCookbookの作成から行います([参考](https://github.com/berkshelf/berkshelf/issues/535#issuecomment-32687054))．
knife solo init, berks init, vagrant initしたtemplateは，[Github](https://github.com/Kztka/environment-cookbook)に用意してます．フォルダ作成がめんどくさい方はこちらをお使いください

```sh:shell
$ knife cookbook create project-cookbook -o .
$ cd project-cookbook
$ knife solo init .
$ mv cookbooks environments data_bags nodes roles .chef  # 上の方の構成と同じにする
$ mkdir .chef/vendor
$ rm -r site-cookbooks
$ vim .chef/knife.rb

# berkshelf の設定
$ berks init  # 両方 Yes
$ rm Vagrantfile
$ bundle install --path=.chef/vendor/bundle
$ vim Berksfile
$ berks vendor .chef/cookbooks  # 先ほどのcookbookをインストールします

# Vagrant の設定
$ vim Vagrantfile
$ vagrant up
$ vagrant ssh-config >> ~/.ssh/config
$ vagrant plugin install vagrant-vbox-snapshot
$ vagrant snapshot take initial
# $ vagrant snapshot back で一番最後にsnapshot takeしたとこまでに戻せます

# knife-soloで実行
$ vim recipes/default.rb
$ knife solo prepare default
$ knife node run_list add default 'recipe[project-cookbook]' -z
$ knife solo cook default
```

```ruby:.chef/knife.rb
cookbook_path    [".chef/cookbooks"]
node_path        ".chef/nodes"
role_path        ".chef/roles"
environment_path ".chef/environments"
data_bag_path    ".chef/data_bags"
#encrypted_data_bag_secret "data_bag_key"

knife[:berkshelf_path] = ".chef/cookbooks"
```

```rbenv:Berksfile
cookbook 'test-nginx', git:'[Repository URL]'
```

```vagrant:Vagrantfile
config.vm.box = "Debian_7.8.0"  # 環境に合わせてください
config.vm.box_url = "https://github.com/kraksoft/vagrant-box-debian/releases/download/7.8.0/debian-7.8.0-amd64.box"
config.vm.network "private_network", ip: "192.168.33.10"
```

```ruby:recipes/default.rb
include_recipe [test-nginx]
```
knife-zeroで，実行する場合は，「knife-soloで実行」以降の部分を[Knife-ZeroとVagrant(単品VM)](http://qiita.com/sawanoboly/items/ae3c96734c5cee72863c)を参考にして実行してください

以上で，完了です．この方法が，Environment Cookbook Patternの方法だと思います．スケーラブルやメンテンナス性はまだまだ触ってないのでわかりませんが，他環境への流用が簡単な気がする

knife zeroについて
======================
chef local_modeをリモートで自動実行するためにknife-zeroがリリースされています([knife-soloとknife-zeroの違い](http://www.creationline.com/lab/6401))．
~~knife-zeroに移行してもよかったのですが， この記事を書いている段階でknife zero chef_client(knife-soloでいうcook)がvagrantだとうまく動かないのと(knife zero bootstrapは問題なく動くのでrecipeの適用はできます)，~~ knife-solo自体もchef local_modeに対応している([参考](https://github.com/matschaffer/knife-solo/pull/385)) ~~等の理由から見送っています．~~
~~knife-zeroだと，node run_list addができます~~(←は関係なかった)

追記:vagrantに対してknife-zeroを適用する方法を[@sawanoboly](http://qiita.com/sawanoboly)さんが解説してくださりました．ありがとうございます！
→[Knife-ZeroとVagrant(単品VM)](http://qiita.com/sawanoboly/items/ae3c96734c5cee72863c)
うん，便利！
追記:コマンドの順番の変更