Chefを読んで実行するための全知識

このドキュメントでは、Chefを実行して、インフラを作成したい人が、既存のレシピがあるのを前提に、Chefの概要を理解するためのドキュメントです。Chef-soloの構成のみに対応した記述になっています。理解が間違えているところとかあればご指摘ください。


1. Chefの概要
---

### 1.1. Chefとは

シェフは、インフラストラクチャーをコードに変換するための自動化プラットフォームです。仮想環境でも、物理環境でも、クラウドでも使う事ができます。インフラストラクチャを自動化することで、プロダクトのマーケット投入を早めたり、スケールや複雑さに対応したり、システムを安全に保ちます。

### 1.2. Chefの仕組み

Chefはサーバーをセットアップして、希望の状態にするための「クックブック」「ノードオブジェクト」というDSL(設定ファイルっぽいもの)をローカルのワークステーションで作成します。それらのDSLをローカルのバーチャルマシンや、クラウド、物理サーバーに反映させると、サーバーのセットアップが完了します。

![Chefの全体像](http://www.getchef.com/images/chart-basic-installation.jpg)


### 1.3. Chefで使われる用語

Chefで使われる代表的な用語は次のような物です。chefはServer-Client形式のものと、単独で使うものがありますが、本ドキュメントでは後者を取り扱います。

#### 1.3.1. cookbookとreceipe

 cookbookというのは、お料理本の意味で、複数のreceipe(レシピ)を含みます。一つのcookbookが一つの料理の料理方法が載っています。例えばカレーです。receipeは、カレーでも、様々なカレーがあるので、そのカレーの種類だけ作り方があり、それがreceipeにまとまっているイメージです。
 　技術的にいうと、MySQLとかApacheとか毎にcookbookがあり、その中にいくつかreceipeがあります。例えばMySQLのMasterとか、Slaveとかです。具体的なreceipeを適用すると、そこで設定されたとおりに、実際のサーバーが出来上がります.receipeには、こうなってほしいという望まれるサーバーの状態が定義されています。

#### 1.3.2. knife
 
 chefは料理の例えになっています。料理をするには包丁とかが必要になります。[knife](http://docs.opscode.com/chef/knife.html)は、実際にcookbookをサーバーに適用する時に使うコマンドです。

### 1.4. Chefの考え方

#### 1.4.1. Immutable Infrastructure 

最近は仮想化の発達により、クラウドや、仮想マシンにより、カンタンにサーバーのインスタンスを作ったり、壊したりということができるようになりました。また、アプリケーションのインストール等も従来は、手で手順を作って、その手順に従ってコマンドを打って環境を作っていました。最近は、サーバーへのインストールや設定、そしてインスタンスの起動や停止といったことまで「プログラム」で実行できるようになりました。この動きをInfrastracture as a codeといいます。この流れで出てきた考えがImmutable infrastructureという考えです。通常のサーバーは一度セットアップのあとは、ライブラリのバージョンアップ、設定変更にあわせて、サーバーで操作を行い、サーバーの状態を変更してきました。ところが、こういう事をすると、サーバーにより、差異がでてきたり、インストールしているパッケージの整合性が取れなくなったり、挙動がおかしくなったり、操作ミスしたりといったようにいろいろな問題が発生してきます。

　ですので、一度作成したサーバーのレシピは、サーバー側では設定変更を行わず、あくまで、Chefのようなプロビジョニングツールの方で、レシピ等を変更するようにします。つまり、サーバーの状態はいつも同じ（不変）な状態をキープできるようになります。そうすると、どこでも、何回セットアップしたとしても、まったく同じ状態のサーバーがでてきます。
　
　ポイントは、サーバー側で設定変更等をしないことです。あくまでレシピを変更しましょう。
　
#### 1.4.2. 冪等性

レシピを作るときには、冪等性という性質をもっている事が重要視されます。冪等性は、Chefにおいては、何回レシピを適用しても、結果（サーバーの状態）が同じになるような性質です。例えば、最初にApacheのレシピを作ったときに、まず、Aapcheをインストールするところまで作ったとして、レシピを流してサーバーを作りました。次に、設定ファイルの変更をレシピに追加して、再度レシピを同じサーバーに流れた時にも、１からインスタンスを上げて同じレシピを流した時でも同じ結果が得られるような作りにしましょうということです。　

#### 1.4.3. 捨てやすくすること

　Chefのようなツールがあれば、サーバーがおかしくなったらいつでも、それを捨てて、すぐにクラウドでインスタンスを立ち上げて、Chefを流して、セットアップするというのが非常に短時間でできます。
　だから、一度作成したインスタンスをずっと使おうという発想ではなくて、今まで使っていたインスタンスはいつでも捨ててChefを流したら同じ状態になるような設計にしましょう。

2. Chefの実行方法
---

### 2.1. chefのインストール

次のコマンドもしくは、Gemfileに記述しましょう

```
% gem install chef
% gem install knife-solo 
```

尚、このGemが使える前提条件は次の通りです。

+ Unix, Linux, MacOSX, MS Windows
+ MacOSXの場合はXCodeをインストールする
+ githubのアカウントが必要なのでgitもインストールしておく
+ サーバーにアクセスできるようにする(SSHで)
+ サーバーのドメイン名か、IPAddressが事前にあること

[Install Chef 11.x on Workspation](http://docs.opscode.com/install_workstation.html)

### 2.2. 初期セットアップ

#### 2.2.1. リポジトリの設定

chefのファイルは、GitHub等のレポジトリで管理するのが望ましいでしょう。１つのプロジェクトに対して１つのchef用リポジトリというレベルの粒度で作成しましょう。


knifeコマンドを使ってリポジトリを初期化します。knifeの設定ファイル(~/.chef/knife.rb)等が作成されます。

```
% knife solo init chef
```

### 2.3. ディレクトリの構造

適当ですが、私のプロジェクトのchefディレクトリ以下は次のような構成になっています。

```
$ tree -L 2
.
|-- Berksfile
|-- Berksfile.lock
|-- Gemfile
|-- Gemfile.lock
|-- cookbooks
|   |-- Berksfile.lock
|   |-- apache2
|   |-- apt
|   |-- aws
|   |-- build-essential
|   |-- chef_handler
|   |-- database
|   |-- homebrew
|   |-- iptables
|   |-- logrotate
|   |-- mysql
|   |-- openssl
|   |-- pacman
|   |-- postgresql
|   |-- windows
|   |-- xfs
|   `-- yum
|-- data_bags
|-- environments
|-- nodes
|   |-- vagrant.json
|   `-- mysql01.json
|-- roles
|   |-- all_in_one.json
|   |-- app_server.json
|   |-- mysql_master_server.json
|   |-- mysql_slave_server.json
|   `-- varnish_server.json
|-- site-cookbooks
|   |-- info-base
|   |-- mysql55
|   |-- mysql56
|   `-- varnish
`-- vendor
    `-- bundle
```

#### 2.3.1. 一般的なディレクトリ

各ディレクトリについて説明していきます。

| ディレクトリ     | 名称 |  
|:-----------|------------:|
| cookbooks     | インターネットから取得したcookbookが複数格納されます |    
| data_bags     | ドメインデータが格納されます |   
| environments       | staging/production等環境固有の状態が格納されます |  
| nodes         | 個別のノードの状態が格納されます |  
| roles         | 役割(web-server/db-server等)の状態が格納されます |    
| site-cookbooks         | 自作のcookbookが作成されます |      


##### 2.3.1.1. cookbooks

 インターネットから取得したcookbookが格納されます。このセクションは自分で触らない方がよいと思います。中身を読むだけにしましょう。Berksというツールを使って、このcookbookを取得します。[Opscode Community](http://community.opscode.com/cookbooks)に存在するcookbookを取得して使う事ができます。
 
 cookbookの具体的な中身に関してはこの後解説いたします。
 
##### 2.3.1.2. data_bags
 
 このセクションには、OSでセットアップするユーザ等、OSセットアップに関する「ドメイン」のデータを格納します。
 具体的にはSSHの鍵のデータもここで保持されたりします。
 
##### 2.3.1.3. environments

 staging/production/develop等の環境の違いに関する設定が記述されます。
 
##### 2.3.1.4. nodes

　個別の具体的なnodeに対する設定や状態が記述されます。
 
##### 2.3.1.5. roles

 web-server, db-server等、サーバーの種類別の設定や状態が記述されます。
 
##### 2.3.1.6. site-cookbooks

 cookbooks以外の、自分たちで作ったcookbookをここに格納します。 


```
$ tree -L 3

|-- chef
|   |-- Berksfile
|   |-- Berksfile.lock
|   |-- Gemfile
|   |-- Gemfile.lock
|   |-- cookbooks
|   |   |-- Berksfile.lock
|   |   |-- apache2
|   |   |-- apt
|   |   |-- aws
|   |   |-- build-essential
|   |   |-- chef_handler
|   |   |-- database
|   |   |-- homebrew
|   |   |-- iptables
|   |   |-- logrotate
|   |   |-- mysql
|   |   |-- openssl
|   |   |-- pacman
|   |   |-- postgresql
|   |   |-- windows
|   |   |-- xfs
|   |   `-- yum
|   |-- data_bags
|   |-- environments
|   |-- nodes
|   |   |-- vagrant.json
|   |   |-- info_ec2.json
|   |   `-- mysql01.json
|   |-- roles
|   |   |-- all_in_one.json
|   |   |-- app_server.json
|   |   |-- mysql_master_server.json
|   |   |-- mysql_slave_server.json
|   |   `-- varnish_server.json
|   |-- site-cookbooks
|   |   |-- info-base
|   |   |-- mysql55
|   |   |-- mysql56
|   |   `-- varnish
|   `-- vendor
|       `-- bundle
```


### 2.3. Chefのファイルの読み方

chefの設定を読み解くためには、nodesディレクトリにある、nodeオブジェクトファイル(jsonファイル)からたどってみるのがいいと思います。

```
$ tree -L 2
.
|-- Berksfile
|-- Berksfile.lock
|-- Gemfile
|-- Gemfile.lock
|-- cookbooks
|   |-- Berksfile.lock
|   |-- apache2
|   |-- apt
|   |-- aws
|   |-- build-essential
|   |-- chef_handler
|   |-- database
|   |-- homebrew
|   |-- iptables
|   |-- logrotate
|   |-- mysql
|   |-- openssl
|   |-- pacman
|   |-- postgresql
|   |-- windows
|   |-- xfs
|   `-- yum
|-- data_bags
|-- environments
|-- nodes
|   |-- vagrant.json
|   `-- mysql01.json
|-- roles
|   |-- all_in_one.json
|   |-- app_server.json
|   |-- mysql_master_server.json
|   |-- mysql_slave_server.json
|   `-- varnish_server.json
|-- site-cookbooks
|   |-- info-base
|   |-- mysql55
|   |-- mysql56
|   `-- varnish
`-- vendor
    `-- bundle
```
#### 2.3.1. nodes/nodeファイル
ちなみに、nodes/roles/environmentのディレクトリのファイルは、書き方は同じになっています。これらは設定を整理するために分割されているだけです。後述の特定のルールによって設定が上書きされます。例えば今のnodeファイルを見てみましょう。

これは、rolesにあるall_in_oneというのを実行(run_list)する事だけが示されています。


```
# vagrant.json

{"run_list":["role[all_in_one]"]}
```

じゃあ、次にRoleのall_in_one.jsonを見てみます。

#### 2.3.2. rolesのファイル

文法は先ほどのものと同じです。
こちらのファイルはいろいろ定義されていますね。このroleは、Vagrantで作ったローカルの仮想マシンに、全てのプロダクトをぶち込むファイルになっています。
読むポイントは次の要素です。

| ディレクトリ     | 名称 |  
|:-----------|------------:|
| default_attributes     | レシピの設定内容を定義します |    
| description     | ファイルの目的を記述します |   
| run_list       | 実行するレシピを順に記述します |  
| override_attributes         | レシピの設定内容を強制的に上書きします |   


```
{
    "name": "all_in_one",
    "chef_type": "role",
    "json_class": "Chef::Role",
    "default_attributes": {
        "mysql": {
            "server_root_password" : "password",
            "server_repl_password" : "password",
            "server_debian_password" : "password"
        },
        "varnish": {
            "backend_host" : "127.0.0.1",
            "backend_port" : "8080",
            "listen_port" : "80"
        }
    },
    "description": "For vargrant environment. All in one package",
    "run_list": [
        "base::packages",
        "info-base",
        "ruby",
        "ruby::passenger",
        "apache2",
        "supervisor",
        "varnish",
        "yum::remi",
        "mysql::server"
    ],
    "env_run_lists" : {},
    "override_attributes": {
        "yum" : {
            "remi" : {
                "includepkgs" : "compat-mysql* mysql*"
            }
        },
        "apache": {
            "listen_ports" : ["8080"]
        }
    }
}
```

基本的に大きなポイントはrun_listと、attributesの設定です。run_listは、実行するレシピ、attributeは、サーバーの設定ファイルに書かれるような設定値を定義します。この設定値は、cookbook/role/environment/nodeで定義されたものが、下記のルールの従って上書きされます。最終的な上書きされた結果がサーバーに反映されます。多分サーバーを作る時は、cookbookには、デフォルトの値が書かれて、roleで、webサーバー等のサーバー種別毎に決まった値が設定されます。environmentには、staging/production毎に違うような値のみを記述します。最終的にnodeでnode毎に違う情報（ホスト名等）のみを上書きします。こうしておくと、設定値がDRYになるのでオススメです。

この上書きルールですが、下記の図がわかりやすいかと思います。

![](http://docs.opscode.com/_images/overview_chef_attributes_precedence.png)
![](http://docs.opscode.com/_images/overview_chef_attributes_table.png)

具体的には例えば次のページにかいてあります。

[About Roles](http://docs.opscode.com/essentials_roles.html)

他にもenvironment等も設定される可能性がありますので、書き方が同じなので省略します。次に、具体的なcookbookを見てみましょう。単純なのがよいので、私の作成したvarnishのモノを見てみましょう。

#### 2.3.3. cookbookの読み方

cookbookを自分で作りたい時は次のコマンドで実行します。



```
$ knife cookbook create varnish -o site-cookbooks
```

site-cookbooksのvarnishディレクトリに行くと次のようなファイルができています。読む側とすると重要なのは、attributes, receipes, templatesの３つです。それぞれ次のような意味です。


| ディレクトリ     | 名称 |  
|:-----------|------------:|
| attributes     | デフォルトの設定情報を定義します |    
| files     | アップロードしたいファイルを格納します |   
| receipes       | レシピの実行内容を記述します |  
| templates         | 設定ファイルのテンプレートを記述します |  


```
$ tree
.
├── CHANGELOG.md
├── README.md
├── attributes
│   └── default.rb
├── definitions
├── files
│   └── default
├── libraries
├── metadata.rb
├── providers
├── recipes
│   └── default.rb
├── resources
└── templates
    └── default
        ├── default.vcl.erb
        └── varnish.erb
```

最初に読みたいのは、receipe/default.rbです。cookbookがrun_listで実行されたらまずコレが呼ばれます。細かい事は説明しませんが、書いてあることだけを説明します。

```
remote_file "/tmp/#{node['varnish']['file_name']}" do
  source "#{node['varnish']['remote_uri']}"
end


rpm_package node['varnish']['rpm_package_name'] do
    action :install
    options "--nosignature"
    provider Chef::Provider::Package::Rpm
   source "/tmp/#{node['varnish']['file_name']}"
end

package "varnish" do
  action :install
end

template "/etc/varnish/default.vcl" do
  source "default.vcl.erb"
  mode  644
  owner "root"
  group "root"
  notifies :restart, "service[varnish]"
  action :create
end

template "/etc/sysconfig/varnish" do
  source "varnish.erb"
  mode  644
  owner "root"
  group "root"
  notifies :restart, "service[varnish]"
  action :create
end

service "varnish" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end
```

ちなみにvarnishのインストール手順 (CentOS)はこんな感じです

```
% rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm
% yum install varnish
```
これは、インストールだけですが、設定ファイルとして、/etc/varnish/default.vclと、/etc/sysconfig/varnishを設定する必要があります。

尚、package等のアクションは、"chef package"などでgoogleすれば、一撃でリファレンスがでてきます。
例えばここです [package - Chef docs](http://docs.opscode.com/resource_package.html)


##### 2.3.3.1. remote_file

リモートからファイルをとってきて格納するための記述です

##### 2.3.3.2. rpm_package

 これは、rpmコマンドに相当する記述です。

##### 2.3.3.3. package

 yum, apt等のパッケージインストールのためのコマンドです。OSによって発行されるコマンドが異なります。
 
##### 2.3.3.4. template

 設定ファイルを作成したり、書き換えたりします。notifiesという設定をすると、このファイルが書き換えられた後のアクションが記述できます。ここでは、varnishの設定ファイルが書き換えられたら再起動になっています。
 
##### 2.3.3.5. service

 サービス化と自動起動の設定をします。
 
##### 2.3.3.6 bash

 この他によく使われる物としてbashというアクションがあります。これは、サーバー側でコマンドを発行するためのものです。出来るだけbashを使わないようにします。というのも、他のアクションは、冪等性が考慮されているので、何回実行しても同じ結果になるようになっています。ところが自分でこのbashを作ると、冪等性のコーディングをしないといけませんし、OS毎の挙動の違いも場合によっては自分で書かないといけません。これは面倒です。
 
##### attributes

　さて、default.rbでは、node['varnish']['file_name']といったような記述が見受けられます。これらの変数の具体的な定義がattributesに書かれています。この値はRole/Environment/Nodeで上書きされる可能性があります。
　
```
default['varnish']['versions']   = "3.0-1.el6"
default['varnish']['file_name']  = "varnish-release-3.0-1.el6.noarch.rpm"
default['varnish']['remote_uri'] = "http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm"
default['varnish']['rpm_package_name'] = "varnish-release"
default['varnish']['backend_host'] = "127.0.0.1"
default['varnish']['backend_port'] = "80"
default['varnish']['listen_port'] = "6081"
```
##### templates

 templateアクションで設定ファイルを作る事を選択した場合は、templatesディレクトリの下に定義ファイルを作りましょう。上記のattributesで設定した値を使う事ができます
 
 default.vcl.erb
 
 ```
 backend default {
.host = "<%= node['varnish']['backend_host']%>";
.port = "<%= node['varnish']['backend_port']%>";
}

acl backend_apps { "127.0.0.1";}

sub vcl_recv {
   if (client.ip ~ backend_apps && req.http.X-REFRESH) {
     set req.hash_always_miss = true;
   }
}
 
 ```

他のcookbook等もこれで一通り読み込むことができます。

### 2.4. Chefの実行 

さて、Chefのnodeオブジェクトを実行して、実際にサーバーを望ましい状態にしてみましょう。

#### 2.4.1. Berksfile

そのまえに、まず、プロジェクトで使うcookbookを取得してみましょう。自分で作るもの以外は既存のものを使用して、attribute部分のみをrole/environment/nodeで書き換えることができます。Berksは、rubyのGemfileのcookbook版みたいな感じです。ここに欲しいcookbookを書いておきましょう。

```
site :opscode
cookbook 'mysql', '~> 4.0.20'
cookbook 'database'
cookbook 'apache2'
cookbook 'yum'

```

そして、実行します。

[Berkshelf](http://berkshelf.com/)

```
% gem install berkshelf
% berks install
```
berkshelfのgemはGemfileに書いておくといいでしょう。これによりcookbooksディレクトリ以下にcookbookが取得されます。

#### 2.4.2. レシピの実行

さて、遂にレシピを実行する時がきました。読み込んで理解したレシピを流してみましょう。
ここでは、解説しませんが、Vagrant等をつかって、仮想マシンを用意しておきましょう。


##### 2.4.2.1. 事前設定

レシピを流したいサーバーに、`% ssh server_name` でログイン出来るように設定しておきましょう。[vargrant](http://www.vagrantup.com/)を使っていない場合は必要ありませんが、vargantの環境の場合は次のようなコマンドが有効です。sshの設定は周りの人に聞いてみてください。

```
% vagrant ssh-config --host server_name >> ~/.ssh/config
```

##### 2.4.2.2. Why-Run

実際にレシピを流す前に、サーバーにレシピを流したら一体どうなるか？(Why-Run)を確認するコマンドがあります。実行はnode以下のファイルを指定するといいでしょう。-WオプションがWhy-Runのオプションです。これを実行すると、サーバーで実行される予定の内容がプリントされます。もしエラーが発生したら、上記のファイルを修正します。

```
bundle exec knife solo cook server_name nodes/vagrant.json -W
```

##### 2.4.2.3. knife-solo

[knife solo](http://docs.opscode.com/chef/knife.html)コマンドのサブコマンドcookで実際にサーバーをレシピの状態に反映させてみます。Why-Runで動作確認したら、-Wオプションをとってコマンド実行しましょう。 ここでは、server_nameというsshでログインできるサーバーにnodes/vagrant.jsonのnodeオブジェクトから呼ばれるレシピが実行されます。

```
bundle exec knife solo cook server_name nodes/vagrant.json
```

これで、無事サーバーがセットアップされるはずです。


4. 参考文献
---
[Chef](http://docs.opscode.com/)

[入門 Chef Solo](http://www.amazon.co.jp/%E5%85%A5%E9%96%80Chef-Solo-Infrastructure-as-Code-ebook/dp/B00BSPH158)

**Chef Fundamentals Module 1** (下のアイコンをクリックすると、チュートリアルが見れます！) 

[![Chef Fundamentals Module 1](https://www.youtube.com/yt/brand/media/image/yt-brand-downloads-logo-for-web.png)](https://learnchef.opscode.com/screencasts/fundi-webinar-week-1/)
ちなみに、同じところに沢山のChefのVideoチュートリアルがあります.


他にも[Youtube Opscode](http://www.youtube.com/user/Opscode)でもいろいろな講演をみることができます。

以上です