hostsのレシピの進化

自分用の備忘録としてhostsをばらまくchefのレシピの進化の過程をまとめておこうかと。

### ・filesでばらまく。
値を差し替えられないので没。

```
$ cat site-cookbooks/base_setting/recipes/hosts.rb

cookbook_files "/etc/hosts" do
  action :create
  source "hosts.erb"
end
```

実はbashリソース使ってたこともあったんですが、冪等にならないデメリットがアレでそれもやめました。

### ・templateをつかって自分自身を識別する

```
$ cat site-cookbooks/base_setting/recipes/hosts.rb

template "/etc/hosts" do
  action :create
  source "hosts.erb"
end
```
```
$ cat site-cookbooks/base_setting/templates/default/hosts.alone.erb 
127.0.0.1  localhost <%= node['hostname'] %> localhost.localdomain localhost4 localhost4.localdomain4

## mng
192.168.0.5 bastion

<%= node['ipaddress'] %>        <%= node['hostname'] %> localhost
```

※自分以外の管理サーバをべた書きしていたころ。管理サーバ群に当てるときどうするかという問題が。

### ・BGデプロイしないはずの固定の管理サーバ群をdata_bagsから読むようにしてみる

　data_bagsをつかうと何が嬉しいかというと、
　・複数個所にかかなくてよくなることと
　・templateやfileリソースで値を差し替えられること。

```
$ cat site-cookbooks/base_setting/templates/default/hosts.erb 
127.0.0.1  localhost <%= node['hostname'] %> localhost.localdomain localhost4 localhost4.localdomain4

## mng
<%= @mng_data %>

<%= node['ipaddress'] %>        <%= node['hostname'] %> localhost
```

```
$ cat site-cookbooks/base_setting/recipes/hosts.rb
hosts_mng = data_bag_item("base_setting","hosts-mng")
hosts_mng_data = hosts_mng["data"]
template "/etc/hosts" do
  action :create
  source "hosts.erb"
  variables ({
        :mng_data => hosts_mng_data
  })
end
```

```
$ cat data_bags/base_setting/hosts-mng.json 
{
  "id": "hosts-mng",
  "data": "192.168.0.5      bastion-mng-192-168-0-5    bastion-mng\n192.168.1.166    gitlab-mng-192-168-1-166   gitlab  gitlab.mng.hoge.net\n192.168.0.201    ldap-mng-192-168-0-201     ldap1\n192.168.11.34    ldap-mng-192-168-11-34     ldap2\n192.168.0.214    log-mng-192-168-0-214      log1\n192.168.8.37     log-mng-192-168-8-37       log2\n192.168.5.71     yumrepo-mng-192-168-5-71   yumrepo\n192.168.0.84     jenkins-mng-192-168-0-84   jenkins\n192.168.0.73     workstation-mng-192-168-0-73       workstation\n192.168.0.203    monitor-mng-192-168-0-203  monitor1\n192.168.15.83    monitor-mng-192-168-15-83  monitor2\n"
}
```

改行は`\n`で1行で入れるとtemplateやfileリソースで展開したときに改行にしてくれるという親切なかんじ。

### ・HAしてるところを別のdata_bagsから呼んでみたのと管理系のlocalhostをなんとかしてみたりawsアカウント増加対応など

一気にやりすぎてわかりづらいかもしれないかな。。

```
site-cookbooks/base_setting/recipes/hosts.rb 
# hosts

## set account name.
environment = node.chef_environment
envaccount = data_bag_item('master_data','env_account')
account = envaccount["#{environment}"]

## set local info.
localname = node['hostname']
localip = node['ipaddress']

## これはちょっとroleからmngに属するかどうかを判別してる（ci環境とかのために）
mng_r = data_bag_item("base_setting","mng_roles")
mng_roles = mng_r["mng_roles"]
role = ("#{node.roles}"[/[\w_-]+/])
role_mng = mng_roles.include?(role)

## set monitor info.
## monitor(監視サーバ)はpacemakerでクラスタ組んでてそっちのdata_bagsを流用して一意に保ちたかった
if "#{environment}" != 'ci'
  cl = data_bag_item("cluster", "#{account}_monitor")
  role_mng = ''
elsif "#{environment}" == 'ci'
  cl = data_bag_item("cluster", "#{account}_monitor-ci")
end
monitor_addr_01 = cl["ucastip1"]
monitor_addr_02 = cl["ucastip2"]
monitor_hostname_01 = cl["host1"]
monitor_hostname_02 = cl["host2"]

## set mng data.
hosts_mng = data_bag_item("base_setting","#{account}_hosts-mng")
hosts_mng_data = hosts_mng["data"]
mng_local_data = ''

## set hosts template.
## mngじゃないやつはこれまでどおりの処理をする(クラスタデータも差し替える）
if "#{environment}" != 'mng' && "#{role_mng}" == 'false'
 template "/etc/hosts" do
  action :create
  source "hosts.alone.erb"
  variables ({
        :monitor_addr_01 => monitor_addr_01,
        :monitor_addr_02 => monitor_addr_02,
        :monitor_hostname_01 => monitor_hostname_01,
        :monitor_hostname_02 => monitor_hostname_02,
        :mng_data => hosts_mng_data
  })
 end
## mngに属するものはちょっと特殊な処理をする
elsif "#{environment}" =~ /mng/ || "#{role_mng}"
 ## ruby_blockスコープ内でchefのリソースが使えるようにselfで生やしておく
 recipe = self
 ruby_block "create_mng_hosts_file" do
  block do
   ## mngの中でもmonitorの文字列が含まれる監視系roleはセカンダリプライベートIPがhostsに載るように処理する（だってBGデプロイを見据えろみたいな話になったもんだから）
   eth01addr = ''
   if "#{role}" =~ /^monitor/
     ## セカンダリプライベートIPをohaiで取得するがkey側に値があるのでちょっと工夫して値を変数に入れる
     eth01_addr_val = node["network"]["interfaces"]["eth0:1"]["addresses"].select { |address, data| data["family"] == "inet" }
     eth01_addr_array = eth01_addr_val.keys
     eth01addr = eth01_addr_array[0]
   end
   ## clusterから呼んだアトリビュートとセカンダリプライベートIPが一致してたら末尾にlocalhostがつくように変数に入れる
   if "#{eth01addr}" == "#{monitor_addr_01}"
     localhost1="#{account}-monitor1 localhost"
     localhost2="#{account}-monitor2"
   elsif "#{eth01addr}" == "#{monitor_addr_02}"
     localhost1="#{account}-monitor1"
     localhost2="#{account}-monitor2 localhost"
   else
     localhost1="#{account}-monitor1"
     localhost2="#{account}-monitor2"
   end
   ## linesメソッドで区切り文字を/nとして1行ずつdataを呼んでlocalhostのIPと一致した場合に末尾を置換してもう一度くっつけてデータ化する。
   "#{hosts_mng_data}".lines(separator = "\n") do |line|
    if "#{line}" =~ /^#{localip} .*/ 
     newline = line.sub(/$/,' localhost')
     mng_local_data = mng_local_data + newline
    else
     mng_local_data = mng_local_data + line
    end
   end
   ## selfをくっつけて生やしたtemplateリソースでデータを差し替えてhostsをばらまく
   recipe.template "/etc/hosts" do
    action :create
    source "hosts.mng.erb"
    variables ({
        :monitor_addr_01 => monitor_addr_01,
        :monitor_addr_02 => monitor_addr_02,
        :monitor_hostname_01 => monitor_hostname_01,
        :monitor_hostname_02 => monitor_hostname_02,
        :mng_data => mng_local_data,
        :localhost1 => localhost1,
        :localhost2 => localhost2
    })
   end
  end
 end
end

## set network template.
template "/etc/sysconfig/network" do
  action :create
  source "network.erb"
end
```

mngのtemplateは以下のようなかんじ。

```
$ cat site-cookbooks/base_setting/templates/default/hosts.mng.erb 
127.0.0.1  localhost <%= node['hostname'] %> localhost.localdomain localhost4 localhost4.localdomain4

## mng
<%= @monitor_addr_01 %> monitor1-vip <%= @monitor_hostname_01 %> <%= @localhost1 %>
<%= @monitor_addr_02 %> monitor2-vip <%= @monitor_hostname_02 %> <%= @localhost2 %>
<%= @mng_data %>
```

レシピがやたら複雑で読みづらいことに。。
途中で値が変わったもんをohaiリロードして取り込むレシピを書こうとすると大してruby分からないなりにどうしてもruby_blockとか使わざるを得ないかんじになってくるといいますか。手前で実行されるプライベートIPくっつけるレシピ内でohaiリロードしています。それを取り込むためにruby_block使ってるかんじです。
こんなんなのは、zabbix_agentd.confとか別のクライアント設定ファイルと連携してるとかの事情もあります。
Route53のPrivateDNSつかえばいいのに的なツッコミは甘んじて受けなくもないです。
ローカルから読んだほうが高速で分かりづらい影響がないかなーとか思ったりもするけど、一斉配布するよりzoneファイル修正するだけのほうがラクですよねー。
まあそのうちそうすることになるんじゃないかなー。


参考：
http://qiita.com/sonots/items/8ac33ce011bbfb210edd
http://www.engineyard.co.jp/blog/2013/chef-recipe-lifecycle/
http://aikotobaha.blogspot.jp/2013/07/chefohai.html
http://qiita.com/smallpalace/items/86ebd59977faca0eb122
http://qiita.com/smallpalace/items/b6274905bde3c3a3fce9