vagrant+chef-solo+berkshelf使う

こないだクローラーでデータ収集まで終わったので、とりあえず適当にDBにぶち込んで検索できるのでも作ってみようかと思ったのだけど、rails newする前に、ちょっと待てよと。せっかくなので開発環境vagrant+chef-soloでモダンな感じにしたいなぁとか思いだして、まぁアプリ作ってからその辺やってもよいのだけど、いずれやるなら後でやるか今やるかの違いなので、じゃあ今でしょ。というわけでやってみた。

Chefの使い方とかは「Chef実践入門 ~コードによるインフラ構成の自動化 (WEB+DB PRESS plus)」を参考にしてる。
当初の目的からずれてきてるけどchef周りは複数のツールが登場して登場人物ややこしくて、使い方覚えるために実際にサンプル試してたらなんか楽しくなってきたので、気がつけばなぜだかchef入門なかんじになってしまった。なのでRails用のruby環境作るのはまたあとで整理してから書く。というわけで、今回はchef関連のツールの使い方のメモです。なんか長くなりすぎた。。。

試してる手元の環境はMacOSX10.10(Yosemite)です。

まずVritualBoxをダウンロードしてインストールする。現時点の最新版VirtualBox 4.3.20を使いました。
https://www.virtualbox.org/wiki/Downloads

次に、Vagrantもダウンロードしてインストールする。現時点の最新版Vagrant 1.3.5を使いました。
http://downloads.vagrantup.com/

    • -

(追記)リンクが間違ってました。正しいリンクは以下です。現時点の最新版は1.7.2のようです。このエントリは1.3.5を使いました。
https://www.vagrantup.com/downloads.html

      • -

vagrantはgem経由でもインストールできるけど、バージョン古いのが入ったりするので公式パッケージから入れた方がよいということらしいです。

ゲストOSのVMイメージはBoxというのだけど、公式のBoxが配布されてるのでそれを使う。
http://www.vagrantbox.es/

今回はChefが公式に配布してるCentOSのイメージ使いました。あえて7系にしようかと思ったけど、ハマりそうなので日和って6.5を入れた。

$ mkdir chef-template
$ cd chef-template
$ vagrant init opscode-centos-6.5 http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Box追加したらVM起動する。初回はイメージのダウンロード550MBぐらいあるので結構時間かかります。気長に待ちましょう。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Box 'opscode-centos-6.5' was not found. Fetching box from specified URL for
the provider 'virtualbox'. Note that if the URL does not have
a box for this provider, you should interrupt Vagrant now and add
the box yourself. Otherwise Vagrant will attempt to download the
full box prior to discovering this error.
Downloading or copying the box...
Extracting box...te: 116k/s, Estimated time remaining: 0:00:01))
Successfully added box 'opscode-centos-6.5' with provider 'virtualbox'!
[default] Importing base box 'opscode-centos-6.5'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Mounting shared folders...
[default] -- /vagrant

起動できたら、vagrant sshでログインしてみる。

$ vagrant ssh
Last login: Fri Mar  7 16:57:20 2014 from 10.0.2.2
[vagrant@localhost ~]$ cat /etc/redhat-release
CentOS release 6.5 (Final)

vagrantにはデフォルトでファイル共有設定があって、ホスト側のVagrantfileが置いてるディレクトリが
ゲスト側の/vagrantマッピングされるので、この機能使えばソースコード編集したりとかは手元のエディタでできる。

[vagrant@localhost ~]$ ls -la /vagrant
合計 12
drwxr-xr-x.  1 vagrant vagrant  136  1月 30 03:26 2015 .
dr-xr-xr-x. 23 root    root    4096  1月 30 04:35 2015 ..
drwxr-xr-x.  1 vagrant vagrant  102  1月 30 03:25 2015 .vagrant
-rw-r--r--.  1 vagrant vagrant 4700  1月 30 03:26 2015 Vagrantfile
[vagrant@localhost ~]$ exit

vagrant ssh-configの出力結果を手元の~/.ssh/configに追記しておけば
vagrant sshじゃなくて普通にsshで入れるようになる。
あとあと複数台区別して使えるようにwebappとか名前つけておく。

$ vagrant ssh-config --host webapp >> ~/.ssh/config
$ ssh webapp
[vagrant@localhost ~]$ exit

次に若干遠回りなんだけど、全体観を理解するためにchef-soloをゲスト側に入れてみる。
(ゲストにログインするのめんどくさいのでこれは後でknife-soloというので置き換える。)

chef-soloはchef本体入れるとついてくる。

$ ssh webapp
[vagrant@localhost ~]$ curl -L https://www.opscode.com/chef/install.sh | sudo bash

インストールしたらコマンド使えるか確認。

[vagrant@localhost ~]$ chef-solo -v
Chef: 12.0.3

試しにHello Worldするcookbook作る。chefでもHello Worldするんかい。

[vagrant@localhost ~]$ sudo knife cookbook create hello -o /var/chef/cookbooks
WARNING: No knife configuration file found
** Creating cookbook hello in /var/chef/cookbooks
** Creating README for cookbook: hello
** Creating CHANGELOG for cookbook: hello
** Creating metadata for cookbook: hello

レシピを編集。ただlog関数で標準出力に文字列表示してるだけ。

[vagrant@localhost ~]$ sudo vi /var/chef/cookbooks/hello/recipes/default.rb
#
# Cookbook Name:: hello
# Recipe:: default
#
# Copyright 2015, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
log "Hello World"

試しにできたクックブックを実行してみる。

[vagrant@localhost ~]$ sudo chef-solo -o hello
{:config_missing=>true}
[2015-01-30T06:07:57+00:00] WARN: *****************************************
[2015-01-30T06:07:57+00:00] WARN: Did not find config file: /etc/chef/solo.rb, using command line options.
[2015-01-30T06:07:57+00:00] WARN: *****************************************
Starting Chef Client, version 12.0.3
[2015-01-30T06:07:59+00:00] WARN: Run List override has been provided.
[2015-01-30T06:07:59+00:00] WARN: Original Run List: []
[2015-01-30T06:07:59+00:00] WARN: Overridden Run List: [recipe[hello]]
Compiling Cookbooks...
Converging 1 resources
Recipe: hello::default
  * log[Hello World] action write


Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 1.476360857 seconds

こんな見づらいHello Worldは初めてだけど、ちゃんとHello Worldできたっぽい。

VMは用済みなのでvgrant destroyで一旦廃棄する。
ちなみに単に止めたい場合はvagrant haltです。

[vagrant@localhost ~]$ exit
$ vagrant destroy
Are you sure you want to destroy the 'default' VM? [y/N] y
[default] Forcing shutdown of VM...
[default] Destroying VM and associated drives...

ログインして作業するのがめんどくさいのknife-soloを入れて、手元のマシンからリモートでレシピ書いて適用できるようにする。

$ gem install knife-solo

Chefのリポジトリデータ初期化する。

$ knife solo init .
/Users/minamijoyo/.rvm/gems/ruby-2.2.0/gems/chef-12.0.3/lib/chef/data_bag_item.rb:161: warning: circular argument reference - data_bag
WARNING: No knife configuration file found
Creating kitchen...
Creating knife.rb in kitchen...
Creating cupboards...

あれ、なんかまずそうな警告できた。

ググってみたら、Chefのバグっぽい。
https://github.com/chef/chef/pull/2707
https://github.com/chef/chef/pull/2816

とりあえずdata_bag_item.rb:161のdata_bagの箇所をこんな感じで直しとけばよいようです。
既にプルリクマージされてるのでそのうち最新版も治ると思う。一時的な問題。

-    def destroy(data_bag=data_bag, databag_item=name)
+    def destroy(data_bag=data_bag(), databag_item=name)

とりあえず直したら警告出なくなった。

$ knife solo init .
Creating kitchen...
Creating knife.rb in kitchen...
Creating cupboards...

knife solo booststrapでchef-soloをゲスト側に入れる。
ちなみにbootstrapはクックブックの適用まですするので手前で止めるならprepare。

$ vagrant up
$ knife solo bootstrap webapp
$ vagrant destroy

クックブックはgemみたくいろいろコミュニティで公開されてるのだけど、
bundleのように依存関係解決しながらクックブック取り込めるberkshelfも入れておく。

$ gem install berkshelf

GemfileみたいなBerksfileというのを作る。

site :opscode

cookbook 'yum-epel'
cookbook 'apache2'

bundleに相当するberksコマンドを実行する。

$ berks
DEPRECATED: Your Berksfile contains a site location pointing to the Opscode Community Site (site :opscode). Site locations have been replaced by the source location. Change this to: 'source "https://supermarket.chef.io"' to remove this warning. For more information visit https://github.com/berkshelf/berkshelf/wiki/deprecated-locations
Resolving cookbook dependencies...
Fetching cookbook index from https://supermarket.chef.io...
Installing apache2 (3.0.0)
Installing logrotate (1.8.0)
Installing iptables (0.14.1)
Installing yum (3.5.2)
Installing yum-epel (0.6.0)

なんかDEPRECATEDって出てるけどsite :opscodeじゃなくて
source "https://supermarket.chef.io"が推奨っぽい。

source "https://supermarket.chef.io"

cookbook 'yum-epel'
cookbook 'apache2'

こうしたら警告でなくなった。

$ berks
Resolving cookbook dependencies...
Using apache2 (3.0.0)
Using logrotate (1.8.0)
Using iptables (0.14.1)
Using yum (3.5.2)
Using yum-epel (0.6.0)

これはレシピをダウンロードしてるだけで、VM作ってるわけではない。
なのでさっきと同じくVM作る。

$ knife solo bootstrap webapp

nodes/webapp.jsonに適用したいレシピを書く。

{
  "run_list": [
    "recipe[yum-epel]",
    "recipe[apache2]"
  ],
  "automatic": {
    "ipaddress": "webapp"
  }
}

クックブックを適用する。

$ knife solo cook webapp

こんなかんじで、コミュニティのいろんなクックブックを取り込めるので汎用的なものであれば自分でレシピ書かなくてもよくなって楽ちん。ただ、どんな設定されてるのかレシピまで読まないと分からないので一長一短なところある。簡単なのなら自分で書いた方がわかりやすいかもしんない。

設定値をカスタマイズしたい場合、例えばapacheのリッスンポートはデフォルト80番ポートになってる。

$ ssh webapp
Last login: Fri Jan 30 09:15:49 2015 from 10.0.2.2
[vagrant@localhost ~]$ sudo netstat -tanp | grep httpd
tcp        0      0 :::80                       :::*                        LISTEN      2749/httpd
[vagrant@localhost ~]$ exit

これはcookbooks/apache2/attributes/default.rbの中の241行目あたりの以下の設定行で定義されてる。

default['apache']['listen_ports']      = %w(80)

なので、nodes/webapp.jsonで属性を上書きすれば変えられる。
例えばこんなかんじで8080にしてみる。

{
  "apache": {
    "listen_ports" : [8080]
  },
  "run_list": [
    "recipe[yum-epel]",
    "recipe[apache2]"
  ],
  "automatic": {
    "ipaddress": "webapp"
  }
}

適用するとちゃんとポート番号が変わってる。

$ knife solo cook webapp
$ ssh webapp
Last login: Fri Jan 30 09:30:01 2015 from 10.0.2.2
[vagrant@localhost ~]$ sudo netstat -tanp | grep httpd
tcp        0      0 :::8080                     :::*                        LISTEN      2749/httpd
[vagrant@localhost ~]$ exit

変更できるパラメータはattributesの中探してみたらよいんじゃないかな。結構複雑なのでお腹いっぱいなかんじになるけど。

ところでvagrant-omnibusプラグインというのを入れれば、Vagrant起動からそのまま直接クックブックが適用できるようになる。

$ vagrant plugin install vagrant-omnibus
Installing the 'vagrant-omnibus' plugin. This can take a few minutes...
Installed the plugin 'vagrant-omnibus (1.4.1)'!

Vagrantファイルの中にchefの設定が入ってきて密結合なかんじが好み分かれそうだけど、一発で起動からクックブック適用まで出来るようになる。

Vagrantfileをこんなかんじで編集する。

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "opscode-centos-6.5"
  config.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box"
  config.omnibus.chef_version = :latest

  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "./cookbooks"
    chef.json = {
      apache: {
        listen_ports: [8080]
      }
    }
    chef.run_list = %w[
      recipe[yum-epel]
      recipe[apache2]
    ]
  end
end

ポイントは

config.omnibus.chef_version = :latest

としておくことと、jsonのところが微妙にハッシュのシンボルになってて書き方が違うの注意。
vagrant up --provisionとするとVM起動したあとに自動でchef-soloとか入れて、クックブックの適用までしてくれる。

$ vagrant destroy
$ vagrant up --provision

できたら確認。ちゃんと適用されてる。

$ ssh webapp
[vagrant@localhost ~]$ sudo netstat -tanp | grep httpd
tcp        0      0 :::8080                     :::*                        LISTEN      2489/httpd
[vagrant@localhost ~]$ exit

なんとなく使い方わかってきた。エコシステムが成長しまくってツール覚える学習コスト高いけど、触ってたらそのうち慣れるに違いない。
長くなってきたので続きはまた今度。