Chef Metalの凄さを体験しよう

![IMG_2597.JPG](https://qiita-image-store.s3.amazonaws.com/0/3470/8116fd10-3c9a-9397-4f53-080be1de007b.jpeg)
Chef Metalの凄さを体験しよう
===

2014/4/14 - 2014/4/17まで行われたChef Conf2014に参加してきました。私にとっては、たまたま一緒に行ったお客様もしくはお友達のおかげで本当に楽しい時間を過ごす事ができました。数ある発表の中で最も衝撃を受けたのが、Chef Metalでした。そのプレゼンを聞いた時には「おお！これよ、これ！」と衝撃を受けました。

このChef Metalを早速触ってもらえるようなカンタンなチュートリアルを創ってみました。実はカンファレンスの後で作者のJohn Keiserさんとペアプロして作ってみました。皆さんに楽しんでもらえると幸いです。


1. Chef Metalとは
---

Chefは、一般的には、プロビジョニングをするツールというイメージがあるでしょう。例えば、仮想OSを起動したら、そこにRubyやら、MongoDBやらをインストールして、設定までしてくれるようなツールです。この単体のChefだけでも世界のルールを変える力をもっていますが、Chef Metalはそれに加えて**「クラスタリング」を制御する事**を目標としています。つまり、**Chef Metalから仮想マシンのインスタンス上げたり、下げたり、落としたり、chefを使える準備したりとかいろいろやっていた事をvagrantとかec2とかすら事を意識する事無くプログラマブルにレシピかけたりする**わけですよ！これはカッコいい！

[Chef Metal](https://github.com/opscode/chef-metal)はChefのプラグインモデルになっていて、仮想マシンの環境として、Virtual Box, EC2, LXC, bare metalその他など、bootstrappersを書く事で対応できるとの事です。

また、今のところchef-metalは、Vagrant, Unix(ssh経由)そしてWindows/winrmをサポートしています。次のバージョンで、FogやDockerもサポートしていくようです(EC2やLXCもカバーされます)これも、image factoryを書く事で、他の環境にも対応することができるようです。

まぁ、ごちゃごちゃ言ってないで、プログラマは、黙って動かしてみましょう!どんだけカンタンに環境が出来上がってしまうか恐ろしい気分になることでしょう。

2. Chef Metal チュートリアル
---

### 2.1. 前提条件

さて、ここから早速チュートリアルです。事前に、[VirtualBox](https://www.virtualbox.org/)や、[Vagrant](http://www.vagrantup.com/)のインストールを済ませておいてください。どちらもインストーラー系なので、困らないと思います。参考のため、私の環境では、Vagrant v1.5.2、Virutal Box 4.2.10 r84104という環境になっています。また、先にChefをインストールしておいてください。私の環境では、Chef 11.12.2を使っています。

### 2.2. Chefのインストール

まだ、Chefをインストールしていない方は、次のコマンドでどうぞ。rbenvを使っている人は`rbenv rehash`をお忘れなく。尚、コマンドは全てローカルのコマンドで実行します。

```
% gem install chef
```

### 2.3. Chef Metalの取得

Chef Metalは今のところgem等では配付されていません。Chef MetalのGitHubは[こちら](https://github.com/opscode/chef-metal)になります。ここの指示に従ってやってみましょう。

まずは、次のインストラクションに従って見ましょう。

```
git clone https://github.com/opscode/cheffish.git
cd cheffish
rake install
cd ..

git clone https://github.com/opscode/chef-metal.git
cd chef-metal
rake install

cd chef-metal
chef-client -z -o myapp::vagrant,myapp::linux,myapp::small
```

これを実行すると、最後の行`chef-client -z -o myapp::vagrant,myapp::linux,myapp::small`でエラーが発生(2014/4/18段階で)しますので、その対処方法をあわせて記述します。

### 2.4. Chef Metalのエラー対処法

#### 2.4.1.xxxx.pemが要求される

実行すると

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp::small
 : 中略

================================================================================
Chef encountered an error attempting to create the client "ushio"
================================================================================

```

といったようなエラーがでて指示通りchef-stacktrace.outを見るとushio.pem(ushioはMacOSXのユーザ名)が見つかりませんとありました。一瞬chefの仕組みからすると、[ORGANIZATION-validator.pemとUSER.pemのプライベートキーを扱う](http://docs.opscode.com/chef/install_workstation.html)ので、それを入れたらいいと思いきや違っていて、私の環境での解決策は次の通りでした。

```
%  mv ~/.chef ~/.chef_bak
```

~/.chefディレクトリには、上記の二つのプライベートキーと、knife.rbという設定ファイルが保存されています。恐らく、これが、chef等のtutorialが残っていたのでしょう。一旦消してしまえばいいと思います。

#### 2.4.2. タイプミス

次はこんなエラーをいただきました。

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp:small 
    ：中略
resolving cookbooks for run list: ["myapp::vagrant", "myapp::linux", "myapp:small"]

================================================================================
Error Resolving Cookbooks for Run List:
================================================================================


Missing Cookbooks:
------------------
No such cookbook: myapp:small
```

これは、タイプミスです。myapp:smallではなくmyapp::smallですね。お恥ずかしい。

#### 2.4.3. レシピの変更

上記の問題を修正して実行すると次はこれでした。

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp::small
  : 中略
================================================================================
Error executing action `create` on resource 'machine[mario]'
================================================================================


RuntimeError
------------
vagrant up mario failed!
STDOUT:Bringing machine 'mario' up with 'virtualbox' provider...

STDERR:There are errors in the configuration of this machine. Please fix
the following errors and try again:

vm:
* The host path of the shared folder is missing: /***/***/oc/code/opscode
```

謎のディレクトリ `/***/***/oc/code/opscode`が指定されています。これは、実は`cookbooks/myapp/receipes/linux.rb`に記述されているので、cookbooks/myapp/recipes/linux.rbを書き換えます。

```
# Use a Linux image
vagrant_box 'precise64' do
  url 'http://files.vagrantup.com/precise64.box'
#  provisioner_options 'vagrant_config' => <<EOM
#    config.vm.synced_folder (ENV['SRC_DIR'] || "#{ENV['HOME']}/oc/code/opscode"), "/mnt/host_src"
#EOM
end

```
provisioner_optionsで始まる行からEOMまでの行をコメントアウトしただけになっています。多分将来直してくれるでしょう。（何しろ本人とこの手順で動かしたのでw）

### 2.4.4. ついに動作

そして遂に動作しました。動作のログは沢山でていますが、ログを追うと我々がいままで手動していた事がごっそり実行されている事がわかります。なんと便利なのでしょう。是非ログを見てみてください。

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp::small

[2014-04-17T23:39:18-07:00] WARN: No config file found or specified on command line, using command line options.
[2014-04-17T23:39:18-07:00] WARN: 

     :中略

Chef Client finished, 6/9 resources updated in 158.752274 seconds
```

3. 動作の確認
---
では、動作したところで、中身を見てみましょう。

### 3.1. コマンドの意味

先ほど実行したコマンドは次の通りでした

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp::small
```

このコマンドの意味は、myapp::smallのレシピに書いてある内容を参考に、vagrantのprecise64 linuxのインスタンスを~/machinetestというディレクトリに作ります。詳細は、次の2つのファイルを見てみればわかります。

### 3.2. machineリソース

Chef-Metalでは、まず、machineリソースを定義します。これがChef-Metalの中心的な要素です。OSやプロビジョニングとは、独立したファイルで定義されています。新しいmachineというDSLを見つける事ができるでしょう。

まずは、cookbooks/myapp/recipes/small.rbを見てみましょう。

ざっくりいうと、mario/luigi1の２つのインスタンスを上げます。まずmarioをcreateしてcookbookをプロビジョニングしたり、設定を書き換えた後、marioを停止。その後luigi1も同じようにして、最後にluigi1を停止しています。

ちなみに、このインスタンスの起動とかも、冪等性(idempotently)が考慮されています。インスタンスが無ければ作るし、停止していたら、起動するし、インスタンスが起動していたら、何もしないといった具合で、常に最終的に同じ状態になるようになっています。


```
# Use a Linux image
vagrant_box 'precise64' do
  url 'http://files.vagrantup.com/precise64.box'
#  provisioner_options 'vagrant_config' => <<EOM
#    config.vm.synced_folder (ENV['SRC_DIR'] || "#{ENV['HOME']}/oc/code/opscode"), "/mnt/host_src"
#EOM
end
Bad-2:chef-metal ushio$ cat cookbooks/myapp/recipes/small.rb 
file '/tmp/blah.txt' do
  content 'woo'
end

machine 'mario' do
  #recipe 'mydb'
  tag 'mydb_master'
  action [:delete, :create]
end

machine_file '/etc/blah.conf' do
  machine 'mario'
  content 'hi'
end

machine 'mario' do
  action :stop
end

num_webservers = 1

1.upto(num_webservers) do |i|
  machine "luigi#{i}" do
    file '/etc/woo.txt' => '/tmp/blah.txt'
    file '/etc/woo2.txt', '/tmp/blah.txt'
    file '/etc/woo3.txt', :local_path => '/tmp/blah.txt'
    file '/etc/woo4.txt', :content => 'WOOOOOOO'
    #recipe 'apache'
    #recipe 'mywebapp'
    action [ :create, :stop ]
  end
end

```
machineがmachineを表す記述ですね。アクション部でどうしてもらうか？とか、プロビジョニングする内容が記述されていますね。



### 3.3. Provisioners

Provisionerは、実際の仕事を物理から切り離して抽象的に定義します。次のようなタスクを冪等性(idempotently)をもって実行します。CloudやVMs、コンテナ、Metal(Razore, etc)等が記述されます

* Machineをクラウドや、コンテナやVMか、bare metalから、獲得します
* sshやwinrmやその他の方法で、接続します。
* Machineに対してbootstrappingを実施して、あなたが指示した、レシピをつかってコンバージョンを行います。

PrivosionerのAPIは、新しいProvisionerを最小の努力で作れるように設計されています。ユーザにとっては、それらをカンタンに利用することができます。現在サポートされているProvisionerは[ここのProvisionersの部分](https://github.com/opscode/chef-metal)に記載されています。今回は、linuxを使っています。

仮想化環境

* vagrant.rb

```
require 'cheffish'
require 'chef_metal_vagrant'

# Set up a vagrant cluster (place for vms) in ~/machinetest
vagrant_cluster "#{ENV['HOME']}/machinetest"
```
* ec2.rb

```
require 'chef_metal_fog'

ec2testdir = File.expand_path('~/ec2test')

directory ec2testdir

with_fog_ec2_provisioner

fog_key_pair 'me' do
  private_key_path "#{ec2testdir}/me"
  public_key_path "#{ec2testdir}/me.pub"
end

```


OS

*linux.rb

```
# Use a Linux image
vagrant_box 'precise64' do
  url 'http://files.vagrantup.com/precise64.box'
#  provisioner_options 'vagrant_config' => <<EOM
#    config.vm.synced_folder (ENV['SRC_DIR'] || "#{ENV['HOME']}/oc/code/opscode"), "/mnt/host_src"
#EOM
end
```
WindowsのProvisionerもあるようです。


4. カスタマイズ
---

さて、このチュートリアルの最後の説明は、レシピのカスタマイズです。こんな風に変えてみました。

`cookbooks/myapp/recipes/small.rb`を書き換えてみましょう `####`が書き換えた部分です。
最初にmarioが起動した後に、marioのインスタンスを削除していましたが、コレを無くしました。そして、
luigiが3台上がるようになっています。こんなにカンタンにサーバーの台数増やせちゃいます。

```
file '/tmp/blah.txt' do
  content 'woo'
end

machine 'mario' do
  #recipe 'mydb'
  tag 'mydb_master'
  #action [:delete, :create]
  action [:create]   #### marioのインスタンスを起動したままにする
end

machine_file '/etc/blah.conf' do
  machine 'mario'
  content 'hi'
end

machine 'mario' do
  action :stop
end

num_webservers = 3   #### ここで、luigiが3インスタンス起動する

1.upto(num_webservers) do |i|
  machine "luigi#{i}" do
    file '/etc/woo.txt' => '/tmp/blah.txt'
    file '/etc/woo2.txt', '/tmp/blah.txt'
    file '/etc/woo3.txt', :local_path => '/tmp/blah.txt'
    file '/etc/woo4.txt', :content => 'WOOOOOOO'
    #recipe 'apache'
    #recipe 'mywebapp'
    action [ :create, :stop ]
  end
end

```

実行のログを見ると、しっかりサーバーが4回起動しています！多少省略しています。

```
% chef-client -z -o myapp::vagrant,myapp::linux,myapp::small


  knife ssl check -c 

Recipe: myapp::vagrant
  * vagrant_cluster[/***/***/machinetest] action create (up to date)
Recipe: myapp::linux
  * vagrant_box[precise64] action create (up to date)
Recipe: myapp::small
  * file[/tmp/blah.txt] action create (up to date)
  * machine[mario] action create
    - run vagrant up mario (status was 'poweroff')[2014-04-18T06:52:36+00:00] 
    
  knife ssl check -c /etc/chef/client.rb

    - run 'chef-client -l auto' on mario

  * machine_file[/etc/blah.conf] action upload (up to date)
  * machine[mario] action stop
    - run vagrant halt mario (status was 'running')

  * machine[luigi1] action create
    - run vagrant up luigi1 (status was 'poweroff')[2014-04-18T06:53:05+00:00] 


  knife ssl check -c /etc/chef/client.rb

    - run 'chef-client -l auto' on luigi1

  * machine[luigi1] action stop
    - run vagrant halt luigi1 (status was 'running')

  * machine[luigi2] action create
    - create new file /***/***/machinetest/luigi2.vm
    - update content in file /***/***/machinetest/luigi2.vm from none to 747561
        --- /***/***/machinetest/luigi2.vm	2014-04-17 23:53:15.000000000 -0700
    - run vagrant up luigi2 (status was 'not created')
    - create rsa private key none (2048 bits)
    - create directory /etc/chef on luigi2
    - write file /etc/chef/client.pem on luigi2
    - create client luigi2 at http://127.0.0.1:8889
    - add public_key = “” 
    - create node luigi2 at http://127.0.0.1:8889
    - add normal.provisioner_options = {"vagrant_options"=>{"vm.box"=>"precise64", "vm.box_url"=>"http://files.vagrantup.com/precise64.box"}}
    - add normal.provisioner_output = {"provisioner_url"=>"vagrant_cluster:///***/***/machinetest", "vm_name"=>"luigi2", "vm_file_path"=>"/***/***/machinetest/luigi2.vm", "forwarded_ports"=>{}}
    - write file /etc/chef/client.rb on luigi2
    - write file /tmp/detect.sh on luigi2
    - upload file /***/***/.chef/package_cache/chef_11.12.2-1_amd64.deb to /tmp/chef_11.12.2-1_amd64.deb on luigi2
    - run 'dpkg -i "/tmp/chef_11.12.2-1_amd64.deb"' on luigi2
    - upload file /tmp/blah.txt to /etc/woo.txt on luigi2
    - upload file /tmp/blah.txt to /etc/woo2.txt on luigi2
    - upload file /tmp/blah.txt to /etc/woo3.txt on luigi2
    - write file /etc/woo4.txt on luigi2[2014-04-18T06:54:44+00:00] WARN: 


  knife ssl check -c /etc/chef/client.rb

    - run 'chef-client -l auto' on luigi2

  * machine[luigi2] action stop
    - run vagrant halt luigi2 (status was 'running')

  * machine[luigi3] action create
    - create new file /***/***/machinetest/luigi3.vm
    - update content in file /***/***/machinetest/luigi3.vm from none to 342ca5
        --- /***/***/machinetest/luigi3.vm	2014-04-17 23:54:55.000000000 -0700
    - run vagrant up luigi3 (status was 'not created')
    - create rsa private key none (2048 bits)
    - create directory /etc/chef on luigi3
    - write file /etc/chef/client.pem on luigi3
    - create client luigi3 at http://127.0.0.1:8889
    - add public_key = ""
    - create node luigi3 at http://127.0.0.1:8889
    - add normal.provisioner_options = {"vagrant_options"=>{"vm.box"=>"precise64", "vm.box_url"=>"http://files.vagrantup.com/precise64.box"}}
    - add normal.provisioner_output = {"provisioner_url"=>"vagrant_cluster:///***/***/machinetest", "vm_name"=>"luigi3", "vm_file_path"=>"/***/***/machinetest/luigi3.vm", "forwarded_ports"=>{}}
    - write file /etc/chef/client.rb on luigi3
    - write file /tmp/detect.sh on luigi3
    - upload file /***/***/.chef/package_cache/chef_11.12.2-1_amd64.deb to /tmp/chef_11.12.2-1_amd64.deb on luigi3
    - run 'dpkg -i "/tmp/chef_11.12.2-1_amd64.deb"' on luigi3
    - upload file /tmp/blah.txt to /etc/woo.txt on luigi3
    - upload file /tmp/blah.txt to /etc/woo2.txt on luigi3
    - upload file /tmp/blah.txt to /etc/woo3.txt on luigi3
    　　　　　　　　　　:
    　Chef Client finished, 0/0 resources updated in 1.871189317 seconds

    - run 'chef-client -l auto' on luigi3
    

  * machine[luigi3] action stop
    - run vagrant halt luigi3 (status was 'running')

[2014-04-17T23:56:39-07:00] WARN: Skipping final node save because override_runlist was given

Running handlers:
Running handlers complete

Chef Client finished, 8/12 resources updated in 273.106443 seconds　　　　　　　　　
    　　　　　　　　　　
```

数字一ついじるだけでクラスタを構成するノード数が変わりました。ちなみに、クラスタ数を下げるダウンワードということも出来るのですが、これは、Chef-Metalのスライドに載っていましたので、ChefConf2014の資料が公開されるのを待ちましょう。


おわりに
---

少しだけでしたが、Chef-Metalの強力さが実感できましたでしょうか？ちなみにChef-Metalと連携したKitchen-Metalというのも近日公開予定らしいです。こちらも楽しみですね。これからも、Chef-Metalにガッツリ注目したいと思います。

ちなみにChefを使う人は基本的にHeavy Metalを聞きながらChefをいじるのがいいと思います。ChefConfで流れた音楽は100% HR/HMでしたので。まぁ、このアルバムはマスター必須ですね。

![Blizzard of Ozz](http://images.myplaydirect.com/autoimage/display/product-detail/media-cf.myplaydirect.com/manual/Ozzy_Osbourne-Diary_Of_A_Madam_Expanded_Edition_new/1000x1000.jpg/1000x1000) 

以上です！