[chef] attributeの理解

今回はattributeの話。
お約束ですが、この記事の内容は私が一人で分かったつもりになって書いていることなので、誤認やウソが書いてありましたらご指摘してくださると助かります。

attributeとは

Recipeではできるだけ汎用的に書いた方がいいという雰囲気がありますが、それはその通りだと思います。
その汎用性を持たせる機能を担っているのが、providerとこのattributeでは無いでしょうか。
providerはOSなどの環境を吸収してくれて、attributeは変数として使うことができます。

分からなかったのはあちこちでattributeが設定できること

CookbookやRecipeを調べてたり、ドキュメントを読んでいたりすると、あちこちでdefault_attributesやらoverride_attributesを見かけます。
いったいどこでattributeを設定するのが正しいの?と混乱してしまっていました。

その混乱を鎮めたのがドキュメント以下の記述。

Chef > Home > Chef Essentials > Attributes > Precedence

これを見ると設定箇所によってプライオリティがあり、どこで設定するのが適切かということが分かった気がします。

イメージとしては、CookbookのCOOKBOOK/attribute/default.rbではデフォルト値を設定。
roleのdefault_attributesではそのRoleに適した値を設定。
という感じでしょうか。

具体例

具体例を挙げてみます。
シナリオはいつもの通りAWS EC2でchefを使うという想定です。
複数のAWSアカウントを持っていて、横断的にchefを使いたい。
例としてAWSアカウントの各種認証情報をサーバに保管するRecipeを作ります。

template "/path/to/credencial" do
 source "credencial.erb"
 owner "root"
 group "root"
 mode 0644
 variables(
  :cert      => node["ec2"]["cert"],
  :pk        => node["ec2"]["cert"],
  :awsid     => node["ec2"]["awsid"],
  :accesskey => node["s3"]["access_key_id"],
  :secretkey => node["s3"]["secret_key"],
  :region    => node["ec2"]["region"]
 )
end

これは/path/to/credencialというファイルにAPIの証明書やらaccesskey_idなどの情報を記載したファイルのResourceです。
もちろんCOOKBOOK/templates/default/credencial.erbというテンプレートファイルも用意します。
そして、COOKBOOK/attribute/default.rbに以下のように上記resourceで使っているattributeのデフォルト値を設定します。

# s3 key 
default["s3"]["access_key_id"] = ""
default["s3"]["secret_key"] = ""

# ec2 credencial
default["ec2"]["key_location"] = ""
default["ec2"]["cert"] = ""
default["ec2"]["pk"] = ""
default["ec2"]["awsid"] = ""
default["ec2"]["region"] = "ap-northeast-1"

デフォルト値と言ってもそれぞれ、適切な値を入れないと意味がないので、適当に空に設定してみました。

次にRoleを作ります。
account_aというAWS Accountに紐づいたRole[account_a]というroleを作ります。

name "account_a"
description "AWS account_a account servers"
run_list("recipe[Ec2Init]")
default_attributes({
  "s3" => {
    "access_key_id" => "xxxxxxxxxxxxxxxx",
    "secret_key" =>  "xxxxxxxxxxxxxxxxxxx"
  },
  "ec2" => {
    "key_location" => "hoge",
    "cert" => "cert-xxxx.pem",
    "pk" => "pk-xxxx.pem",
    "awsid" => "xxxx"
  }
})

こんな感じで、attributeを便利に使うことができました。

まとめ

attributeが使えるようになって、汎用性がぐっと広がりました。
やってみて分かったんですが、attributeは最終的にnodeに付与されて、node['hoge']['piyo']という感じで指定するんですね。

次回はなにかな?

ChefのRecipeを書く(1)

recipeを書いてみる

前回まででとりあえずChef-soloが動くことは確認できたので今回はrecipeを書いてみます。

仕様を決める

recipeを書き始める前に、どのようなことをしたいかを考えたいと思います。
実際の環境はAWSのEC2なのでそれを想定して考えてみるととりあえず以下の通りにしたいと思います。

  • /mnt/logを作る
    • SymLinkで/logにする
  • /mnt/tmpを作る
    • パーミッションを1777にする
    • SymLinkで/tmpにする
  • ntpdをインストール
    • /etc/ntp.confを適当に設定する
    • ntpユーザで動かす

とりあえずざっくりこれを目指したいと思います。

ディレクトリの操作

まずは/mnt/logについて書いていきます。
cookbooks/base_packages/recipes/default.rbに以下を記述。

directory "/mnt/log" do
 owner "root"
 group "root"
 mode "0755"
end

試しに実行してみます。

$ sudo chef-solo -c .chef/solo.rb -j .chef/chef.json
INFO: *** Chef 0.10.8 ***
INFO: Setting the run_list to ["recipe[base_packages]"] from JSON
INFO: Run List is [recipe[base_packages]]
INFO: Run List expands to [base_packages]
INFO: Starting Chef Run for chef-test-01
INFO: Running start handlers
INFO: Start handlers complete.
INFO: Processing directory[/mnt/log] action create (base_packages::default line 20)
INFO: directory[/mnt/log] created directory /mnt/log
INFO: directory[/mnt/log] owner changed to 0
INFO: directory[/mnt/log] group changed to 0
INFO: directory[/mnt/log] mode changed to 755
INFO: Chef Run complete in 0.024623 seconds
INFO: Running report handlers
INFO: Report handlers complete

$ ls /mnt
log/

できましたー。

次に/mnt/logのSymLinkとして/logを作りたいのでさらに以下を記述。

link "/log" do
 to "/mnt/log"
end

実行してみるとエラーとなる。
既に/logというディレクトリがありますよーというエラーが出てしまいました。
確かに/logというディレクトリはもともとあって、今まではそれを一度消してSymLinkを作っていました。
そこまで一気にやってくれることを期待したのですが、やっぱり始めに/logディレクトリを消してSymLinkを作らないといけないようです。
puppetはその辺よろしくやってくれそうな気がするんですが、どうなんでしょうか。

ということで以下のように変更。

directory "/log" do
 action :delete
 only_if "test -d /log"
end

link "/log" do
 to "/mnt/log"
end

これでできました。  
と思ったら、もう一回実行すると
directory “/log”のところでエラーが出てしまいました。
どうやら/logはSymLinkなので「ディレクトリじゃないよ」と怒られている様子。
only_ifが効かないのはなぜ??

原因不明につき、今回はこれまで。

雑感

ここまでやってみて思ったのは、僕はpuppetの方が好みのような気がするということです。何となくですが、puppetは運用目線、Chefは開発者目線のような雰囲気を感じました。
とは言ってもChefの調査を続けます。

追記(2012/4/23)

最後のonly_ifが効かない件、後日再び、そのままの設定で実行したらなんでか分かりませんが問題なく動きました。
なんだったんだろう?

Chef調査

Chef調査開始

chefにはスタンドアロンでも使えるしServer/Clientでも使える。
実際使う時はServer/Clientだけども、まずどんなものかを知るためにスタンドアロンのchef-soloをいじってみることにしました。

で、やり方をググるとさっそくchef-soloで作業環境構築の自動化(ひげろぐ)がヒット。
chef-soloの導入が丁寧に書いてあるので参考にさせていただいた。

既にRuby1.9.2はインストールしてあり(CentOS5.7は1.8.5だったのでSourceを拾ってきてBuildした)、chefもgem installで導入済みだったのでchef-repoのところから始めた。

git clone git://github.com/opscode/chef-repo.git

してChefリポジトリを構築。

.chef/solo.rbと.chef/chef.jsonを作ってさっそくchef-soloを実行してみる。

$ sudo chef-solo -c .chef/solo.rb -j .chef/chef.json
<internal:lib/rubygems/custom_require>:29:in `require': no such file to load -- openssl (LoadError)
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from /usr/local/lib/ruby/1.9.1/net/https.rb:92:in `<top (required)>'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    --(中略)--
    from /usr/local/lib/ruby/gems/1.9.1/gems/chef-0.10.8/lib/chef.rb:25:in `<top (required)>'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from /usr/local/lib/ruby/gems/1.9.1/gems/chef-0.10.8/lib/chef/application/solo.rb:18:in `<top (required)>'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from <internal:lib/rubygems/custom_require>:29:in `require'
    from /usr/local/lib/ruby/gems/1.9.1/gems/chef-0.10.8/bin/chef-solo:23:in `<top (required)>'
    from /usr/local/bin/chef-solo:19:in `load'
    from /usr/local/bin/chef-solo:19:in `<main>'

む。何やらエラーの様子。
rubyバージンなのでよくわからないが、opensslがロードできないと言われてそうだ。

openssl-develが無かった

エラーメッセージでググると
require ‘twitter’のエラー(opensslがロードできない)と言われる(Ruby_log)
を発見。
さっそく、書いてある通りにやってみた。

$ cd /path/to/ruby-src/ext/openssl

$sudo ruby extconf.rb 
=== OpenSSL for Ruby configurator ===
=== Checking for system dependent stuff... ===
checking for t_open() in -lnsl... no
checking for socket() in -lsocket... no
checking for assert.h... yes
=== Checking for required stuff... ===
checking for openssl/ssl.h... no
=== Checking for required stuff failed. ===
Makefile wasn't created. Fix the errors above.

openssl/ssl.hがないって言われたのでyumでopenssl-develをインストール。

sudo yum install openssl-devel.x86_64
sudo ruby extconf.rb

通ったのでbuild。
make; make install

node_nameでエラー

これでopensslが入ったと思うので、再度chef-soloを実行してみる。

$ sudo /usr/local/bin/chef-solo -c .chef/solo.rb -j .chef/chef.json 
[Fri, 20 Apr 2012 08:48:45 +0900] INFO: *** Chef 0.10.8 ***
[Fri, 20 Apr 2012 08:48:47 +0900] FATAL: Stacktrace dumped to /tmp/chef-solo/chef-stacktrace.out
[Fri, 20 Apr 2012 08:48:47 +0900] FATAL: Chef::Exceptions::CannotDetermineNodeName: Unable to determine node name: configure node_name or configure the system's hostname and fqdn

chef-soloは動いた。でもまたエラー。
調べてみるとnode_nameという値を設定しないといけないみたい。
.chef/solo.rbに追加する。

vi .chef/solo.rb

file_cache_path "/tmp/chef-solo"
cookbook_path "/home/piyo/chef-repo/cookbooks"
node_name "chef-test-01" ←追加

再度実行して、

FATAL: Chef::Exceptions::CookbookNotFound: Cookbook base_packages not found. If you're loading base_packages from another cookbook, make sure you configure the dependency in your metadata

とエラーが出るけど、レシピを書いていないので、これは想定通り。
これで、初期動作の確認は完了です。

cookbookの作成

続けて、書いてある通り
$ rake new_cookbook COOKBOOK=base_packages
を実行してcookbookを作成。
お約束の「knifeを使いなさい」が出ましたのでそっちもやってみました。

$ knife cookbook create COOKBOOK
WARNING: No knife configuration file found
** Creating cookbook COOKBOOK
ERROR: Errno::EACCES: Permission denied - /var/chef

knife configurationが無いと言われてしまった。
参考資料は先に進んでいるがここは勉強なのでknifeの動作環境の整備をしてみる。

knife configure

Where should I put the config file? [~/.chef/knife.rb] /home/piyo/knife-repo/.chef/knife.rb

configurationファイルの場所を指定する。

Please enter the chef server URL: [http://localhost:4000]

よくわからないのでそのままENTER.

Please enter an existing username or clientname for the API: [www] 

よくわからないのでそのままENTER.

Please enter the validation clientname: [chef-validator] 

よくわからないのでそのままENTER.

Please enter the location of the validation key: [/etc/chef/validation.pem] 

よくわからないのでそのままENTER.

Please enter the path to a chef repository (or leave blank): /home/piyo/chef-repo

先ほど構築したリポジトリのディレクトリを指定。

これで動くでしょうか。
先ほどのcookbook作成をもう一回実行してみます。

$ knife cookbook create COOKBOOK -c .chef/knife.rb 
** Creating cookbook COOKBOOK
** Creating README for cookbook: COOKBOOK
** Creating metadata for cookbook: COOKBOOK
$ ls cookbooks
COOKBOOK/  README.md  base_packages/

できた!でも今はいらないので消しときます。

とりあえずここまで。
次はいよいよレシピ作りに取りかかります。
何やらRuby全開の予感がして、手こずりそうです。