【環境構築】 Bundler を使用した Itamae / Serverspec の導入

# **はじめに**

下記の記事で Ruby を取り巻く基本的な言葉について整理した。

* [Ruby覚書 - Ruby / RubyGem / rvm / rbenv / Bundler](http://qiita.com/udein/items/3f35fb98b0f9e023475c)

本稿はそれに続き、メインとなる **Itamae** と **Serverspec** を使用した自動構成管理の導入に関する環境構築の手順について記載する。

# **前提条件**

本稿の作成にあたり、下記の作業環境および構築環境を前提とする。

## **作業環境**

本記事の作成にあたり使用した作業環境は下記の通り。

|項目|内容|バージョン|
|:--|:--|:--|
|OS|macOS|Sierra 10.12.3|
|VM|VirtualBox|5.1.14 r112924|
|その他|Vagrant|1.8.7|

## **構築環境**

本記事が構築対象とするシステムの主要なソフトの構成は下記の通り。

|項目|内容|バージョン|ホスト A|ホスト B|
|:--|:--|:--|:--|:--|
|OS|bento/centos-6.8|-|○|○|
|S/W|rbenv|1.1.0-2-g4f8925a|○|-|
|S/W|ruby-build|-|○|-|
|S/W|Bundler|version 1.14.6|○|-|
|S/W|Itamae|v1.9.10|○|-|
|S/W|Serverspec|2.38.0|○|-|

Vagrant の Box の構成は下記の通り。

```bash:Vagrant
  # config.vm.box = "bento/centos-6.8"
  config.vm.define "host" do |node|
    node.vm.box = "bento/centos-6.8"
    node.vm.hostname = "host"
    node.vm.network :private_network, ip: "192.168.33.10"
  end
  config.vm.define "web" do |node|
    node.vm.box = "bento/centos-6.8"
    node.vm.hostname = "web"
    node.vm.network :private_network, ip: "192.168.33.11"
  end
```


# **構築手順**

## **1. Rubyの導入**

rbenvを使用したRubyのインストール手順の概要は下記の通り。

1. Rubyのビルド環境（gccなど）をインストール
2. **rbenv** + **rbenv-build** をインストール
3. **.bash_profile** にPATHを通して設定を適用
4. **Ruby** のバージョン一覧の確認とインストール
5. **rbenv global** で環境全体に設定を適用

本手順に関する詳細なコマンドについては下記が詳しい。

* [rbenv を利用した Ruby 環境の構築 - Developers.IO](http://dev.classmethod.jp/server-side/language/build-ruby-environment-by-rbenv/)

## **2. Bundler**

### **インストール**

```console:Command
// Bundleをインストール
$ gem install bundler

// bundler のバージョンを確認
$ bundler -v
Bundler version 1.14.6
```

### **使用方法**

**bundler** の基本的な使用方法は下記の通り。

1. **Gemfile** を作成する
2. 使用したい **gem** を **Gemfile** に記述する
3. **Gemfile** にリスト化したgemを一括インストール
4. インストールしたgemをプログラムソース上でロード
5. **bundle exec** を最初につけてコマンドを実行する

コマンド例を下記に記す。

```console:Command
// 1. Gemfile を作成する
$ bundle init

// 2. Gemfile に使用したいgemを記述する
$ vim Gemfile

// 3. Gemfile にリスト化したgemを一括インストール
$ bundle install --path vendor/bundle

// 4. bundle exec を最初につけてコマンドを実行する（下記は一例）
$ bundle exec ruby foo.rb
```

## **3. Itamae と Serverspec**

### **ディレクトリ構成**

それぞれにベストプラクティスとなるディレクトリ構成がある。

#### Itamae

```
.
├── cookbooks
│   └── nginx
│       ├── default.rb
│       ├── files
│       │   └── etc
│       │       └── nginx
│       │           └── conf.d
│       │               └── static.conf
│       └── templates
│           └── etc
│               └── nginx
│                   └── conf.d
│                       └── dynamic.conf
└── roles
    └── web.rb
```

[Best Practice · itamae-kitchen/itamae Wiki · GitHub](https://github.com/itamae-kitchen/itamae/wiki/Best-Practice)

#### Serverspec

```
.
├── Rakefile
├── spec
│          ├── ap
│          │        ├── mysqlclient_spec.rb
│          │        ├── nodejs_spec.rb
│          │        ├── rvm_spec.rb
│          │        └── sqlite_spec.rb
│          ├── base
│          │        ├── build-essential_spec.rb
│          │        └── git_spec.rb
│          ├── db
│          │        └── mysql_spec.rb
│          └── spec_helper.rb
```

[ ap / base/ db ] はホスト名。

[[Serverspec]Serverspecのテストケースをロール単位で管理する - 城陽人の本棚](http://d.hatena.ne.jp/minamijoyo/20150225/p1)

### **インストール**

前述の情報を参考に、実際に **Itamae** と **Serverspec** をインストールする。

```console:Command
// 任意のプロジェクトフォルダを作成する
$ mkdir ~/sample && cd $_

// --------------------------
// Itamae のインストール
// --------------------------
$ mkdir ~/sample/itamae && cd $_

// Gemfileを作成する
$ bundle init

// Gemfileを編集する。（ヒアドキュメントを使用）
$ cat << EOS > Gemfile
source "https://rubygems.org"

gem 'itamae'
gem 'thor', "0.19.1"
gem 'rake'
EOS

// Gemfile にリスト化したgemを一括インストール
$ bundle install --path vendor/bundle

// --------------------------
// Serverspec のインストール
// --------------------------
$ mkdir ~/sample/serverspec && cd $_

// Gemfileを作成する
$ bundle init

// Gemfileを編集する　（ヒアドキュメントを使用）
$ cat << EOS > Gemfile
source "https://rubygems.org"

gem 'serverspec'
gem 'rake'
EOS

// Gemfile にリスト化したgemを一括インストール
$ bundle install --path vendor/bundle
```

#### Itamaeのインストール確認とディレクトリ準備

Itamaeが正常にインストール出来たかどうか確認し、ベストプラクティスに沿ったディレクトリを用意する。

```console:Command
// Itamaeのインストール確認
$ cd ~/sample/itamae
$ bundle exec itamae help

$ mkdir cookbooks
$ mkdir roles
```

#### Serverspecの初期化

serverspec-init コマンドで構築対象のシステムに関する情報を入力し、初期化操作を行う。
ここではリモートLinuxへsshで接続する設定を行う。
（Exec(Local)については文献が少なく使用方法を把握しきれていない。。）

```console:Command
// Serverspecの初期化
$ cd ~/sample/serverspec
$ bundle exec serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: web
 + spec/
 + spec/web/
 + spec/web/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
```

#### ディレクトリ構成

結果、作成されたディレクトリ構成（略）は下記の通り。

```
$ cd ~/sample
$ tree . -L 2
.
├── itamae
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── cookbooks
│   ├── roles
│   └── vendor
└── serverspec
    ├── Gemfile
    ├── Gemfile.lock
    ├── Rakefile
    ├── spec
    └── vendor
```

#### 動作確認

これで Itamae と Serverspec がインストール出来たので、試しにリモートホストにhttpdをインストールしてみる。

まずはコマンドを楽にするためにsshの設定を行う。
（本題とは少し異なるのでさらっと流す。）

```console:command
// Vagrant の他方のインスタンスへの接続設定
$ cat << EOS > ~/.ssh/config
Host web
 HostName 192.168.33.11
EOS

$ chmod 600 ~/.ssh/config
$ ssh-keygen -t rsa
$ ssh-copy-id web
$ ssh web
$ exit
```

下記でItamaeとServerspecの初歩的な使用をしてみる。

```console:Command
// --------------------------
// Itamae / レシピ作成
// --------------------------
// Itamae のレシピを作成
$ echo "package 'httpd'" > ~/sample/itamae/cookbooks/recipe.rb

// --------------------------
// Serverspec / テストを作成
// --------------------------
// Serverspecのファイルを作成
$ cat << EOS > ~/sample/serverspec/spec/web/httpd_spec.rb
require 'spec_helper'

describe package('httpd') do
  it { should be_installed }
end
EOS

// --------------------------
// Itamae / レシピの正常性確認
// --------------------------
// Itamae のドライラン
$ cd ~/sample/itamae
$ bundle exec itamae ssh -h web cookbooks/recipe.rb -n
 INFO : Starting Itamae...
 INFO : Recipe: /home/vagrant/sample/cookbooks/recipe.rb
 INFO :   package[httpd] installed will change from 'false' to 'true'

// --------------------------
// Serverspec / エラーパターンの動作確認
// --------------------------
// Serverspecのエラーパターン
$ cd ~/sample/serverspec
$ rm spec/web/sample_spec.rb
$ bundle exec rake spec
/home/vagrant/.rbenv/versions/2.2.6/bin/ruby -I/home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/lib:/home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-support-3.5.0/lib /home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/web/\*_spec.rb

Package "httpd"
  should be installed (FAILED - 1)

Package "httpd"
  should be installed (FAILED - 2)

Service "httpd"
  should be enabled (FAILED - 3)
  should be running (FAILED - 4)

Port "80"
  should be listening (FAILED - 5)

Failures:

  1) Package "httpd" should be installed
     On host `web'
     Failure/Error: it { should be_installed }
       expected Package "httpd" to be installed
       sudo -p 'Password: ' /bin/sh -c rpm\ -q\ httpd
       package httpd is not installed

     # ./spec/web/httpd_spec.rb:8:in `block (2 levels) in <top (required)>'

  2) Package "httpd" should be installed
     On host `web'
     Failure/Error: it { should be_installed }
       expected Package "httpd" to be installed
       sudo -p 'Password: ' /bin/sh -c rpm\ -q\ httpd
       package httpd is not installed

     # ./spec/web/sample_spec.rb:4:in `block (2 levels) in <top (required)>'

  3) Service "httpd" should be enabled
     On host `web'
     Failure/Error: it { should be_enabled }
       expected Service "httpd" to be enabled
       sudo -p 'Password: ' /bin/sh -c chkconfig\ --list\ httpd\ \|\ grep\ 3:on

     # ./spec/web/sample_spec.rb:12:in `block (2 levels) in <top (required)>'

  4) Service "httpd" should be running
     On host `web'
     Failure/Error: it { should be_running }
       expected Service "httpd" to be running
       sudo -p 'Password: ' /bin/sh -c ps\ aux\ \|\ grep\ -w\ --\ httpd\ \|\ grep\ -qv\ grep

     # ./spec/web/sample_spec.rb:13:in `block (2 levels) in <top (required)>'

  5) Port "80" should be listening
     On host `web'
     Failure/Error: it { should be_listening }
       expected Port "80" to be listening
       sudo -p 'Password: ' /bin/sh -c netstat\ -tunl\ \|\ grep\ --\ :80\\\

     # ./spec/web/sample_spec.rb:27:in `block (2 levels) in <top (required)>'

Finished in 0.37589 seconds (files took 0.73406 seconds to load)
5 examples, 5 failures

Failed examples:

rspec ./spec/web/httpd_spec.rb:8 # Package "httpd" should be installed
rspec ./spec/web/sample_spec.rb:4 # Package "httpd" should be installed
rspec ./spec/web/sample_spec.rb:12 # Service "httpd" should be enabled
rspec ./spec/web/sample_spec.rb:13 # Service "httpd" should be running
rspec ./spec/web/sample_spec.rb:27 # Port "80" should be listening

/home/vagrant/.rbenv/versions/2.2.6/bin/ruby -I/home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/lib:/home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-support-3.5.0/lib /home/vagrant/sample/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/web/\*_spec.rb failed

// --------------------------
// Itamae / httpdのインストール
// --------------------------
// Itamae を使用して自動構築
$ cd ~/sample/itamae
$ bundle exec itamae ssh -h web cookbooks/recipe.rb
 INFO : Starting Itamae...
 INFO : Recipe: /home/vagrant/sample/cookbooks/recipe.rb
 INFO :   package[httpd] installed will change from 'false' to 'true'

// --------------------------
// Serverspec / 正常パターンの動作確認
// --------------------------
// Serverspecを使用して自動テスト
$ cd ~/sample/serverspec
$ bundle exec rake spec
/home/vagrant/.rbenv/versions/2.2.6/bin/ruby -I/home/vagrant/sample/serverspec/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/lib:/home/vagrant/sample/serverspec/vendor/bundle/ruby/2.2.0/gems/rspec-support-3.5.0/lib /home/vagrant/sample/serverspec/vendor/bundle/ruby/2.2.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/web/\*_spec.rb

Package "httpd"
  should be installed

Finished in 0.52577 seconds (files took 0.30729 seconds to load)
1 example, 0 failures

```

* [ショロカレ 1 日目 | 俺の板前修業日記 〜 itamae でユーザーを複数のグループに所属させるメモ 〜 - ようへいの日々精進XP](http://inokara.hateblo.jp/category/serverspec)

以上の手順で **Itame** と **Serverspec** を使用する準備が出来た。
あとはベスト・プラクティスに沿った構成を意識しながら、 **Serverspecで期待構成の記載** ⇢ **Itamaeで自動構成** を繰り返してインフラのTDDを実行していくことが出来る。


---

**rbenv-build** に関する日本語文献が少なくなかなか理解しきれていない。

* GitHub : [rbenv-build](https://github.com/rbenv/ruby-build)

> ruby-build is an rbenv plugin that provides an rbenv install command to compile and install different versions of Ruby on UNIX-like systems.
>
> You can also use ruby-build without rbenv in environments where you need precise control over Ruby version installation.
引用 : [ruby-build - GitHub](https://github.com/rbenv/ruby-build#ruby-build)