Cookbook

Cookbook とは

Chef の Recipe(ソースコード)をとりまとめたものです。
一般的に、ひとつのアプリケーションやサービスに対し、ひとつの Cookbook が用意されることが多いです。 

 

ひな形の生成

ChefDK の chef コマンドで Cookbook のひな形を生成します。

$ cd workspace
$ chef generate cookbook <Cookbook 名>

 

生成されたもの

コマンドを実行すると、以下のファイルが作成されます。

 

Eclipse プロジェクトに変換

Cookbook を編集するうえで、Eclipse が特に有利になる点はありません。
現時点で RHEL 系の日本語環境で比較的マシに利用できるのが Eclipse だっただけです。
好みのエディタで編集してください。

 

.project を生成

スクリプトを実行し .project を生成します。

$ cd <Cookbook 名>
$ gen-eclipse

gen-eclipse は、.project を生成する自作スクリプトです。

手動で作成する場合、.project は以下のような内容です。

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
        <name>${cookbook_name}</name>
        <comment></comment>
        <projects>
        </projects>
        <buildSpec>
                <buildCommand>
                        <name>org.eclipse.dltk.core.scriptbuilder</name>
                        <arguments>
                        </arguments>
                </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.dltk.ruby.core.nature</nature>
        </natures>
</projectDescription>

 

プロジェクトをインポート

Eclipse を起動し、次の手順でプロジェクトをインポートします。

File -> Import...

 

General -> Existing Projects into Workspace

 Next

 

「/home/vagrant/workspace/<Cookbook 名>」を追加

Finish 

 

Package Explorer に隠しファイルも表示させる

標準では .kitchen.yml が見えないので、次の手順で隠しファイルを表示します。

Package Explorer の ▽ を選択 -> Filters…


 「.*resources」のチェックを外す
OK

 

Recipe

Recipe とは

Chef では、サーバーのあるべき状態を Recipe(ソースコード)として記述します。
Recipe は「ファイルのダウンロード」や「コマンドの実行」など、さまざまな機能をもつ Resource から構成されます。


Resource は Chef 公式のものだけで40種類以上ありますが、基本的な構文はどれも同じで、
次のように、ユニークな Name をもち、1つ以上の Attribute によりパラメータを指定でき、実行時の動作を Action で指定します。

resource "name" do
  attributeA "valueA"
  attributeB "valueB"
  action :action
end

 

次の Recipe は「'/tmp' ディレクトリで、 'tar zxf archive.tar.gz' というスクリプトを、bash で実行する、'extract tar file' という名前の Resource」を定義したものです。

bash 'extract tar file' do
  cwd '/tmp'
  code 'tar zxf archive.tar.gz'
  action :run
end

ここでは Chef 標準の bash Resource をベースに、cwd Attribute に実行時のディレクトリを、code Attribute に実行するスクリプトを指定し、
Action には Resource の実行時にスクリプトが実行されるように :run を指定していて、
これらの指定を行った Resource に 'extract tar file'  という Name をつけています。


指定できる Action や Attribute は Resource ごとに異なるので、Resource のドキュメントを逐一確認するようにしてください。
bash Resource であれば ここ です。

Resource のドキュメントは見つけにくいです。
Chef の公式ドキュメントから辿るよりも、Google で「Opscode bash」などと検索したほうが早く見つけられたりします。
また、Mac であれば Dash に Chef のドキュメントがあるので、これを使うのもいいかもしれません。

 

Resource の実行順序

Recipe は上から順に解釈され、Resource は基本的には Recipe に定義した順で実行されます。
たとえば、「ファイルをダウンロード」してから「ダウンロードしたファイルを解凍」したい場合は、次のように記述できます。 

remote_file '/tmp/archive.tar.gz' do
  source 'http://path/to/archive.tar.gz'
  action :create
end

bash 'extract tar file' do
  cwd '/tmp'
  code 'tar zxf archive.tar.gz'
  action :run
end

notifies などを利用して、Resource の実行順序を制御することも可能です。

Recipe に書いたコードは基本的に Recipe が解釈されるときに実行されます。
Resource が実行されるタイミングでコードを実行したい場合は ruby_blocklazy を使います。

 

冪等性を確保する

Chef では何度 Recipe を実行しても同じ結果になることが望まれています。
これは Chef 標準の package Resource などを使用していれば Resource 側で確保してくれるのですが、
bash Resource などでは自力でがんばって確保するしかありません。 

 

冪等性を確保するには、not_if や only_if を使用して Resource にガード条件を設定します。

 

例えば、特定のファイルが存在しないときだけ Resource を実行するには次のように書きます。

exexute 'install peco'
  command 'cp /tmp/peco_linux_amd64/peco /usr/local/bin/peco'
  not_if { File.exists?('/usr/local/bin/peco') }
end

この例では、Resource が実行される直前に not_if ブロックの Ruby コードが実行され、結果が false のときだけ Resource が実行されます。
逆に結果が true のときだけ Resource が実行されるようにしたい場合は only_if を使います。

 

また、Ruby のブロックでなく、文字列を直接わたすこともできます。

exexute 'install peco'
  command 'cp /tmp/peco_linux_amd64/peco /usr/local/bin/peco'
  not_if "test -e '/usr/local/bin/peco'"
end

この場合は not_if にわたした文字列がシェルスクリプトとして実行され、実行結果が 0 以外のときだけ Resource が実行されます。
さきほどと同じように、結果が 0 のときだけ Resource が実行されるようにするには only_if を使います。 

 

Recipe を書く手順 

具体的に何をしたいのかを明らかにする

例えば、「あるアプリケーションのコマンドにパスを通したい」場合は、具体的にどのような方法でパスを通すかを決めておきます。
.bashrc に追記してもいいですし、/etc/profile.d/ にスクリプトを置いても実現可能ですが、それぞれ細かい条件が異なりますよね。
.bashrc に書く場合は、どのユーザーを対象とするかを決めなければいけませんし、既に .bashrc に記述されている内容を壊さないようにしないといけません。
/etc/profile.d/ にスクリプトを置く場合は、スクリプトファイル名を決めなければいけませんし、すべてのユーザーにパスを通して問題ないか確認が必要です。
今回は、/etc/profile.d/application.sh に「export PATH=$PATH:/opt/application/bin」という内容のファイルを置くことにします。 

 

使えそうな Resource を探し、仕様を確認する

/etc/profile.d/ にスクリプトを置くとします。これはどの Resource を使えば実現できるでしょうか。
Chef のドキュメント [ http://docs.opscode.com/chef/resources.html ] を見て、使えそうな Resource を探しましょう。
「Chef 実践入門」や「Chef 活用ガイド」といった書籍を利用するのも手です。

/etc/profile.d/ にスクリプトファイルを置く場合は、たとえば次のような手段があります。

各 Resource の詳細はドキュメントを参照して確認してください。

bash Resource や ruby_block Resource でも問題なく実現できますが、「もし既にファイルが存在していたら Resource の実行をスキップする」という処理を書くのが少々面倒です。
template Resource や file Resource には :create_if_missing という Action があり、対象のファイルが存在ないときにだけ Resource を実行させることができます。
template Resource は配置するファイルの内容を動的に制御する機能を備えていますが、 今回配置したいスクリプトファイルは完全に固定のものなので、そこまでの機能は不要です。
なので、file Resource を使うのが適しているでしょう。


Recipe を記述する

使用する Resource が決まったら、ドキュメントを確認しながら ./recipes/default.rb に Recipe を記述します。
default.rb はその Cookbook が標準的に実行する Recipe で、一般的なプログラミングでいうメインメソッドのようなものです。

file Resource のドキュメント [ http://docs.opscode.com/resource_file.html ] を確認すると、name にファイルパスを、content Attribute にファイルの内容を指定することがわかりますので、
その仕様に従って Recipe を記述すればOKです。 

file “/etc/profile.d/application.sh” do
  content “export PATH=$PATH:/opt/application/bin”
  action :create_if_missing
end