Macにサーバと同じ環境を構築する 〜Chef編〜

# 1. はじめに

[前回](http://qiita.com/segawa/items/e77985ed2c107505930e)、Vagrant で Mac 上にサーバと同じ環境を構築しましたが、この記事ではシェルではなく Chef を使ってプロビジョンしてみたいと思います。プロビジョニングの前までは前回の記事を参照してください。

個人的にChefは覚えることが多く、とっつきにくいです。
詳しい話は使い方を少し覚えてからの方が理解しやすいと思うので、この記事では Vagrant のプロビジョニングを Chef で行うはじめの一歩になればと思っています。

**前回までの状態**

```console:ディレクトリの構成
/sample
└ Vagrantfile
```

```rb:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "rafacas/centos62-plain"

  config.vm.network "forwarded_port", guest: 80, host: 8080

  config.vm.network "private_network", ip: "192.168.33.10"

  config.vm.synced_folder ".", "/var/www/html",
    :create => true, 
    :owner => 'vagrant', 
    :group => 'vagrant', 
    :mount_options => ['dmode=777', 'fmode=666']

  config.vm.provision "shell", inline: <<-SHELL
    yum -y update

    service iptables stop
    chkconfig iptables off

    # Apache
    yum -y install httpd
    service httpd start
    chkconfig httpd on

    # MySQL
    yum -y install mysql mysql-server mysql-devel
    service mysqld start
    chkconfig mysqld on

    # PHP
    yum -y install php php-opcache php-devel php-mbstring php-mcrypt php-mysqlnd php-phpunit-PHPUnit php-pecl-xdebug php-pecl-xhprof

    cp /etc/php.ini /etc/php.ini_org
    sed -i -e "s|expose_php = On|expose_php = Off|" /etc/php.ini
    sed -i -e "s|;date.timezone =|date.timezone = Asia/Tokyo|" /etc/php.ini
    sed -i -e "s|display_errors = Off|display_errors = On|" /etc/php.ini
    sed -i -e "s|;mbstring.language = Japanese|mbstring.language = Japanese|" /etc/php.ini

    service httpd restart

    # Git
    yum install -y git

  SHELL
end
```

# 2. ざっくり Chef について

## Chef とは何か

Chef はサーバ構築を自動化するツールです。
Ruby で設定ファイルを記述し、コマンドを実行するとサーバが設定した状態でセットアップされます。

**公式サイト**
https://www.chef.io/chef/

**Github**
https://github.com/chef

## Chef の種類

Chef には商用版とオープンソース版の2種類があります。今回使用するのは **オープンソース版** の方です。

## Chefの動作形態

動作形態には以下の2種類あります。

### クライアント／サーバー型

**Chef Server** 上に設定ファイルを保持し、**Chef Client** に配布する形式です。

![Chef Components](https://docs.chef.io/_images/chef_overview.svg)
参照元：https://docs.chef.io/chef_overview.html


### スタンドアロン型

Chef Server を必要とせずに動作する Chef Client です。この記事ではスタンドアロン型を使用します。
以前は **Chef Solo** という形で提供されていましたが、現在（Chef Client 11.8 以降）は **Chef Zero** と呼ばれる Chef Client のローカルモードを使用することが推奨されています。

ただ現状、日本語の情報は圧倒的に Chef Solo が多いので、最初に使うなら Chef Solo の方がいろいろと調べやすいと思います。この記事でも Chef Solo を使用します。

## Chef の用語

ひとまず以下の単語を覚えてください。

|単語|説明|例|
|-----|-----|-----|
|**recipe**|レシピ。サーバ設定の手順が記述されたファイル。|Apacheのレシピ|
|**cookbook**|クックブック。レシピと必要なデータをまとめたもの。WEB上で公開されている **コミュニティクックブック** と独自に作成する **サイトクックブック** がある|Apacheのレシピとコピーするhttpd.conf|
|**kitchen**|キッチン。Chefのリポジトリのこと。複数のクックブックとChefの実行に必要なファイル群|ApacheのクックブックとMySQLのクックブック|
|**knife**|Chefリポジトリを操作するためのコマンド。多くのサブコマンドを持っています|
|**knife-solo**|knifeにサブコマンドを追加するGem。ローカルから仮想サーバ上の `Chef Solo` を操作可能になる|


# 3. Chef をインストールする

## Chef のインストール

インストールする方法がいくつかありますが、インストール方法によってインストールディレクトリが変わったり、Vagrant のプラグインが動かなかったりして苦労します。

現在、Chef が公式に推奨しているインストール方法は **[Chef Development Kit](https://downloads.chef.io/chef-dk/)** を使ったインストールです。
が、私の環境では Gem でインストールしているため、その前提で話を進めます。（ChefDK でインストールし直したいですが、締め切りの関係で断念しました。。。）

```console:Chefのインストール
$ gem install chef
```

Chef をインストールすると、Chef Solo もインストールされます。
バージョンを確認してみましょう。

```console:バージョン確認
$ chef-solo -v
Chef: 12.5.1
```

## knife-soloのインストール

次に **knife-solo** をインストールします。

```console:knife-soloのインストール
$ gem install knife-solo
```

## Berkshelfについて

Chef で検索をすると多くの記事で cookbook の管理に **Berkshelf** という依存管理ツールを使用していると思います。
便利なツールを入れれば入れるほど、覚えることと障害ポイントが多くなって最初はつらいので、この記事では使用しません。

## vagrant-omnibus のインストール

最後に仮想サーバに Chef をインストールするための Vagrant プラグインをインストールします。

```console:vagrant-omnibusのインストール
$ vagrant plugin install vagrant-omnibus
```

## Vagrantfile を変更

Vagrantfile に以下の1行を追記し、vagrant-omnibus プラグインを有効にします。
これで `vagrant up` すると、仮想サーバに Chef がインストールされているかを確認し、インストールされていない場合は最新版の Chef をインストールします。

```rb:Vagrantfile
config.omnibus.chef_version = :latest
```

また、プロビジョニングの設定を `shell` から `chef_solo` に書き換えます。

```rb:Vagrantfile
  config.vm.provision "shell", inline: <<-SHELL
   ：
  SHELL

↓ 変更

  config.vm.provision "chef_solo" do |chef|
    # まだ中身は空でOKです
  end
```

# 4. 設定する

さて、ここからが本番です。

## Chef リポジトリを作成する 

sample ディレクトリに Chef のリポジトリを追加します。
先ほどインストールした knife solo を使います。

```console:Chefリポジトリの作成
$ cd sample
$ knife solo init .
```

コマンドを実行するとファイルが作成され、以下の状態になります。

```console:
$ tree -p
.
|-- [-rw-r--r--]  Vagrantfile
|-- [drwxr-xr-x]  cookbooks
|-- [drwxr-xr-x]  data_bags
|-- [drwxr-xr-x]  environments
|-- [drwxr-xr-x]  nodes
|-- [drwxr-xr-x]  roles
`-- [drwxr-xr-x]  site-cookbooks

6 directories, 2 files
```

**追加されたディレクトリの説明**

|名前|説明|
|-----|-----|
|cookbooks|WEBで公開されているコミュニティクックブックを配置する|
|data_bags|今回は使用しません。クックブックで利用するデータを格納する|
|environments|今回は使用しません。開発、ステージング、本番等、環境ごとに設定する値を定義するファイルを置きます|
|nodes|今回は使用しません。ホスト名等の node ごとに設定する値を定義するファイルを置きます|
|roles|今回は使用しません。WebサーバやDBサーバ等、サーバの役割ごとに設定する値を定義するファイルを置きます|
|site-cookbooks|自作のクックブックを配置する|


## コミュニティクックブックを入手する

Web上に公開されているコミュニティクックブックを利用します。

こちらのサイトで検索するか
https://supermarket.chef.io/

以下のコマンドで、どのような cookbook があるか調べることが可能です。

```bash
# 一覧
knife cookbook site list

# 名前を指定して検索
knife cookbook site search apache2

# クックブックの情報を見る
knife cookbook site show apache2
```

### apache2

では、`apache2` の cookbook を入手してみましょう。

```console:apache2クックブックの入手
$ knife cookbook site download apache2
$ knife cookbook site install apache2
```

インストール時に初回は下記のようなエラーになるかもしれません。

```console:
$ knife cookbook site install apache2
Installing apache2 to sample/cookbooks
ERROR: The cookbook repo sample/cookbooks is not a git repository.
Use `git init` to initialize a git repo
```

エラーになった場合は sample ディレクトリに git リポジトリを作成します。

```
$ git init
$ git add -A
$ git commit -m
```

その後、もう一度インストールコマンドを実行してください。
すると cookbooks ディレクトリ配下にファイルが展開されます。

```console:sample/cookbooks/apache2
$ tree -L 1 -p
.
|-- [-rw-r--r--]  CHANGELOG.md
|-- [-rw-r--r--]  README.md
|-- [drwxr-xr-x]  attributes
|-- [drwxr-xr-x]  definitions
|-- [drwxr-xr-x]  files
|-- [-rw-r--r--]  metadata.json
|-- [drwxr-xr-x]  recipes
`-- [drwxr-xr-x]  templates
```

上記の recipes ディレクトリの中にレシピファイルが格納されています。

## php

同様に PHP クックブックも入手します。

```console:phpクックブックの入手
$ knife cookbook site download php
$ knife cookbook site install php
```

## git

```console:gitクックブックの入手
$ knife cookbook site download git
$ knife cookbook site install git
```

## Vagratnfile を変更する

下記のようにクックブックのパス設定と、使用するクックブックを追記します。

```rb:Vagrantfile
  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = ["cookbooks"]

    chef.add_recipe 'apache2'
    chef.add_recipe 'php'
    chef.add_recipe 'git'

  end
```

変更後の Vagrantfile は以下のような状態になっているはずです。

```rb:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "rafacas/centos62-plain"

  config.vm.network "forwarded_port", guest: 80, host: 8080

  config.vm.network "private_network", ip: "192.168.33.10"

  config.vm.synced_folder ".", "/var/www/html",
    :create => true, 
    :owner => 'vagrant', 
    :group => 'vagrant', 
    :mount_options => ['dmode=777', 'fmode=666']

  config.omnibus.chef_version = :latest

  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = ["cookbooks"]

    chef.add_recipe 'apache2'
    chef.add_recipe 'php'
    chef.add_recipe 'git'

  end
end
```


# 5. Vagrant の起動

ファイルを書き換えたら Vagrant を起動しましょう。
以下の流れで、仮想サーバが構築されます。

**1. Vagrant が仮想サーバを起動**
**2. ディレクトリが同期され cookbook が仮想サーバに転送される**
**3. vagrant-omnibus プラグインによって chef-solo が仮想サーバにインストールされる**
**4. 仮想サーバ上で cookbook の設定にもとづき chef-solo がアプリケーションをインストールする**


前回 sample ディレクトリで起動した仮想サーバがある場合は、起動前に削除してください。

```console:仮想サーバを削除
$ vagrant destroy
```

Vagrant を起動します。

```console:仮想サーバを起動
$ vagrant up
：
：
==> default: Thank you for installing Chef!
==> default: Running provisioner: chef_solo...
==> default: Detected Chef (latest) is already installed
```

起動が完了したら、仮想サーバにログインして確認してみましょう。

```console:インストールしたソフトのバージョン確認
$ vagrant ssh

↓

$ chef-solo -v
Chef: 12.5.1

$ httpd -v
Server version: Apache/2.2.15 (Unix)
Server built:   Dec 15 2015 15:50:14

$ php -v
PHP 5.3.3 (cli) (built: Jul  9 2015 17:39:00) 
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

$ git --version
git version 1.7.1
```

これが Chef の一番かんたんな部分の動きになります。

ここまでやってみると、「あれ？ yum update はどうやるの？」とか「apache のバージョンは指定できないの？」とか「既存の httpd.conf を使えないか」とか「MySQL をインストールして、ユーザーとデータベースまで自動で作成したい」とか、いろいろやりたいことが出てくると思います。
**全部できます。** やりたいことに合わせて、調べてみてください。