このエントリーをはてなブックマークに追加

chef-soloで環境設定

chefはサーバ構成管理ツールです。構成を設定ファイルとして書き残しておき、chefを実行すればそのとおりにサーバを構築してくれます。また、何度実行しても同じように構築してくれますし、サーバ構成をいじったとしても修正してくれます。

というわけで、chefを使ってみたのでそのメモ。

事前準備

chefはrubyで作られていますので、rubyが必要です。また、chef自体のインストールにはgemが楽ちんです。

% gem install chef

chefにはサーバなどがありますが、今回は単独で実行できる chef-solo のみを使います。

雛形作成

後述するcookbookなどの雛形を作ってくれるRakefileがopscodeのサイトにあります。

githubのopscodeから取ってきます。

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

この構成をそのまま使うのが吉です。また、ここにあるRakefileを使うことで、cookbookの雛形作成も楽に出来ます。

cookbook

cookbookとはRecipeなどの設定ファイルをひとまとめにしたディレクトリ。その中身はcookbookは以下のディレクトリで構成されます。

  • attributes
  • definitions
  • files
  • metadata.rb
  • providers
  • recipes
  • resources
  • templates

これらは上記Rakefileを使えば、

% rake new_cookbook COOKBOOK=test

と打つだけで雛形を作ってくれます。

recipe

レシピです。このレシピに書いてあるとおりにサーバが設定されます。

例えば、ディレクトリを作ってリンクを張る、という設定はこんな感じです。

directory "/tmp/tmpdir" do
  mode "0755"
  action :create
end

link "/tmp/link" do
  to "/tmp/tmpdir"
end

ここで、"directory"と"link"をResourceと呼びます。Resourceは順番に意味がありますので、directory -> link の順番で呼ばれます。

Resourceにはいろいろあって、例えば、remote-fileというResourceはsourceで指定した、cookbook/files以下にあるファイルを指定の場所にコピーします。ディレクトリまるごとの場合はremote-directoryを使います。

 remote_directory "/tmp/dir" do
   source "tmpdir"
   owner "apache"
   group "apache"
   mode "0755"
end

また、templateというresourceを使うと、cookbook/template以下にある、erbで書いたテンプレートファイルに値を埋めてファイルを作成してくれます。

詳しくはResourceの説明を見てください。

これらのResourceは「何度繰り返しても同じ結果になります」大事です。英語で言うと、idempotentです。冪等性(べきとうせい)とも言います。

shell scriptでmkdir hogeと二回打つと、二回目は「File exists」というエラーになりますよね?(-p使えという話もありますが)しかし、chefを使った場合ではエラーになりません。これはchefが自動的に判断して実行しないからです。

また、templateであれば、もし手動でなにかを書き換えていた場合にはその変更は書き潰されますし、ownerを変更した場合はchefのレシピ通りに戻されます。

script Resource

script Resourceはbash、ruby、Pythonなどのスクリプトを実行出来ます。強力な反面、idempotentではなくなる場合もあるため、使用には注意が必要です。できるだけ使わない方がいいでしょう。

とはいえ、make installが必要な場合等はあるんですけどね。

Action

httpd.confを書き換えたらhttpdをreloadする、などのように、「このResourceを実行したらこのResourceを実行する」などはnotifiesを使います。

このあたりはRubyマガジン Chef でサーバ管理を楽チンにしよう! (第 2回)を参考にしてみてください。

chef-soloの実行

最低限CookbookとRecipeがあればchef-soloは実行できます。以下の二つのファイルを作成しましょう。defaultではsolo.rbは /etc/chef/solo.rb を見に行くようです。

chef.json
chef-soloを実行するターゲットの設定ファイル
solo.rb
cookbookのパスなどを設定
{
  "run_list": [
    "recipe[test]",
    "recipe[test::test2]"
 ]
}

run_list内のrecipeは複数書けます。ここで[test::test2]とあるのは、

test/
    recipes/
            default.rb
            test2.rb

のtest2.rbを実行しますということです。solo.rbはこう書きます。

file_cache_path "/tmp/chef-solo"
cookbook_path ["/path/to/cookbooks", "/path/to/cookbooks2"]
role_path "/path/to/roles"
log_level :debug

主にパス設定ですね。cookbook_pathは複数設定出来ます。role_pathは複数設定出来ません。

さて、この二つのファイルを用意しておけばあとは以下のコマンドを打つだけです。

% sudo chef-solo -c /path/to/solo.rb -j /path/to/chef.json

log_levelをdebugにしているのでかなり多くの情報がずらずらーっと出てくると思います。

attributes

ここまでで一通りできるのですが、もう一つattributesを覚えておくと今後便利です。

attributesとは、recipeやテンプレートで使える変数を外出ししたものです。

例えば、 cookbook/attributes/default.rb にこう書きます。

default["apache"]["version"] = "2.2.22"
default["apache"]["conf_dir"] = "/usr/local/apache/conf/"

こう書いておくと、Recipeでたとえばこう書けます。

template "#{node.apache.conf_dir}/httpd.conf" do
  source "httpd.conf"
  owner "apache"
  group "apache"
  mode "0644"
end

また、テンプレートの中で

ServerRoot "/usr/local/apache-<%= node.apache.version %>"

と書けます。

このattributesはjsonの中で上書きできます。

"override_attributes": {
      "apache": {"version": "2.4.2", "conf_dir": "/etc/apache/conf/"}
},

このように変数をレシピの外に出しておくことで、レシピを変えることなく環境に合わせた設定ができます。

definitions

definitionは自分で新しいResourceを作れる仕組みです。definitionを使うことで、何度も同じResourceを定義する必要がなくなります。

definitionは cookbook/definitionsの下に、例えば postgresql_func.rb という名前で作ります。

define :postgresql_func, :action => :create, :owner => "postgres" do
  # 名前がpostgresql_func、デフォルトの設定が残り
  contrib = node.postgresql.contrib_dir
  # node.postgresql.contrib_dirはattributesで設定

  case params[:action]
  when :create
      execute "psql -1 -f #{contrib}/#{params[:mod]}.sql  #{params[:name]}" do
        # params[:name] はpostgres_funcの引数
        user "postgres"
        not_if "psql -At1 -c \"SELECT prosrc FROM pg_proc \" #{params[:name]} | grep '#{params[:func]}'", :user => "postgres"
    end

  when :drop
    execute "psql -1 -f #{contrib}/uninstall_#{params[:mod]}.sql
    #{params[:name]}" do
      user "postgres"
      not_if "psql -At1 -c \"SELECT prosrc FROM pg_proc \" #{params[:name]} | grep '#{params[:func]}'", :user => "postgres"
    end
  end
end

こうしておけば、あとはrecipeの中でこう使えます。

postgresql_func "template1" do
  mod "dblink"
  func "dblink_connect"
end

postgresql_funcの引数(ここでは"template1")が params[:name] として渡されます。

なお、definition自体はその中で包含するResourceで置き換えられます。つまり、実際にはdefinitionというResourceは作られません。そのため、definitionに対してActionを送ることは出来ません。もしResourceに対してActionを送りたければ Provider を使うとのことです。

role

roleはattributeの設定やrun_listの設定をまとめたものです。使うには以下のように solo.rb にrole_pathを加えておきます。(cookbook_pathとは違い、複数指定できません)

file_cache_path "/var/chef-solo"
cookbook_path "/var/chef-solo/cookbooks"
role_path "/var/chef-solo/roles"

で、実際のroleはこんな感じ。

/var/chef-solo/roles/test.json

{
 "name": "test",
 "default_attributes": { },
 "override_attributes": { },
 "json_class": "Chef::Role",
 "description": "This is just a test role, no big deal.",
 "chef_type": "role",
 "run_list": [ "recipe[test]" , "recipe[test2]"]
}

こういうファイルを作っておけば、あとは chef.json は

{ "run_list": "role[test]" }

と書くだけで、test roleが適用されます。attributeなんかもroleの中に書いておけるので、依存性が低くなります。