Edited at

個人的Chefのtips、豆知識

More than 3 years have passed since last update.


初期知識


  • コーディング規約は Ruby コーディング規準 に準拠する

  • デフォルトのactionはcreateです。それ以外の操作がしたい場合はactionを記述しましょう

  • コメントアウトでどういう動作をしてるか、resource毎に日本語で書きましょう。

  • fileやtemplate、user、group resourceなどは、resourceブロックの名前に書く事で、

    path(実際に配置するpath)や、group_name部分を省略してます。


    (例)file '/tmp/hogehoge.txt' do ・・・/tmp/hogehoge.txtを配置

  • 定数、分岐はなるべく少なくしましょう。

  • environments, roleを有効活用しましょう


①bash : スクリプトを打ちたいとき、git cloneしたいとき


  • bash, execute, scriptなど色々有りますが、どれもやってることはほぼ同じなのでbashに統一します。

  • scriptだと、スクリプトがtypoしてたり間違ってたら止まっちゃいます。くらい

### /tmp/hogeにhogehogeと書き込む

bash 'write to /tmp/hoge' do
code <<-EOS
echo "hogehoge" >> /tmp/hoge
EOS
end


②bash or package : yum操作をするとき


  • package resourceを使う方法もありますが、色々とエラーが出てしまう場合が多いので、bashでやるのがおすすめです。(一応packageの方法も乗せます)

  • yumコマンドのinstallはupdateもするが、

    package resourceのupgradeは、既にパッケージがインストールされてないとupdateはしない。


    しかもupgradeで読みにいったファイルが、インストールされてる物より古かった場合、下記のようなエラーを吐いて処理が止まる

    「Installed package prelink-0.4.6-3.1.el6_4 is newer than candidate package prelink-0.4.6-3.el6」

  • package resourceのinstall➡installのみ。upgradeはしない

  • package resourceのupgrade➡installもupgradeもする

  • packageが多い場合はattributeディレクトリで管理するのがおすすめです。

### symlinksとprelink packageのインストール + アップデート

%w(symlinks prelink).each.do |package|
bash 'install #{package}' do
code <<-EOS
yum -y install
#{package}
EOS
end
end

### symlinksとprelink packageのインストール + アップデート

### パッケージ名の部分は12.1以降の記述方法です。それ以前のバージョンであれば、パッケージはeach doで回しましょう。
package 'install package' do
package_name [
'symlinks',
'prelink'
]
options "--disablerepo=* --enablerepo=base,epel"
action :upgrade
end


③gem_package : gemパッケージをインストールしたいとき

### ruby2.2.1のrakeとbundlerをインストール

%w(rake bundler).each do |package|
gem_package "#{package}" do
gem_binary '/usr/local/bin/gem'
version '2.2.1'
action :install
end
end
# %w・・・,(コンマ)や' "(クォート)を使わずに配列を作るrubyの記法


④file, directory : 空ファイル、空ディレクトリを作成、パーミッション変更したいとき


  • file : 空ファイルを作成、パーミッション変更

  • directory : 空ディレクトリ作成、パーミッション変更。recursiveで再帰的に作成も出来る。
    が、再帰的にパーミッションは変更出来ないので注意。対象がファイルでもパーミッション変更されます。

### /tmp/hoge.txtを作成

file '/tmp/hoge.txt' do
owner 'root'
group 'root'
mode '0644'
end
# action :deleteを使うと、ファイルが削除出来るが、
# 削除対象がシンボリックリンクの場合は警告が出るので、link resourceを使うのがベスト。

### /tmp/hoge/fugaを作成

directory '/tmp/hoge/fuga' do
owner 'root'
group 'root'
mode '0644'
recursive true
end


⑤template, cookbook_file, remote_directory :
cookbook内のファイル、ディレクトリを配置したいとき


  • template、cookbook_file、remote_directoryを使い分けましょう。

    ①template : ファイル内に変数を置いたり、配置する際に書き込みできる。
    なので、配置する際に中身を読み込んでしまう。
    圧縮ファイルやバイナリ、ディレクトリ以外は基本ココに置きましょう。
    erb記述のファイルも扱えます。


    ②cookbook_file : 配置する際に中身は読み込まない。圧縮ファイルとか、rpmファイルとか、バイナリファイルなど置くと良い。


    ③remote_directory : files/default/のディレクトリとその中のファイルを配置。またファイルに対して必要なパーミッションを付与出来る

  • 上書きが困る場合はaction: create_if_missing や not_if {File.exist?} などを使いましょう

### templateディレクトリのhogehoge_producrtion.txtを/tmp/hogehoge.txtとして配置する

template '/tmp/hogehoge.txt' do
source 'hogehoge_producrtion.txt'
owner 'root'
group 'root'
mode '0644'
end

### filesからrpmを配置する

### files/default/hogehoge.el6.x86_64.rpmを配置しておく。
cookbook_file '/tmp/hogehoge.el6.x86_64.rpm' do
source 'hogehoge.el6.x86_64.rpm'
owner 'root'
group 'root'
mode '0644'
end

### filesから/etc/httpd/conf.dディレクトリを配置する。またその中のファイルのパーミッションを変更する

### files/default/plugins/....を配置しておく。
remote_directory '/etc/httpd/conf.d' do
source 'conf.d'
files_owner 'apache'
files_group 'apache'
files_mode '0755'
owner 'apache'
group 'apache'
mode '0755'
recursive true
action :create
end
# files_ownerでファイルのパーミッション変更、ownerでディレクトリの変更


⑥remote_file : 外部からファイルをダウンロードしたいとき

### http://hoge.example.com/fugafuga.rpmをダウンロードし、/tmp/fugafuga.rpmに配置

remote_file '/tmp/fugafuga.rpm' do
source 'http://hoge.example.com/fugafuga.rpm'
owner 'root'
group 'root'
mode '0644'
end


⑦data_bags : ユーザを作成

### ユーザを定義

$ data_bags/users/alice.json
// alice
{
"id": "alice",
"uid": "1234",
"gid": "alice"
"shell": "/bin/bash",
"password": "password"
}

### ⑧ユーザ作成
$ vim site-cookbooks/hoge/recipes/default.rb
data_bag('users').each do |id|
u = data_bag_item('users', id)
user u['id'] do
shell u['shell']
uid u['uid']
gid u['gid']
password u['password']
supports :manage_home => true, :non_unique => false
action [:create]
end
end


⑧user : ユーザの作成、変更をしたいとき


  • ユーザ管理は出来る限りdata_bagsで管理しましょう〜。

### hogeユーザ作成

user 'hoge' do
uid 1234
gid 'hoge'
home '/home/hoge'
supports :manage_home => true
password 'passworddayo'
end
# supports :manage_home => trueは、ホームディレクトリを作成するオプション


⑨group : グループの作成、変更をしたいとき

### apache groupを作成して、"hoge"と"fuga"ユーザを所属させる

group 'apache' do
gid 48
members ['hoge', 'fuga']
append true
action :modify
end
# append trueにすると、追加するユーザだけ記述すればいい。
# append falseにすれば、apacheに属しているユーザを全て書かなければならない。
# 新しくgroupを作りたい場合はデフォルトのcreateです。


⑩service : サービスの起動、停止、再起動をしたいとき

### httpdの起動 + chkconfig on を実施

service 'start httpd add chkconfig on' do
service_name 'httpd'
action [:enable, :start]
end
# actionは複数じゃなければ、[]はいらないが、文言統一のために毎回[]でくくった方が良い
# supports status: true, restart: true, reload: true を指定すると、httpdではこのコマンドが使えるよ!と教えて上げれる。使う場合は記述しましょう。
# restart => trueを指定しない場合、stop + start で代用される
# ignore_failure true を指定すると、エラーは無視します。

### Action 説明
# :enable ブート時の起動を有効化
# :disable ブート時の起動を無効化
# :nothing 何もしない(default)
# :start サービスを起動
# :stop サービスを停止
# :restart サービスを再起動
# :reload

### virtualhosts.confを作成した後、httpdを再起動する。service resourceは先に書いても、後に書いてもどちらでも動く

template '/etc/httpd/conf.d/virtualhosts.conf' do
source 'virtualhosts.conf'
owner 'root'
group 'root'
mode '0644'
notifies :restart, 'service[httpd]', :immediately
end

service 'httpd' do
action [ :nothing ]
end
# notifies:該当のResourceが実行された際、他のResourceに通知してそのResourceの実行を促す。複数行記述することができる。
# subscriibes:notifiesと逆で、他のResourceの実行終了後に該当ResourceのActionを指定して実行する。


⑪link : シンボリックリンク、ハードリンクを貼る

### /tmp/passwdに/etc/passwdを参照するシンボリックリンクを作成

link '/tmp/passwd' do
to '/etc/passwd'
end
# link_type :hard を指定するとハードリンクが貼られる。:symbolicだとシンボリックリンク。
# ハードリンクってあまり使わないので、基本シンボリックリンクだと思います。
# なので、link_typeは明示的に書かなくても良いでしょう。


⑫文字列置換したいとき

### /tmp/hoge.txtに書かれてるeth0をeth1に変更

file '/tmp/hoge.txt' do
f = Chef::Util::FileEdit.new(path)
f.searchfile_replace_line('eth0', 'eth10')
content f.send(:editor).lines.join
end


⑬only_ifとnot_ifの違い


  • not_if : 既にファイルが存在している場合コマンドを実行しない

  • only_if : 既にファイルが存在している場合コマンドを実行する

  • デフォルトはファイル上書き

### only_ifの例

# file が存在し、シンボリックリンクであれば、削除を実行する。
link "/tmp/mylink" do
action :delete
only_if "test -L /tmp/mylink"
end

# /var/lib/mysqlが存在した場合、/tmp/hoge.txtを作成する
file '/tmp/hoge.txt' do
owner 'root'
group 'root'
mode '0644'
only_if {Dir.exist?("/var/lib/mysql")}
end

### not_ifの例

# /tmp/hogeが存在した場合、書き込まない
bash "fugaaa" do
code <<-EOS
echo "fugaaaaa" >> /tmp/hoge
EOS
not_if { File.exist?('/tmp/hoge') }
end


⑭attributeについて


  • attributeに値を定義することができます。こんな感じに。(それぞれの書き方は公式のexampleを見ると良いでしょう。https://docs.chef.io/attributes.html)

### 定義

$ vim site-cookbooks/hogehoge/attributes/default.rb
# インストールしたいrpmパッケージを定義
default['rpm_package'] = %w(
httpd
httpd-devel
)

### environmentなどはjson形式で定義
$ vim environments/development.json
# hoge = development dayoと定義
"default_attributes": {
"hoge": "development dayo"
}

### recipeで実際に使う
$ vim site-cookbooks/hogehoge/recipes/default.rb
# rpmをインストール
node['rpm_package'].each do |package|
bash "install #{package}" do
code <<-EOS
yum -y install
#{package}
EOS
end
end

# ⑮ohaiで取得した、対象サーバのipアドレス。その名前をファイル名として、/tmp/配下にファイル作成
file "/tmp/#{node['ipaddress']}" do
owner 'root'
group 'root'
mode '0644'
end

### erb形式など
$ vim site-cookbooks/hogehoge/templates/default/test.txt
# development dayoと書く
<%= node['hoge'] %>


  • 優先順位は弱い順に下記。どんどん上書きされて行きます。

    ①recipes/attribute/

    ②recipes/default/

    ③environments/

    ④roles/

    ⑤nodes/



  • Attributeの強さは弱い順に下記。同じく上書きされて行きます。

    ①default

    ②force_default

    ③normal

    ④override

    ⑤force_override

    ⑥automatic



  • ルールを作って運用するのが一番。自分は下記でやってます。


    • なるべく変数、定数を使わないように構成する。

    • defaultとoverrideのみ使います。

      ①recipes/attribute/:インストールするパッケージ一覧や、rubyのバージョンなどのような変動が多い部分。
      また③④のjson形式で解消出来ない部分。

      ②recipes/default/:使わない

      ③environments/:development(開発)、production(本番)でのサーバ収容先、またはネットワーク差異の分岐。

      ④roles/:基本構成、AP(apache用)、AP(nginx用)、DBサーバの分岐。

      ⑤nodes/:使わない





⑮environmentsの情報を取ってくる

### もしenvironmentsがproductionだったら・・・

### もしenvironmentsがdevelopmentだったら・・・
if node.chef_environment == "production"
・・・・
elsif node.chef_environment == "development"
・・・・
end


⑯ohai便利


  • ohaiはchefが実行環境の情報を取得するためのライブラリです。

  • ohai | lessなどで、取って来れる情報がJSON形式で出力されます。

  • #{node['ipaddress']}で、IPが取ってこれたり、

  • #{node['virtualization']['system']}で、kvmかopenstackかを取ってこれたりします。

### CPUが1コアの場合、1coreと記述、1コアじゃない場合は、not 1coreと記述。

bash "hoge" do
if node['cpu']['total'] == 1
code <<-EOS
echo "1core" >> /tmp/hoge
EOS
else
code <<-EOS
echo "not 1core" >> /tmp/hoge
EOS
end
end


chefエラー対応


ERROR: Cookbook repository not found.とエラーが出たら


  • キッチン(リポジトリ)のトップディレクトリにいって、$ knife solo init .を実行する


ERROR: Net::SSH::HostKeyMismatch: fingerprint 0c:82:e9:23:22:6b:b0:73:f6:06:cb:e6:55:49:7a:0a does not match for "10.26.2.19"

の場合は、known_hostsで対象行を消しましょう

* エラー見たら分かるかと思います。


chef豆知識編


cookbook名に .(ドット)は使えません


下記は同じ動き


  • %w{test test2 test3}

  • ['test', 'test2', 'test3']


chef 12から cookbookのmetadata.rb内のname attributeの記述が必須になった


  • metadata.rbのnameを、cookbook名にすればOK


rubocopで整形しましょう

$ gem install rubocop --no-ri --no-rdocで、インストールして、$ rubocop site-cookbooks


  • 整形すると綺麗になります

  • warningやcriticalが出た部分は適宜修正しましょう


erbを使うときでもファイル名をerbにしなくても通ります。


resourceの使い回しは出来ます

### 当然と言えば当然ですが。こんな感じでもいけます。

service "httpd" do
action [:start]
end

service "httpd" do
action [:stop]
end


MySQL5.6を使ってる方は/usr/my.cnfを事前に作りましょう


  • rpmでmysql5.6をインストールすると、その時点でmysql_install_dbが走ります。

  • パスワードを設定する処理などが走りますので、/usr/my.cnfを事前に作っておきましょう


gitリソースを使って、cloneするときはenable_checkout falseをオプションに入れてあげましょう


  • gitリソースを使うと、下記のように新しくdeployがbranchとして切られます。

  • enable_checkout falseをオプションにつけるとdeployブランチが切られません。