こんにちは。
株式会社ロックオンの三原です。
前回ご紹介したサーバ構築自動化ツール「Chef」について、実際に利用して経験した内容をTips形式でご紹介します。
Role(Environment) vs Recipe
いざ、Chefを使って社内の手順書をレシピ化しようとすると、かなりの可能性で遭遇するのがこの問題です。
Roleとは、必要なRecipeをrun_listでまとめることで、各Nodeの役割を定義することができるChefのモデルです。なにも考えなければ、「ADEBiSのWEBサーバ」や「THREeのDBサーバ」といった形で、Roleを作っていきたいのですが…、ちょっと待ってください。Webで検索してみると「Beginner Chef Antipatterns」において、RoleではなくRecipeを使うべきだという主張されているではありませんか。
この主張の元になっている課題は、レシピ間の関係を、どうバージョン管理するかというものです。
Roleは、バージョン管理の仕組みを持っていないため、内部のRecipeが更新されていくと、影響範囲のコントロールが難しくなるという問題があります。
そこで、Roleに相当する役割のRecipeを作成し、Recipeの持っているバージョン管理の仕組みを使って、Recipeの影響範囲をコンロールするというのが、「Beginner Chef Antipatterns」で述べられている主張になります。
この主張は、Roleに近い構成要素を持つEnvironmentにも当てはまります。
今回は、この主張を採用し、役割に関するRecipeレシピ(Roleに相当)、環境に関するRecipe(Environmentに相当)という形でレシピを作成しました。
前回の記事にも書いた、”THREeのバッチサーバ” × “開発環境”というのが、まさにこれに当たります。
とはいえ、この使い方には議論の余地があると私自身思っており、特定要素の表現に特化したモデル(RoleやEnvironment)があるにも関わらず、汎用的なモデル(Recipe)で全て表現するというのは、特定のモデルに特化して便利に使えるツールが今後出てきた際に、上手く連携できない可能性があります。
例えば、Environment毎に環境を構築して、テスト等を並列に走らせてくれるツールなどは、上手く連携できない可能性があります。
この辺りは、現実の事象をChefのモデルにどう反映させるかの議論になるので、実際のChefの利用事例を調査しながら、良い形を目指していきたいと思います。
Chef以外で触る可能性がある設定ファイル
# This file is managed by Chef.
# Do NOT modify this file directly.
私はChefのtemplateファイルの冒頭には、必ず上記のコメントを入れるようにしています。
その理由は、Chefは動かすごとにtemplateファイルで上書きしてしまうため、「手動で設定ファイルを触るな」と明示的する必要があるからです。
しかし実際問題、Chef以外で設定を触れないサーバというと、中々導入しずらいのも事実です。初回構築におけるChefを使った構築コスト低下は歓迎されるので、落とし所として「初回構築はChefでして、運用は手動かChefで」という形で、徐々にChefに移行してもらう作戦となります。
そこで、Chefのtemplateを使わずに設定する方法を考えます。
ruby_block "edit postgresql.conf" do
notifies :restart, 'service[postgresql-9.2]'
block do
_file = Chef::Util::FileEdit.new("/var/lib/pgsql/9.2/data/postgresql.conf")
_file.insert_line_if_no_match('### BY CHEF RECIPE###', >> EOS
### BY CHEF RECIPE###
deadlock_timeout = 5min
effective_cache_size = 1GB
timezone = 'Japan'
lc_messages = 'ja_JP.UTF-8'
lc_monetary = 'ja_JP.UTF-8'
lc_numeric = 'ja_JP.UTF-8'
lc_time = 'ja_JP.UTF-8'
listen_addresses = '*'
log_destination = 'syslog'
log_min_duration_statement = 5s
max_connections = 512
shared_buffers = 1GB
standard_conforming_strings = off
work_mem = 32MB
### BY CHEF RECIPE ###
EOS
)
_file.write_file
end
end
上記は、Postgresqlを設定するレシピになります。
Postgresの設定ファイルは、デフォルト設定が基本コメントアウトされているため、ファイルの末尾に設定したい値を追記する対応をとっています。
また、chef初回の設定済みというマーカーを入れているため、2回目以降からは上記の設定は動きません。
クックブックのコンパイル時にResourceを実行させる方法
Chefの実行サイクルには、以下のフェイズが存在します。
- Chef Serverとの通信、認証処理
- Chef SErverからのクックブック、データ取得
- クックブックのコンパイル
- ノードの設定、収束(converge)
- 通知(Notification)の実行
通常、第4フェーズか第5フェーズでResourceは実行されるのですが、第3フェーズで無理やりResourceを実行させる方法をご紹介します。
そもそも、なぜそんなHow-toが必要だったのか?
それは、あるサードパーティレシピを使おうとした時に、そのサードパーティレシピはDebian系でのみテストされていたと思うのですが、Redhat系では存在しないパッケージをインストールして失敗するという問題にぶつかりました。
この問題を回避するためには、サードパーティレシピの第4フェーズより先に、代替のパッケージをインストールするResourceを実行する必要があったため、以下の様にResourceオブジェクトに対して、run_actionメソッドを実行することで、第3フェーズでResourceを実行しました。
# databasegem()
gem_package "pg" do
action :nothing
gem_binary "/opt/chef/embedded/bin/gem"
options "-- --with-pg-config=/usr/pgsql-9.3/bin/pg_config"
end.run_action(:install)
求む、Chef上級者の方(一緒に悩んでいただける方でも大歓迎)
ここまで、実際にChefを導入する時に必要になった少し泥臭いTopicをご紹介させて頂きました。対処方法やコードが美しくないというご指摘もあるかと思います。
私自身、Chefに関してまだまだ勉強不足であり、もっと良いベストプラクティスがあるはずです。
しかし、初心者が現場にChefを導入する際には、泥臭いやり方を取らざる負えないこともあるかと思いますので、この記事が、その際の参考になれば幸いです。また、よりスマートなやり方をお持ちの方は、ぜひ私(FB)までご教授をよろしくお願いいたします。