2012年12月18日火曜日

Chef Recipeの書き方と運用

Chefがインストールできたので次はRecipeを書いていきます。


Recipeの作成


ChefはRecipeをバージョン管理することができます。ただSVNやGitのような高機能なバージョン管理システムはもっていないので複数人でRecipeを編集する場合は編集点を明確にする上でもSVNやGitを導入した方がよいでしょう。

リポジトリは'knife configure -i'で設定した場所になります。
デフォルトだと'/var/chef/cookbooks/'になります。

knifeコマンドでbaseというRecipeを作成します。

#knife cookbook create base


以下のようなファイルとディレクトリが作成されたと思います。

-attributes/
-CHANGELOG.md
-definitions/
-files/
-libraries/
-metadata.rb
-providers/
-README.md
-recipes/
-resources/
-templates/

普段使わないものもあるので、利用頻度の高そうなものから書きたいとおもいます。

recipes


このディレクトリの中にdefault.rbというファイルがあります。
このファイルにRecipeを書きます。
Rubyが使えます。

'/tmp/work'というディレクトリを作成するには以下のように書きます。

directory "/tmp/work" do
owner "root"
group "root"
mode "0777"
action :create
end


directory部分はResourcesと言われまして、Chef側で予めサポートされているものになります。
標準Resourcesだけで十分Recipeを書くことができます。
Chef Resources一覧

Resourcesを使いこなすことでグンとRecipeを書くスピードが上がりコストも下がるので一度目を通しておくとよいでしょう。

スクリプトを記述することもできます。
ただ、毎回実行されてしまうためこのようにtar展開などをすると毎回上書きされ実行時間が長くなるのであまりオススメできません。
スクリプトはPerl,Ruby,bash,csh,Pythonが対応しています。


script "install_something" do
interpreter "bash"
user "root"
cwd "/tmp"
code <<-EOH
wget http://www.example.com/tarball.tar.gz
tar -zxf tarball.tar.gz
cd tarball
./configure
make
make install
EOH
end


files


クライアントに配布したいファイルをこのディレクトリに置きます。
CHEF-REPO/base/files/default/New_File.tgzと配置したとします。

Recipeでこのように書くと'/tmp/'に配置されます。

cookbook_file "New_File.tgz" do
source "/tmp/New_File.tgz"
end


filesはパッケージや設定ファイルを含めてファイルを配布するには非常に便利ですが設定ファイルはtemplateで配布することを推奨したいです。
そうすることでChefの本来のパワーを自然と発揮できるようになるはずです。

libraries


recipesと殆ど同様の使い方ができます。
RubyとChef既存のResourcesで記述でき独自のクラスを定義することでChefを拡張できます。

moduleを定義することもできますし、defで定義してrecipeで利用することもできます。
your_cookbook/libraries/your_example_library.rb

# define a module to mix into Chef::Recipe
module YourExampleLibrary
def your_function()
# ... do something useful
end
end


your_cookbook/recipes/default.rb


# open the Chef::Recipe class and mix in the library module
class Chef::Recipe
include YourExampleLibrary
end

your_function()


definitions


機能的にはlibrariesと近いですがChefの拡張ではなく既存のResourceを組み合わせて、再帰可能なResourceを定義します。
Rubyが使えます。

apache_siteというdefinitionsを定義します。
apache_site Definition

define :apache_site, :enable => true do
include_recipe "apache2"

if params[:enable]
execute "a2ensite #{params[:name]}" do
command "/usr/sbin/a2ensite #{params[:name]}"
notifies :restart, resources(:service => "apache2")
not_if do
::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") or
::File.symlink?("#{node[:apache][:dir]}/sites-enabled/000-#{params[:name]}")
end
only_if do ::File.exists?("#{node[:apache][:dir]}/sites-available/#{params[:name]}") end
end
else
execute "a2dissite #{params[:name]}" do
command "/usr/sbin/a2dissite #{params[:name]}"
notifies :restart, resources(:service => "apache2")
only_if do ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") end
end
end
end


recipeないでapache_siteに対して渡された値はparams[:hoge]で取得できます。



# Enable my_site.conf
apache_site "my_site.conf" do
enable true
end

# Disable my_site.conf
apache_site "my_site.conf" do
enable false
end


definitionsは読み込まれたrecipeないで包括的に利用されるためActionメソッドを渡すことはできません。
Actionメソッドを使いたい場合はResouceを使います。
cookbook {cookbook_name}でcookbookを指定することも可能なのでファイルなどは特定のrecipeにまとめることができます。


templates/attributes


erbが使えます。
設定ファイルはerbを書かなくてもtemplatesで配布すること推奨します。
templateかrecipeにnilの処理を入れておかないとnilがあった際にエラーがでます。
node[:hoge]の部分はattributeから取得できる値に展開されます。

A template (templates/default/sudoers.erb)


#
# /etc/sudoers
#
# Generated by Chef for <%= node[:fqdn] %>
#

Defaults !lecture,tty_tickets,!fqdn

# User privilege specification
root ALL=(ALL) ALL

<% @sudoers_users.each do |user| -%>
<%= user %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL
<% end -%>

# Members of the sysadmin group may gain root privileges
%sysadmin ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL

<% @sudoers_groups.each do |group| -%>
# Members of the group '<%= group %>' may gain root privileges
%<%= group %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL
<% end -%>


attributeを定義しておくと、プラットフォームやOSが代わっても柔軟に対応させることができます。個々のパラメータなどはattributeに書いておきましょう。

default["authorization"]["sudo"]["groups"] = [ "sysadmin","wheel","admin" ]
default["authorization"]["sudo"]["users"] = [ "jerry","greg"]


A template resource (recipes/default.rb)

template "/etc/sudoers" do
source "sudoers.erb"
mode 0440
owner "root"
group "root"
variables({
:sudoers_groups => node[:authorization][:sudo][:groups],
:sudoers_users => node[:authorization][:sudo][:users]
})
end



recipeの更新


recipeを書き終えるとcookbookの更新を行わければ最新バージョンは適用されません。


//cookbooK_nameのrecipeを更新
#knife cookbook upload {cookbooK_name}

//すべてのrecipeを更新
#knife cookbook upload -a


metadata.rbでバージョンを0.1.1にあげてuploadするとバージョンがあがることがわかります。

Uploading hoge [0.1.1]


バージョンを区切ると前回のバージョンは別で保持されるので、クライアントごとにバージョンを指定したり特定のバージョンに固定化させることもできます。バージョンの運用方法はそれぞれあるので環境に適した方法を選んでください。

オススメは、とにかく編集した後はバージョンを上げるてクライアントにはバージョン指定でrecipeを適用する方法でそうすることでデグレードを最小限に防ぐことができます。

recipeの適応


recipeの適応はクライアント側でchef-clientを実行するのみです。
chef-clientを実行すると即座に適応が開始されるためDryRunを適応前に行いましょう。

chef-client --why-run


knife sshを使いこなせ


数百台レベルのサーバになるとchef-clientの実行自体が容易ではないのでサーバからknifeコマンドでクライアントのrecipeを適用します。

knife sshコマンドはオプションが多数あるので一例です。
hostnameの部分はhostnameを元にマッチしたホストにSSHを実行します。デフォルトはFQDNになります。

interactive オプションをつけることで接続できたクライアントに対してインタラクティブに操作することが可能になります。

-aは接続に利用するattributeを選択でいます。attributeで取得できる値であればなんでも利用可能なのでIPアドレスベースでの接続も可能です。例はhostnameを指定しています。

-x:接続ユーザ
-P:パスワード

knife ssh 'hostname:client-1[0-9]' interactive -x root -P password -a hostname

Opscode Knife

0 件のコメント:

コメントを投稿