Chefマスターから見たら「えっそんなこともちゃんとやってなかったの?情弱」的な感じかもしれませんが、とりあえず自分で気をつけたポイントをメモしてみました。
ほんとはこう分けた方が良いよ!とかありましたら教えてもらえると嬉しいです!
課題
やっとサービスが成長してきたので、ちゃんとChefで3環境(local, staging, production)を管理しているのですが、
「3rd partyのcookbookも使ってるし、attributeのoverrideをrunlistで行っていて、環境ごとのrunlistに差分が出る。ヤバい。目diff無理」
という素敵な課題が勃発
対策
このrunlist問題に対して、以下のような対策を打って、リファクタしました。
1. バージョンをattribute化してあるものは必ずserver specでテスト
えぇ、、、なんでやってなかったんだって怒られそう。
2. site-cookbooks内のcookbookのまとまり感を揃える
リファクタ前は、あるpakcageのインストールをcookbook内のrecipeとして管理したり、独立したcookbookとして管理したり、粒度や方針がバラバラでした。
例えば以下のようなものがバラッバラでした。
そうなると当然、チーム内で新しいpackageを追加したいときに「これって切り出すべき?それともどっかのrecipeにするべき?」って話し合う事が多くなる感じです。
そこで、以下のような大きく4つのcookbookを作って整理した結果、runlistへの見通しも良くなりました。
(分け方は趣味です)
- initialize
- 最初にやっておくべき事(sudoとかuserの追加とか、iptablesとか)
- languages
- node, ruby, pythonとか
- db
- db接続とかdbそのものなど
- sites
- nginx周りの設定とかweb server絡み
ただ、recipeの内容が複雑になる場合やtemplateを多用する場合は、まとめてしまうとメンテし難くなるのでcookbookとして切り出し、include_recipeするwrap recipeを上記のcookbookに食わせるようにしました。
3. 3rd partyのcookbookを直接runlistから実行せず、包む。
runlistを長くなる原因の一つとして、3rd partyのcookbookをそのままrunlistから実行する事が挙げられると思います。
対策としては、ほぼ2のcookbook切り出した場合と同じで、3rd partyのcookbookをincludeするwarp recipeを作り、attributeもそのcookbook内でまず上書きする方針にしました。
例えばgitの3rdpartyを以下のようにrunlistで記載するのではなく、warpすると以下になるイメージです
リファクタ前
// runlist
{
"environment": "production",
"git": {
"version: "1.9.0",
"url": "https://git-core.googlecode.com/files/git-1.9.0.tar.gz"
},
"run_list": [
"git::source"
]
}
リファクタ後
wrapするrecipeを作って
# initialize::git
include_recipe "git::source"
attributeで上書きして
# attribute
# git
default['git']['version'] = '1.9.0'
default['git']['url'] = 'https://git-core.googlecode.com/files/git-1.9.0.tar.gz'
runlistはこんな感じに
// runlist
{
"environment": "production",
"run_list": [
"initialize::git"
]
}
このとき、attributeに記載するデフォルト値はstaging環境にしてみました。
理由としては以下2つです。
- productionにすると、環境を増やしたときに繋がれてしまう恐れが有る
- local(development)にすると、papertrailなど環境を増やしたときにattribute設定の漏れが出る恐れが有る
4. environmentを利用してrunlistからattributeを分離
環境ごとに異なるtokenやdbのつなぎ先などはenvironmentで管理します。
中を見ると以下のようになっているのですが、
{
"name": "production",
"description":"This is it",
"chef_type": "environment",
"json_class": "Chef::Environment",
"default_attributes": {
},
"override_attributes": {
}
}
override attributeは、優先度の高い値として、attributeを強制的に上書きされるので、overrideを使います。
reference: http://docs.opscode.com/essentials_environments.html
で、そこで明示的に示しておくべきattributeは、defaultにあったとしてもそれぞれattributeで明示的に書いておくことにしました。
例えば、database.ymlの設定や.envファイルの設定です。これは、環境を別途切り出した場合の設定ミスを防ぐ目的で行いました。
5. runlist内の重複は、roleで回避する
ある程度整理できてくるとnodeの重複が出てきます。
リファクタ前
local
{
"environment": "local",
"run_list": [
"initialize::default",
"initialize::manage",
"initialize::users",
"initialize::sudo",
"initialize::ssh",
"initialize::hosts",
"initialize::iptables",
"initialize::git",
...
// only staging, local
"db::mysql_server",
"sites::memcached"
...
]
}
production
{
"environment": "production",
"run_list": [
"initialize::default",
"initialize::manage",
"initialize::users",
"initialize::sudo",
"initialize::ssh",
"initialize::hosts",
"initialize::iptables",
"initialize::git",
...
]
}
リファクタ後
common, db, web, dev(dbやらcacheやら、productionでは外に出すけど、stagingとかではお腹に置くもの)とか切り出します
// roles/common.json
{
"name": "common",
"chef_type": "role",
"json_class":"Chef::Role",
"default_attributes":{},
"override_attributes":{},
"description":"webserver’s role",
"run_list": [
"initialize::default",
"initialize::manage",
"initialize::users",
"initialize::sudo",
"initialize::ssh",
"initialize::hosts",
"initialize::iptables",
"initialize::git"
...
]
}
runlist
{
"environment": "local",
"run_list": [
"role[common]",
...
"role[dev]"
]
}
ただ、以下のサイトにもあるようにrunlistはバージョン管理できない事も有り、問題が有るかもしれません。