関連記事
- Chefを使ってより効率的にサーバを管理しよう。まずは用語から
- Berkshelfを使って外部クックブックをノードに適用する。
- クックブックを作成してノードに適用する。- その1
- クックブックを作成してノードに適用する。- その2
- ChefでCentOS 6.7 + rbenv + ruby 2.3.0の環境を構築する。
- ChefでCentOS 6.7 + Vim 7.4 + luaを設置する。
- ChefでVimプラグインマネージャーNeoBundleを設置する。
- Chefのレシピをデバッグしてみよう。
- ChefでCentOS 6.7 + nginxを設置する。- 外部クックブック
- ChefでCentOS 6.7 + nginxを設置する。- カスタムクックブック
- ChefでCentOS 6.7 + nodejs + npmを設置する。
- ChefでVimプラグインマネージャーvim-plugを設置する。
- ohaiのカスタムプラグインを作ってみよう。
- Chef SoloからChef Zeroへ
- Chefでnginx + php(php-fpm) + mariadb + wordpressを設置する。
ohaiとは
- ノードの情報をAttributeで保持しているシステムでChefをインストールするとき一緒にインストールされる。
- ノート上のさまざまな情報をJSONのデータとして扱えるようにしてくれる。
カスタムプラグインの必要性
- nginxをtarballでインストールする。
- nginxのバージョンと使っているmoduleが同じであれば次にChefを実行するときはインストールをスキプしたい。
- ohaiを使って最初インストールするときのバージョンとmoduleの情報をノード上に保持する。
- Chefを実行するときノード上に保持されているバージョンとmoduleの情報をクックブックのattributeと比較する。
- 比較して異なる場合のみインストールする。
カスタムプラグインの作成
Berksfileに追加
- ohaiというコミュニティクックブックに依存的なので、先にインストールする。
% vi Berksfile
... snip ...
cookbook 'ohai'
:wq
% bin/berks vendor cookbook
- nginxのコミュニティクックブックがインストールされている場合はすでにohaiがインストールされている。
依存性を追加
- nginxのカスタムクックブック内のmetadata.rbファイルに依存性を記入する。
... snip ...
depends 'ohai'
レシピを生成
- 既存nginxのレシピ(default.rb)からohaiを使うためにohai_pluginという新規レシピを作成する。
- nginxのコミュニティクックブックからそのまま持ってきた。
- ノード上の/etc/chef/ohai_pluginsの配下にnginx.rbファイルが生成される。
- site-cookbooks/nginx/recipes/ohai_plugin.rb
ohai 'reload_nginx' do
plugin 'nginx'
action :nothing
end
template "#{node['ohai']['plugin_path']}/nginx.rb" do
source 'plugins/nginx.rb.erb'
owner 'root'
group 'root'
mode '0755'
notifies :reload, 'ohai[reload_nginx]', :immediately
end
include_recipe 'ohai::default'
既存レシピの修正
- 新規作成したohai_pluginをdefaultレシピの中からincludeする。
- nginxがインストールされるたびに今度作成したohaiのカスタムプラグインをリロード(再実行)する。
include_recipe 'nginx::ohai_plugin'
nginx_url = node['nginx']['url']
nginx_filename = "nginx-#{node['nginx']['ver']}.tar.gz"
src_filepath = "#{Chef::Config['file_cache_path'] || '/tmp'}/#{nginx_filename}"
... snip ...
bash "install nginx" do
cwd File.dirname(src_filepath)
code <<-EOH
tar zxf #{nginx_filename} -C #{File.dirname(src_filepath)}
cd #{File.dirname(src_filepath)}/#{File.basename(nginx_filename, ".tar.gz")}
./configure #{configure_flags.join(' ')}
make
make install
EOH
not_if do
force_recompile == false &&
node.automatic_attrs['nginx'] &&
node.automatic_attrs['nginx']['version'] == node['nginx']['ver'] &&
node.automatic_attrs['nginx']['configure_arguments'].sort == configure_flags.sort
end
notifies :restart, 'service[nginx]'
notifies :reload, 'ohai[reload_nginx]', :immediately
end
... snip ...
カスタムプラグインをテンプレートで生成
- ohaiのバージョン(6, 7, 8)別にプラグインの記述方法が異なる。
- nginxのコミュニティクックブックからそのまま持ってきた。
- しかし、バージョン6で記述されていて古い。
- site-cookbooks/nginx/templates/default/plugins/nginx.rb.erb
provides "nginx"
provides "nginx/version"
provides "nginx/configure_arguments"
provides "nginx/prefix"
provides "nginx/conf_path"
def parse_flags(flags)
prefix = nil
conf_path = nil
flags.each do |flag|
case flag
when /^--prefix=(.+)$/
prefix = $1
when /^--conf-path=(.+)$/
conf_path = $1
end
end
[ prefix, conf_path ]
end
nginx Mash.new unless nginx
nginx[:version] = nil unless nginx[:version]
nginx[:configure_arguments] = Array.new unless nginx[:configure_arguments]
nginx[:prefix] = nil unless nginx[:prefix]
nginx[:conf_path] = nil unless nginx[:conf_path]
status, stdout, stderr = run_command(:no_status_check => true, :command => "<%= node['nginx']['sbin'] %> -V")
if status == 0
stderr.split("\n").each do |line|
case line
when /^configure arguments:(.+)/
# This could be better: I'm splitting on configure arguments which removes them and also
# adds a blank string at index 0 of the array. This is why we drop index 0 and map to
# add the '--' prefix back to the configure argument.
nginx[:configure_arguments] = $1.split(/\s--/).drop(1).map { |ca| "--#{ca}" }
prefix, conf_path = parse_flags(nginx[:configure_arguments])
nginx[:prefix] = prefix
nginx[:conf_path] = conf_path
when /^nginx version: nginx\/(\d+\.\d+\.\d+)/
nginx[:version] = $1
end
end
end
- バージョン6の記述方法はすでにdeprecatedになっているので、バージョン7で直してみた。
- バージョン7と8の記述方法は同様である。
- site-cookbooks/nginx/templates/default/plugins/nginx.rb.erb
Ohai.plugin(:Nginx) do
provides "nginx"
def parse_flags
so = shell_out("<%= node['nginx']['sbin'] %> -V 2>&1")
so.stdout.lines.each do |line|
case line
when /^nginx version: nginx\/(\d+\.\d+\.\d+)/
@version = $1
when /^configure arguments:(.+)/
@configure_arguments = $1.split(/\s--/).drop(1).map { |ca| "--#{ca}" }
end
end
end
collect_data do
parse_flags
nginx Mash.new
nginx[:version] = @version
nginx[:configure_arguments] = @configure_arguments
end
end
-
provides
- このプラグインが提供する項目を宣言する。
- 保持したい情報を宣言した項目に割り当てる。
-
Mash
- データストアとしてMashを利用する。Hashだと考えばいい。
- Mashに保持したいデータをセットする。
-
collect_data
- ohaiにより呼び出されるrubyのブロックでデータを取集して保持する。
- platformを引数として指定するのもできる。ex):aix, :freebsd, :linux...
shell_out
- chef-shell(又はirb)でカスタムプラグインの中のコマンドを実行してみる。
- 使いたいコマンドの実行結果を加工して集取するのができるので、カスタムプラグインの作成のとき役に立つ。
chef (12.7.2)> include Chef::Mixin::ShellOut
=> Object
chef (12.7.2)> so = shell_out('/opt/nginx-1.8.1/sbin/nginx -V 2>&1')
=> <Mixlib::ShellOut#41687580: command: '/opt/nginx-1.8.1/sbin/nginx -V 2>&1' process_status: #<Process::Status: pid 9533 exit 0> stdout: 'nginx version: nginx/1.8.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/opt/nginx-1.8.1 --conf-path=/etc/nginx/nginx.conf --sbin-path=/opt/nginx-1.8.1/sbin/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module' stderr: '' child_pid: 9533 environment: {"LC_ALL"=>"en_US.UTF-8", "LANGUAGE"=>"en_US.UTF-8", "LANG"=>"en_US.UTF-8"} timeout: 600 user: group: working_dir: >
chef (12.7.2)> so.stdout
=> "nginx version: nginx/1.8.1\nbuilt by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC) \nbuilt with OpenSSL 1.0.1e-fips 11 Feb 2013\nTLS SNI support enabled\nconfigure arguments: --prefix=/opt/nginx-1.8.1 --conf-path=/etc/nginx/nginx.conf --sbin-path=/opt/nginx-1.8.1/sbin/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module\n"
chef (12.7.2)> so.stdout.lines
=> ["nginx version: nginx/1.8.1\n", "built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC) \n", "built with OpenSSL 1.0.1e-fips 11 Feb 2013\n", "TLS SNI support enabled\n", "configure arguments: --prefix=/opt/nginx-1.8.1 --conf-path=/etc/nginx/nginx.conf --sbin-path=/opt/nginx-1.8.1/sbin/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module\n"]
ohaiプラグインの実行
- コマンドの実行結果にプラグインから生成したデータが含まれている。
- このデータとクックブックのattributeのデータを比べてインストールするかどうかを決める。
- バージョン6の記述方法でプラグインが作成されている場合は、deprecatedになっているという警告メッセージが表示される。
- そして、バージョン6の場合は最初に表示されるため|headを付けて確認する必要がある。
- バージョン7の場合は最後に表示されるため付けなくても結構だ。
$ ohai -d /etc/chef/ohai_plugins/
... snip ...
"nginx": {
"version": "1.8.1",
"configure_arguments": [
"--prefix=/opt/nginx-1.8.1",
"--conf-path=/etc/nginx/nginx.conf",
"--sbin-path=/opt/nginx-1.8.1/sbin/nginx",
"--with-http_ssl_module",
"--with-http_stub_status_module",
"--with-http_gzip_static_module",
"--with-http_flv_module",
"--with-http_mp4_module"
]
}
chef-shellで確認する方法
$ chef-shell -s -c solo.rb -j dna.json
chef (12.7.2)> Ohai::Config[:plugin_path] << '/etc/chef/ohai_plugins'
=> ["/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-8.10.0/lib/ohai/plugins", "/etc/chef/ohai_plugins"]
chef (12.7.2)> o = Ohai::System.new
[2016-03-16T13:25:44+00:00] WARN: Ohai::Config[:plugin_path] is set. Ohai::Config[:plugin_path] is deprecated and will be removed in future releases of ohai. Use ohai.plugin_path in your configuration file to configure :plugin_path for ohai.
=> #<Ohai::System:0x00000004ea1b00 @plugin_path="", @config={}, @data={}, @provides_map=#<Ohai::ProvidesMap:0x00000004ea1a38 @map={}>, @v6_dependency_solver={}, @loader=#<Ohai::Loader:0x00000004ea0e30 @controller=#<Ohai::System:0x00000004ea1b00 ...>, @v6_plugin_classes=[], @v7_plugin_classes=[]>, @runner=#<Ohai::Runner:0x00000004ea0db8 @provides_map=#<Ohai::ProvidesMap:0x00000004ea1a38 @map={}>, @safe_run=true>>
chef (12.7.2)> o.all_plugins
... snip ...
chef (12.7.2)> o.attributes_print('nginx')
=> "{\n \"version\": \"1.8.1\",\n \"configure_arguments\": [\n \"--prefix=/opt/nginx-1.8.1\",\n \"--conf-path=/etc/nginx/nginx.conf\",\n \"--sbin-path=/opt/nginx-1.8.1/sbin/nginx\",\n \"--with-http_ssl_module\",\n \"--with-http_stub_status_module\",\n \"--with-http_gzip_static_module\",\n \"--with-http_flv_module\",\n \"--with-http_mp4_module\"\n ]\n}\n"
chef (12.7.2)> o.attributes_print('nginx/version')
=> "[\n \"1.8.1\"\n]\n"
chef (12.7.2)> o.attributes_print('nginx/configure_arguments')
=> "[\n \"--prefix=/opt/nginx-1.8.1\",\n \"--conf-path=/etc/nginx/nginx.conf\",\n \"--sbin-path=/opt/nginx-1.8.1/sbin/nginx\",\n \"--with-http_ssl_module\",\n \"--with-http_stub_status_module\",\n \"--with-http_gzip_static_module\",\n \"--with-http_flv_module\",\n \"--with-http_mp4_module\"\n]\n"
嵌ったところ
- ohaiのカスタムプラグインの記述方法をバージョン6からバージョン7に変更したあと、考えたとおりに行かなくて苦労した。
- 特にこの部分については入門書のレベルでは説明がほぼなかったためぐっぐたネット情報に依存するしかなかった。
- 日本語の情報もほとんどない。
- 英語でとても参考になったサイトのURLを最後に貼っておいたので参考にしていただきたい。
- shell_outについてはqiitaに投稿されている記事が役に立った。
コマンドの誤り
- nginxをtarballで設置するときのコンパイル情報を調べるためには-Vオプションを付ければいい。
- /opt/nginx-1.8.1/sbin/nginx -Vをして置いたがコンパイル情報の取得に失敗した。
- 'nginx/version'だけversion7というわからないデータが保持されて'nginx/configure_arguments'は空っぽだった。
- ここでshell_outで使うコマンドに問題があることは気が付かずにバージョン7の記述方法に問題があったんではないかと疑っていた。
- /opt/nginx-1.8.1/sbin/nginx -V 2>&1に変更したらコンパイル情報が正常に取得された。
- 正常に結果を得られるまで1日位時間が掛かった。
node.run_state
- Chef-solo(又はChef-client)が起動される間データを一時的に保持する。
- nginxのコミュニティクックブックの中で使っていたので、使ってみたが保持したはずなのにずっとnilだった。
- 問題は実行のタイミングにあった。
- 明示的に遅延されていないrubyコード(ruby_block、lazy、not_if/only_if)はコンパイル段階で実行される。
- Chefの実行はコンパイル>収束(Converge)>通知(Notification)順で行う。
- コンパイル段階で実行されるところに収束の段階で実行されるnode.run_stateを参照するとnilになるわけだ。
- 詳細はここを参考にしていただきたい。(特に英語のサイト)
# bashリソースのnot_ifブロックの中でnilになってしまい進めない。
node.run_state['force_recompile'] = false
node.run_state['configure_flags'] = node['nginx']['default_configure_flags'] | node['nginx']['modules']
# bashリソースのnot_ifブロックの中でもnilにならない。
force_recompile = false
configure_flags = node['nginx']['default_configure_flags'] | node['nginx']['modules']
参考
バージョン7の書き方
- http://blog.levvel.io/blog-post/levvel-up-your-devops-game-with-custom-ohai-plugins-chef/
- https://docs.chef.io/release/ohai-8/
- https://github.com/rackerlabs/ohai-plugins/tree/master/plugins
shell_outについての説明
Chefの実行タイミングの説明
書籍
- Chef実践入門
- オライリーの「Customizing Chef」(まだ翻訳されてない)