Chef実践入門 ~コードによるインフラ構成の自動化 のまとめノート
3 レシピの書き方
3.1 リソースとは
- service package templateといった部品。
- Chefのレシピを書くということは、「Chefというフレームワークが用意したserviceやpackageといったDSLでリソースを定義する」ということ。
3.2 td-agentのレシピを読む
- 複数のリソースをうまく使ったレシピ例を見る
groupとuser
- それぞれOSのグループとユーザを定義するためのリソース。
- アクションとして:createが定義されている。
group 'td-agent' do
group_name 'td-agent'
gid 403
action :create
end
user 'td-agent' do
comment 'td-agent'
uid 403
group 'td-agent'
home 'var/run/td-agent'
shell 'bin/false'
password nil
supports :manage_home => true
action [:create, :manage]
end
- userリソースいろいろ
user リソース名 do
action
:create(作成:デフォルト)
:remove(削除)
:modify(変更:該当ユーザーが存在しない場合エラー)
:manage(変更:該当ユーザーが存在しない場合何もしない)
:lock(ロック)
:unlock(ロック解除)
:nothing(何もしない)
username "ユーザー名(デフォルト値:リソース名)"
comment "コメント(デフォルト値:nil)"
uid uid(デフォルト値:nil)
gid "グループ名(デフォルト値:nil)"
home "ホームディレクトリーのパス(デフォルト値:nil)"
system
nil(デフォルト)
true(システムユーザー)
false(非システムユーザー)
shell "デフォルトシェルのパス(デフォルト値:nil)"
password "パスワードのハッシュ値(デフォルト値:nil)"
supports
:manage_home =>
false(ホームディレクトリーを作成しない:デフォルト)
true(ホームディレクトリーを作成する)},
:non_unique =>
false(uidの重複を認めない:デフォルト)
true(uidの重複を認める)
retries リトライ回数(デフォルト値:0)
retry_delay リトライ間隔[秒](デフォルト値:2)
ignore_failure
false(エラー時に終了:デフォルト)
true(エラー時も継続)
end
directory
- 任意のディレクトリを定義するためのリソース
directory '/etc/td-agent' do
owener 'td-agent'
group 'td-agent'
mode '0755'
action :create
end
caseによるOSの判定
- node['platform']という変数に入っている値を軸にcase文で分岐処理を行っている。
- ubuntu系とRedHat系の場合で分けている。
case node['platform']
when "ubuntu"
dist = node['lsb']['codename']
source = (dist == 'precise') ? "http://packages.treasure-data.com/precise/" : "http://packages.treasure-data.com/debian/"
apt_repository "treasure-data" do
uri source
distribution dist
components ["contrib"]
action :add
end
when "centos", "redhat"
yum_repository "treasure-data" do
url "http://packages.treasure-data.com/redhat/$basearch"
action :add
end
end
AttributeとOhai
Attrbute
- テンプレートやレシピの中から参照できるkey-valueの値を管理する仕組み。
- node['platform']はまさにそのAttrbuteで、ubuntuやredhatといった値が入ってくる。
- Attrbuteはnodeオブジェクトに自分で設定できる。
- Chefがシステムからあらかじめ抽出した値も入ってくる。
Ohai
- ChefはOSから各種情報を集めてAttrbuteを構成するのにOhiとういRubyのライブラリを使う(Chefにpreinstall済み)。
- データをJSON形式で生成してくれる。
# Ohaiの実行委結果
$ ohai | head
{
"cpu": {
"real": 2,
"total": 4,
"mhz": 2400,
"vendor_id": "GenuineIntel\n",
"model_name": "Intel(R) Core(TM) i5-2435M CPU @ 2.40GHz\n",
"model": 42,
"family": 6,
"stepping": 7,
# 引数にキーを指定すると任意の値のみ取得できる
$ ohai platform
[
"mac_os_x"
]
- Ohaiの値をChefで実行したい場合には、レシピ内で node[:platform]のように書いて取得できる。
template、package、service
template "/etc/td-agent/td-agent.conf" do
mode "0644"
source "td-agent.conf.erb"
end
package "td-agent" do
options value_for_platform(
["ubuntu", "debian"] => {"default" => "-f --force-yes"},
"default" => nil
)
action :upgrade
end
service "td-agent" do
action [ :enable, :start ]
subscribes :restart, resources(:template => "/etc/td-agent/td-agent.conf")
end
- 以上がtd-agentのレシピ。
3.3 主要なリソース解説
- Chefが標準で提供しているリソースのうちよく使うものを中心に説明する。
package
基本的な使い方
- OSのソフトウェアパッケージを扱うためのリソース。
- プラットフォームにあわせてパッケージを選択してくれる。
package "git" do
action :install
end
複数パッケージをインストールする
- Rubyのシンタックス使う。
%w{gcc make git}.each do |pkg|
package pkg do
action :install
end
end
バージョンを指定する
- バージョン指定でバージョンを固定できる。
- action:installを:upgradeにすると、レシピを複数回実行したとき既存のパッケージが古い場合に最新版に入れ替える。
package "perl" do
action :install
version "5.10.1"
end
パッケージを削除する
package "perl" do
action :remove
end
パッケージを指定する
- パッケージを指定したファイルからインストールする
package "tar" do
action :install
source "/tmp/tar-1.16.1-1.rpm"
end
オプションを指定する
package "debian-archive-keyring" do
action :install
options :"--force-yes"
end
service
- Webサービスやデータベースのような「サービス」の状態を記述するのがserviceリソース。
基本的な使い方
- Apacheを起動する
- action行でサービスを有効かつ起動している。
- support行でstatus,restart,reloadを有効にしている。
service "httpd" do
action [ :enable, :start ]
supports :status => true, :restart => true, :reload => true
end
Notificationとserviceを組み合わせる
- httpd.confが更新された場合にreloadする
- notifiesは第一引数にアクション、第二引数にresource_type[resource_name]の形式で記述する。
- notifies(通知先)に指定できるのはserviceに限らない。以下のようにexecuteリソースも実行できる。
notifies :run, "execute[test-nagios-config]"
service "httpd" do
supports :status => true, :restart => true, :reload => true
action [ :enable, :start ]
end
template "httpd.conf" do
path "/etc/httpd/conf/httpd.conf"
owner "root"
group "root"
mode 0644
notifies :reload, 'service[httpd]'
end
Notificationのタイミング
- Notificationの実行は、Chef全体の実行の限りなく終盤。
- これにより通知を送る側はあまり順番を気にしなくていい。
- notifiesの第三引数に :immediately を渡すと即座に実行される。
Subscribe
- なんらかのリソースに変化があったらアクションに利用するのがSubscribe。
- 以下は、td-agent.confが更新されたらサービスをrestartするというアクション。
service "td-agent" do
support :status => true, :restart => true, :reload => true
action [ :enable, :start]
subscribes :restart, "template[ts-agent.conf]"
end
template
- 設定ファイルなど外部ファイルを使うリソース。
- Attrbuteの値をテンプレート内で展開したい場合に利用する。
- Attrbuteを使わなければ静的ファイルを操作する目的のcookbook_fileを使って定義する。
基本的な使い方
- httpd.confをテンプレートから生成する。
- テンプレートはクックブックディレクトリ内のtemplates/default/httpd.conf.erbが利用される。
template "httpd.conf" do
path "/etc/httpd/conf/httpd.conf"
source "httpd.conf.erb"
owner "root"
group "root"
mode 0644
end
// 簡略化
template "/etc/httpd/conf/httpd.conf" do
source "httpd.conf.erb"
owner "root"
group "root"
mode 0644
end
// さらに簡略化(templates/default/httpd.conf.erbが利用される)
template "/etc/httpd/conf/httpd.conf" do
owner "root"
group "root"
mode 0644
end
テンプレート内ではAttrbuteが使える
- たとえば、node[:platform] は <%= node[:platform] %> と書く。
- ChefのtemplateリソースはERBが前提。
userとgroup
user
- ユーザー定義の例
user "naoya" do
comment "naoya"
home "/home/naoya"
shell "/bin/bash"
password nil
supports :manage_home => true // ユーザを新規作成した時にホームディレクトリを一緒に作る
action [:create, :manage]
end
group
- グループ追加の例
group "webdb" do
gid 999
members ['naoya', 'inao']
action :create
end
- 既存グループへのユーザ追加の例
group "admin" do
action :modify
members [ 'dikeda' ]
append true
end
directory
- ディレクトリの定義
directory '/etc/td-agent/' do
owner 'td-agent'
group 'td-agent'
mode '0755'
action :create
end
cookbook_file
- cookbook_fileを使うと、クックブックに同梱したファイルを任意のパスへ転送できる。
- templateはAttrbuteを使いたい場合に。cookbook_fileは静的なファイルを扱いたい場合に。
基本
- RPMファイルをテンポラリディレクトリに転送する
- テンポラリディレクトリのパスはハードコードせずにChefが設定で持っているパスを使用する。
cookbook_file "#{Chef::Config[:file_cache_path]}/supervisor-3.0a12-2.el6.noarch.rpm" do
mode 00644
end
- ソースファイルを明示的に指定する。(転送先で名前を変更する)
cookbook_file "#{Chef::Config[:file_cache_path]}/supervisor-3.0.rpm" do
source "supervisor-3.0a12-2.el6.noarch.rpm"
mode 00644
end
チェックサムを利用する
- 破損や改さん防止にチェックサムを使う。
- チェックサムが合致しない場合は実行時エラーになる。
- ChefのファイルチェックサムはSHA-256が前提。
cookbook_file "#{Chef::Config[:file_cache_path]}/supervisor-3.0a12-2.el6.noarch.rpm" do
source "supervisor-3.0a12-2.el6.noarch.rpm"
mode 00644
checksum "012f34db9e08f67e6060d7ab8d16c264b93cba82fb65b52090f0d342c406fbf7"
end
インフラレイヤーのリソース
ifconfig
ifconfig "192.168.30.1" do
device "eth0"
end
mount
mount "/mnt/volume1" do
device "volume1"
device_type :label
fstype "xfs"
options "rw"
end
script
- scriptリソースは内部に定義したシェルスクリプトなどのスクリプトをroot権限で実行できる。
- 冪等性は自分で保証する必用がある。
script(bash)
- perbrewをインストールする際にcurlでインストーラーを実行するが、シェルスクリプトによるインストーラーの扱いがpackageでは難しいので以下のように記述する。
# shellの場合
$ curl -kL http://install.perlbrew.pl | bash
// Chefの場合
bash "install perlbrew" do
user 'vagrant'
group 'vagrant'
cwd '/home/vagrant' // カレントワーキングディレクトリ
environment "HOME" => '/home/vagrant' // 環境変数
// ヒアドキュメントでscriptを定義
code <<-EOC
curl -kL http://install.perlbrew.pl | bash
EOC
creates "/home/vagrant/perl5/perlbrew/bin/perlbrew" // このコマンドがこのファイルを作成するであろうことを指示し、かつすでにそのファイルがある場合はこのコマンドを実行しない
end
- perlbrewの実行ファイルが存在するかどうかをインストールの有無の判定に使っているのが欠点。
not_if, only_if
- not_if: 指定した条件が真でないならコマンドを実行する。
- only_if: 指定した条件が真のときのみコマンドを実行する。
- たとえば、createsと同じ事は、
not_if {File.exists?("/home/vagrant/perl5/perlbrew/bin/perlbrew")}
と記述することもできる。
// not_ifの使用例
// rubybuildの存在有無をガード節に使っている
bash "install-rubybuild" do
not_if 'which ruby-build'
code <<-EOC
cd /tmp/ruby-build
./install.sh
EOC
end
// not_ifやonly_ifでの条件判定の例
not_if <<-EOC, :user => 'vagrant', :environment => { 'HOME' => '/home/vagrant' }
略
EOC
3.4 そのほかのリソース
- ドキュメント参照
3.5 AttributeとData Bag
- Attribute: ノードに紐つく属性のうち、固定的にあつかうものではないもの。
- Data Bag: ユーザの各種データなどノードの属性でもない、リソース属性でもないものを定義する。
Attribute
- Nodeオブジェクトの記述例
{
"httpd" : {
"port" :80
},
"run_list": [
"recipe[git]",
"recipe[apache]",
"recipe[mysql]"
]
}
- node['httpd']['port']のようにキーに文字列を使うのが慣習。
Attributeの初期値
- Attributeはデフォルト値を決めておくことができる。
- 初期値があらかじめクックブック内に定義しておく
- JSONファイルでノードごとに値を定義しておく
default["apache"]["dir"] = "/etc/apache2"
default["apache"]["listen_ports"] = [ "80", "443" ]
Data Bag
- 各ノードで共有するようなデータはDataBagを使って定義する。
- 複数ユーザがいる状態を定義したい場合など。
各ノードで共有したいデータを準備する
- DataBagで扱うデータは<リポジトリ>/data_bagsディレクトリに置く。
# Data Bagディレクトリの構造例
├─ Berksfile
├─ Vagrantfile
├─ cookbooks
├─ data_bags
├─ users
├─ naoya,json
├─ inao.json
{
"id" : "naoya",
"username" : "naoya",
"home" : "/home/naoya",
"shell" : "/bin/bash"
}
{
"id" : "inao",
"username" : "inao",
"home" : "/home/inao",
"shell" : "/bin/inao"
}
#### データを利用する
* Data Bagからの値の取得例
login_users/recipes/default.rb
data_ids = data_bag('users')
data_ids.each do |id|
u = data_bag_item('users', id)
user u['username'] do
home u['home']
shell u['shell']
end
end
#### データを暗号化する
* Data Bagにはパスワードや鍵等のデータを暗号化して格納しセキュアに扱う方法もある。
* chef solo encrypted data bags
* knife solo data bag
## 3.6 クックブックのディレクトリレイアウト
* ドキュメント参照