chef
chef-solo

Chefレシピ逆引きメモ

More than 3 years have passed since last update.

よく使うものを逆引きできるように。

アンチパターンを知る

http://www.slideshare.net/JulianDunn/beginner-chef-antipatterns
http://www.creationline.com/lab/3080

  1. すべてのChefデータを1つの巨大なGitレポジトリに入れてしまう
    • cookbooksにはバージョンがあるが、environmentsやrolesにはない
    • cookbooksとして分割すべきものはリポジトリを分けるべき
  2. 会社名つきの巨大なCookbookを作ってしまう
    • 本来組み合わせるべきでないものが混ざる危険がある
    • プロジェクト別に分けるべき
  3. Environmentsを論理的な環境以上の目的で使ってしまう
    • developmentとかproductionとか論理的な環境で使い、クラスタとかデータセンターといった割り当てに使わない
  4. Community Cookbookをフォークしてしまう
    • 開発元のバグ修正や機能追加といった恩恵を受け取れない、なるべくラップする方が良い
  5. Role内でrun_listを管理してしまう
    • roleはバージョンが付かない、代わりに include_recipe を使いrecipeでまとめる
  6. 無秩序なdata bagを作ってしまう
    • data bagとdata bag itemという2階層なので作る前に計画しておく
  7. chef-shellを知らない、使わない
    • Cookbook開発、デバッグに適している
  8. LWRPを怖がってしまう
    • 言うほどLWRPの実装は難しくない
  9. 外部発祥だから利用しない症候群に陥ってしまう
    • まず使えるものがあるか探し、変更が必要ならコントリビュートする
  10. 孤独なChef使いになってしまう
    • あるセットアップに自分より詳しいメンバーがいるなら任せ、チームで責任を持つ

レシピをチェックする

http://acrmp.github.io/foodcritic/

gem i foodcritic
foodcritic -C .

FC001など警告の内容についてはサイトに載っている。

FC001以外をチェックする

FC001はdeprecatedになった。FC001を除外して検証するには以下のようにする。

foodcritic -t ~FC001 site-cookbooks

ログを出す

logリソースを使う。でもレシピの一時的なデバッグならrubyのputspでもいいかもしれない。

log "a debug string" do
  level :debug
end

log "message" do
  message "This is the message that will be added to the log."
  level :info
end

Vagrantのプロビジョニングでログレベルを変更する

log_level属性を使う。
http://docs.vagrantup.com/v2/provisioning/chef_common.html

config.vm.provision :chef_solo do |chef|
  chef.log_level = :debug

nodeの属性を出力する

とりあえずjsonで

puts JSON.pretty_generate(node)

またはレシピ内で使いそうなものだけ確認。

[:platform, :platform_version, :ipaddress, :fqdn, :hostname, :domain].each do |key|
  puts "node['#{key}'] = #{node[key]}"
end

置き換わったファイルのバックアップを見る

templateリソースなどでファイルを置き換えた場合に、元のファイルはリモートサーバの /var/chef/backup/ に保存されている。

パスワードのハッシュを生成する

Unixではパスワードを $<ハッシュ方式>$<salt>$<ハッシュ後のパスワード> で表現している。以下のコマンドでそれに対応したMD5のhashを出力できる。

openssl passwd -1 'password'

Environmentで分岐する

node.chef_environment を見る

if node.chef_environment == 'development'
end

OSで分岐する

OS

case node["platform"]
when "debian", "ubuntu"
  # do debian/ubuntu things
when "redhat", "centos", "fedora"
  # do redhat/centos/fedora things
end

OSカテゴリ

xx系で分岐する場合。yumかaptでpackage名が変わったりパスが変わったりするときなどで使う。

case node["platform_family"]
when "debian"
  # do things on debian-ish platforms (debian, ubuntu, linuxmint)
when "rhel"
  # do things on RHEL platforms (redhat, centos, scientific, etc)
end

テンポラリファイルのパスを組み立てる

foodcritic の FC013/tmp ではなく Chef::Config[:file_cache_path] を使えとある。

remote_file "#{Chef::Config[:file_cache_path]}/large-file.tar.gz" do
  source "http://www.example.org/large-file.tar.gz"
end

事前に用意した静的ファイルを配置する

cookbook_fileリソースを使う。

デフォルトではファイルは上書きになる。上書きが困る場合は action: create_if_missingnot_if {File.exist?} などを使う。

# files/default/testfile
cookbook_file "/#{Chef::Config[:file_cache_path]}/testfile" do
  source "testfile"
  mode 00644
  owner 'root'
  group 'root'
end

配置する元ファイルは以下の順番で files のサブディレクトリから検索される。

  1. ホスト名 host-#{node[:fqdn]}
  2. OS-フルバージョン #{node[:platform]}-#{node[:platform_version]}
  3. OS-メジャーバージョン #{node[:platform]}-#{version_components}
  4. OS node[:platform]
  5. default
files/
   host-foo.example.com
   ubuntu-10.04
   ubuntu-10
   ubuntu
   default

テンプレートを使ってファイルを配置する

templateリソースを使う。

cookbook_fileリソースとの違いは用意できるファイルがerbテンプレートであり動的に出力を変えられるところ。テンプレートのデフォルトのコンテキストはnodeオブジェクトで、variables属性で任意のhashをテンプレートに渡すことも可能。

template "/etc/apache2/ports.conf" do
  source 'apache/ports.conf.erb'
  mode '0644'
end

ソースのテンプレートファイルは files ディレクトリではなく、templates ディレクトリに配置する。

templates/
   host-foo.example.com
   ubuntu-10.04
   ubuntu-10
   ubuntu
   default

書き換えてしまったファイルの前のバージョンを見る

cookbook_fileリソースやtemplateリソースで既存のファイルを上書きしてしまった場合に世代バックアップが/var/chef/backup にある。

空のファイルを作成する

fileリソースの: create_if_missingアクションを使う。(ファイルが無い時限定)

file "/tmp/something" do
  action :create_if_missing
end

ファイルを削除する

fileリソースを使う。

file "/tmp/something" do
  action :delete
end

削除対象がシンボリックリンクの場合は警告がでるので、linkリソースを使うのがエレガント。

link "/tmp/mylink" do
  action :delete
  only_if "test -L /tmp/mylink"
end

ディレクトリを作成する

directoryリソースを使う。

directory "/var/lib/foo" do
  owner "root"
  group "root"
  mode 00755
  action :create
end

親ディレクトリが存在しないと失敗する。recursive trueをつけると親ディレクトリを再帰的に作成する。

シンボリックリンクを作成する

linkリソースを使う。

link "/tmp/passwd" do
  to "/etc/passwd"
end

link "/tmp/passwd" do
  to "/etc/passwd"
  link_type :hard
end

link "/tmp/mylink" do
  action :delete
  only_if "test -L /tmp/mylink"
end

ファイルがなければリソースを実行する

not_ifでファイルをチェックする。

execute "create file hoge" do
  command "touch /tmp/hoge"
  not_if {File.exist?("/tmp/hoge")}
end

Dir.exist? も使える(rubyの世界)

コマンドを実行する

executeリソースを使う。

execute 'bundle install' do
  cwd '/myapp'
  not_if 'bundle check' # This is not run inside /myapp
end

シェルファイルを実行する

正確には以下の例はbashリソースからコマンドを実行。

bash "install_ruby_build" do
  cwd "#{Chef::Config[:file_cache_path]}/ruby-build"
  user "rbenv"
  group "rbenv"
  code <<-EOH
    ./install.sh
  EOH
  environment 'PREFIX' => "/usr/local"
end

Gitリポジトリからファイルを取得する

gitリソースを使う。revision属性にタグなどを指定できる。

git "/home/#{node[:user]}/redmine"  do
  repository "git://github.com/edavis10/redmine.git"
  revision '2.5.1'
  user node[:user]
  group node[:user]
end

ウェブにあるアーカイブファイルを展開する

remote_fileリソースでダウンロード。bashリソースで展開。

#  the following code sample is similar to the ``upload_progress_module`` recipe in the ``nginx`` cookbook: https://github.com/opscode-cookbooks/nginx

src_filename = "foo123-nginx-module-v#{node['nginx']['foo123']['version']}.tar.gz"
src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}"
extract_path = "#{Chef::Config['file_cache_path']}/nginx_foo123_module/#{node['nginx']['foo123']['checksum']}"

remote_file src_filepath do
  source node['nginx']['foo123']['url']
  checksum node['nginx']['foo123']['checksum']
  owner 'root'
  group 'root'
  mode 00644
end

bash 'extract_module' do
  cwd ::File.dirname(src_filepath)
  code <<-EOH
    mkdir -p #{extract_path}
    tar xzf #{src_filename} -C #{extract_path}
    mv #{extract_path}/*/* #{extract_path}/
    EOH
  not_if { ::File.exist?(extract_path) }
end

hostsファイルにエントリを追加する

hostsfile cookbook を追加し、hostsfile_entry リソースを使う。

hostsfile_entry '1.2.3.4' do
  hostname  'www2.example.com'
  aliases   ['foo.com', 'foobar.com']
  comment   'Append by Recipe X'
  action    :append
end

ファイルを置き換えたのでサービスを再起動する

Notificationsを使う。

template '/etc/httpd/conf/httpd.conf' do
  source 'httpd/httpd.conf.erb'
  notifies :restart, 'service[httpd]'
end
service 'httpd' do
  action [ :enable ]
end

notifiesで指定するリソース名が存在しないといけないので serviceリソースで :enable:nothingなどのActionで記述しておく。

Ohaiで取得して使える属性

属性 説明
node['platform'] nodeのプラットフォーム
node['platform_version'] プラットフォームのバージョン
node['ipaddress'] IPアドレス。nodeがデフォルトルートを持っている場合はIPv4アドレス、もっていない場合はnil
node['macaddress'] MACアドレス
node['fqdn'] 完全修飾ドメイン名
node['hostname'] ホスト名
node['domain'] ドメイン
node['recipes'] 関連付けられたレシピのリスト
node['roles'] 関連付けられたroleのリスト
node['ohai_time'] Ohaiが最後に実行された時刻。knife statusサブコマンドでサーバに保存し参照できる