Chef 實戰 part5 — 定期自動跑 chef-client 機制

Luyo
verybuy-dev
Published in
12 min readAug 29, 2017

接續前篇 Chef 實戰 part4 — 設定 Elasticsearch cluster,現在總算可以來設定 rolechef-client cookbook 實現自動更新機制了。

1. 確認 Berkshelf 設定

Learn Chef Rally 學習筆記 part7 — 除錯及定期執行 chef-client 的時候我已安裝好 chef-client cookbook 了,這邊再重新確認一下即可。

切換至 ~/learn-chef/ 目錄底下,打開 Berksfile

source 'https://supermarket.chef.io'
cookbook 'chef-client'

確認這個設定沒有問題,就可以執行 berks install 指令:

$ berks install
Resolving cookbook dependencies...
Using chef-client (8.1.8)
Using cron (4.1.3)
Using logrotate (2.2.0)
Using ohai (5.2.0)
Using windows (3.1.2)
Using compat_resource (12.19.0)

沒有需要更新的,所以立馬就跑完了,再來是 berks upload

$ berks upload
Skipping chef-client (8.1.8) (frozen)
Skipping compat_resource (12.19.0) (frozen)
Skipping cron (4.1.3) (frozen)
Skipping logrotate (2.2.0) (frozen)
Skipping ohai (5.2.0) (frozen)
Skipping windows (3.1.2) (frozen)

也沒有需要更新的,到這邊就可以確認 chef-client 已經在 Chef server 上待命了。

2. 設定 role

編輯 JSON 檔

再來是設定 role。在 Learn Chef Rally 學習筆記 part7 的時候我已經建立了一個 ~/learn-chef/roles/ 的目錄,先切換到這個目錄底下:

$ cd ~/learn-chef/roles/

然後參考 web.json 檔案,再生一個 es.json 內容如下:

{
"name": "es",
"description": "es server role.",
"json_class": "Chef::Role",
"default_attributes": {
"chef_client": {
"interval": 7200,
"splay": 1800
}
},
"override_attributes": {
},
"chef_type": "role",
"run_list": ["recipe[chef-client::default]",
"recipe[chef-client::delete_validation]",
"recipe[elasticsearch_ik::default]"
], "env_run_lists": {
}
}

ineterval = 7200, splay = 1800,表示每 2 小時會跑一次 chef-client ,且隨機在 1800 秒內挑一個時間去執行。

決定 interval、splay

來算一下兩個 node 同時被重新啟動造成 downtime 的秒數期望值,再考慮要不要更動這組設定。

我試了一下我的 instance 重啟 elasticsearch service 的 downtime 大約是 12 秒,保險一點算 15 秒好了,然後假設每次執行 chef-client 都會觸發重啟 service 的條件。

那麼當 node 1 處於 downtime 狀態的其中某 1 秒,node 2 也處於 downtime 狀態的機率大約是 15/1800;因為 node1 的 downtime 會有 15 秒,所以兩台同時處於 downtime 的期望值就是 15x15/1800。而 chef-client 每天會執行 12 次,假設一個月有 30 天,平均每個月會發生兩台同時處於 downtime 的秒數就是 (15x15/1800)x12x30 = 45 秒,換算成 SLA:

SLA = 1–15x(15/1800)x12x30/(86400x30) = 99.9983%

雖然不算是很理想,但考慮到這是最壞的情況,而實際情況是不太可能每次執行 chef-client 都觸發 service restart,所以評估下來應該算是可以接受的。而且未來只要再加一個 node,在最差的情況下三台同時處於 downtime 的平均秒數會變成 15x(15/1800)²x12x30 = 0.375 秒,而 SLA 會變成:

SLA = 1–15x(15/1800)²x12x30/(86400x30) = 99.999986%

這樣可靠性應該算就蠻能接受了吧,那就保留這組設定值。誒,以上算式是我自己掰出來的,不保證正確喔。

產生 role

再來就可以告訴 Chef server 我們要產生這個 role:

$ knife role from file es.json
Updated Role es

確認一下 role 的資訊是否正確:

$ knife role show es
chef_type: role
default_attributes:
chef_client:
interval: 7200
splay: 1800
description: es server role.
env_run_lists:
json_class: Chef::Role
name: es
override_attributes:
run_list:
recipe[chef-client::default]
recipe[chef-client::delete_validation]
recipe[elasticsearch_ik::default]

綁定 role 至 node 上

再來就可以綁定 role 到我的 nodes 上啦:

$ knife node run_list set es-* "role[es]"
ERROR: The object you are looking for could not be found
Response: node 'es-*' not found

呃,這個指令不給我用 wildcard 的寫法,只好一台一台綁:

$ knife node run_list set es-1 "role[es]"
es-1:
run_list: role[es]
$ knife node run_list set es-2 "role[es]"
es-2:
run_list: role[es]

最後就是來試跑 chef-client 了:

$ knife ssh 'role:es' 'sudo chef-client' --ssh-user centos --identity-file
~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 Running handlers:
172.31.21.70 Running handlers complete
172.31.21.70 Chef Client finished, 11/82 resources updated in 24 seconds
172.31.21.50
172.31.21.50 Running handlers:
172.31.21.50 Running handlers complete
172.31.21.50 Chef Client finished, 19/83 resources updated in 26 seconds

沒有問題,這樣就大功告成了!

更新 recipe

等等,突然想到我的 recipe 某一行還是用 name:es-* 這個條件,趕快來更新一下 elasticsearch_ik/recipes/default.rb

#elk_nodes = search(:node, 'name:es-*').map(&:ipaddress).sort.uniq 刪掉這行,換成下面這行
elk_nodes = search(:node, 'role:es').map(&:ipaddress).sort.uniq

3. 新增第三台 node

最後我想再看一個 node 起來,試試看用目前的整組設定是不是真的能夠一步到位。

先去 EC2 console 再開一台 CentOS t2.micro,把內網 IP 複製起來,對它做 bootstrapping:

$ knife bootstrap 172.31.18.83 --ssh-user centos --sudo --identity-file ~/.ssh/test.pem --node-name es-3 --run-list 'role[es]'
(...略)
172.31.18.83 11>> elk_nodes = search(:node, 'name:es-*').map(&:ipaddress).sort.uniq
(...略)
172.31.18.83 [2017-08-29T11:15:20+00:00] ERROR: No attribute `node['ipaddress']' exists on
172.31.18.83 the current node. Specifically the `ipaddress' attribute is not
172.31.18.83 defined. Please make sure you have spelled everything correctly.
172.31.18.83
172.31.18.83 [2017-08-29T11:15:20+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

結果還是失敗了,原因是 recipe 中的 node['ipaddress'] 屬性抓不到。

google 了很久都找不到解法,最後發現 knife bootstrap 有一個 --json-attribute 選項,說不定可以利用這東西來設定 node 的屬性。

死馬當活馬醫,跟剛剛一樣的指令,再加上 --json-attribute ,內容是 JSON 格式 '{"ipaddress":"172.31.18.83"}'

$ knife bootstrap 172.31.18.83 --ssh-user centos --sudo --identity-file ~/.ssh/test.pem --node-name es-2 --run-list 'role[es]' --json-attributes '{"ipaddress":"172.31.18.83"}'
(...略)

本來完全不抱期望,結果 bootstrap 居然成功了!

但我還要再跑一次 chef-client 讓另外兩個 node 去更新 IP 列表才行,跑完之後確認一下 cluster 有 3 個 nodes:

$ curl 172.31.21.70:9200/_cluster/health?pretty
{
"cluster_name" : "development",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 3,
"number_of_data_nodes" : 3,
"active_primary_shards" : 0,
"active_shards" : 0,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}

確認 es-3 順利加入 cluster 囉。

太感動了,至此總算完成了可以一道指令搞定一個 elasticsearch node 的 chef repository,真是成就感破表啊!

4. 從 Chef server 上移除 node

接下來要把多開的機器關掉,留兩台下來就好,順便可以練習一下將 node 從 Chef server 上移除的指令。

移除需要兩個步驟,第一步:

$ knife node delete es-3 --yes
Deleted node[es-3]

第二步:

$ knife client delete es-3 --yes
Deleted client[es-3]

接下來就可以把機器關掉了。

5. 確認 node 有自動跑 chef-client

過了好一陣子之後,回來下指令,確認一下 node 的自動更新機制有在運作:

$ knife status 'role:es' --run-list
46 minutes ago, es-2, ["role[es]"], centos 7.3.1611.
1 minute ago, es-1, ["role[es]"], centos 7.3.1611.

剛好遇到 es-1 前一分鐘才更新,這樣就確認更新機制正常囉。

你可能會覺得奇怪,為什麼兩台更新時間差距超過半小時? splay 不是只有 30 分鐘嗎?

其實是因為我的 es-2 有重開過,設定好 chef-client 的時間跟 es-1 是不一樣的,所以會有時間差。不過我們也可以從這裡觀察到一點:每台 node 的排程時間是各自獨立的!所以前面算的 SLA 如果是以這個情況來看,同時 downtime 的機率就會變得更小,可靠度就變得更高了。

心得

其實做到這邊,雖然已經完成了一個能用的 chef repository,但越是往前進,越是發現自己的不足,例如接下來我希望能夠針對環境做對應的設定、以及需要寫一些測試來確認 cookbook 能正確運作等等,這些目前我都還沒有學到,很多用法也都是自己撞牆撞出來的,覺得急需回 Learn Chef Rally 充電了。

--

--