Chef 實戰 part2 — 使用官方 Cookbook 架設 Elasticsearch

Luyo
verybuy-dev
Published in
31 min readAug 26, 2017

上一篇 Chef 實戰 part1 — 從 0 開始寫 Cookbook 架設 Elasticsearch 中,我是用從無到有的方式來架設 Elasticsearch node。但其實官方有提供寫好的 cookbook,我覺得主要的差別是只要設定好需要的參數,就可以不用自己準備設定檔。

我們可以到 Chef Supermarket 搜尋 “elasticsearch” 就可以找到官方的 elasticsearch cookbook,然後參考 Learn Chef Rally 學習筆記 part7 — 除錯及定期執行 chef-client 學到的 Berkshelf 相關指令來安裝及應用這個 cookbook。

1. 產生新的 cookbook

先備份上一篇做好的 cookbook:

$ cd ~/learn-chef/cookbooks
$ mv elasticsearch_ik elasticsearch_ik_bak

重新產生一個 cookbook:

$ chef generate cookbook elasticsearch_ik
Generating cookbook elasticsearch_ik
- Ensuring correct cookbook file content
- Committing cookbook files to git
- Ensuring delivery configuration
- Ensuring correct delivery build cookbook content
- Adding delivery configuration to feature branch
- Adding build cookbook to feature branch
- Merging delivery content feature branch to master
Your cookbook is ready. Type `cd elasticsearch_ik` to enter it.There are several commands you can run to get started locally developing and testing your cookbook.
Type `delivery local --help` to see a full list.
Why not start by writing a test? Tests for the default recipe are stored at:test/smoke/default/default_test.rbIf you'd prefer to dive right in, the default recipe can be found at:recipes/default.rb

進入目錄:

$ cd elasticsearch_ik

2. 更新 Berkshelf 相依套件

編輯 Berksfile 內容,按照官方的 elasticsearch cookbook 指示,在檔案最後新增一行:

# frozen_string_literal: true
source 'https://supermarket.chef.io'
metadatacookbook 'elasticsearch', '~> 3.2.1'

更新完 Berksfile 之後執行 bersk install 指令安裝相依套件:

$ berks install
(...略)

最後將這些套件上傳至 Chef server:

$ berks upload
(...略)

3. 編輯 recipe

處理好相依套件之後就可以來寫 recipe 了,但問題是要怎麼將 cookbook 呼叫進來?研究了一下 cookbook-elasticsearch 自己 recipes/default.rb 檔案內容:

# Encoding: utf-8
#
# Cookbook Name:: elasticsearch
# Recipe:: default
#

include_recipe 'chef-sugar'

# see README.md and test/fixtures/cookbooks for more examples!
elasticsearch_user 'elasticsearch' do
node['elasticsearch']['user'].each do |key, value|
# Skip nils, use false if you want to disable something.
send(key, value) unless value.nil?
end
end

elasticsearch_install 'elasticsearch' do
node['elasticsearch']['install'].each do |key, value|
# Skip nils, use false if you want to disable something.
send(key, value) unless value.nil?
end
end

elasticsearch_configure 'elasticsearch' do
node['elasticsearch']['configure'].each do |key, value|
# Skip nils, use false if you want to disable something.
send(key, value) unless value.nil?
end
end

elasticsearch_service 'elasticsearch' do
node['elasticsearch']['service'].each do |key, value|
# Skip nils, use false if you want to disable something.
send(key, value) unless value.nil?
end
end

# by default, no plugins
node['elasticsearch']['plugin'].each do |plugin_name, plugin_value|
elasticsearch_plugin plugin_name do
plugin_value.each do |key, value|
# Skip nils, use false if you want to disable something.
send(key, value) unless value.nil?
end
end
end

一開始就有 include_recipe 的語法,把相依套件呼叫進來。接下來是自己定義的幾個 resource 的區塊,裡面就是將有設定的 attributes 叫出來執行 send(key, value)

先試試 include_recipe ,編輯 default.rb

include_recipe 'elasticsearch'

NOTE: 其實開頭少了一行 package 'java-1.8.0-openjdk' ,因為實戰 part1 已經安裝過這個套件,所以這邊就漏掉了。

上傳並更新 node:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
WARNING: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
WARNING: The cookbooks: elasticsearch_ik exist in multiple places in your cookbook_path.
A composite version of these cookbooks has been compiled for uploading.
IMPORTANT: In a future version of Chef, this behavior will be removed and you will no longer
be able to have the same version of a cookbook in multiple places in your cookbook_path.
WARNING: The affected cookbooks are located:
elasticsearch_ik:
/home/yo/learn-chef/cookbooks/elasticsearch_ik_bak
/home/yo/learn-chef/cookbooks/elasticsearch_ik
WARNING: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Uploading elasticsearch_ik [0.1.0]
ERROR: Version 0.1.0 of cookbook elasticsearch_ik is frozen. Use --force to override.
WARNING: Not updating version constraints for elasticsearch_ik in the environment as the cookbook is frozen.
ERROR: Failed to upload 1 cookbook.
172.31.21.70 Starting Chef Client, version 13.3.42
172.31.21.70 resolving cookbooks for run list: ["elasticsearch_ik"]
172.31.21.70 Synchronizing Cookbooks:
172.31.21.70 - elasticsearch_ik (0.1.0)
172.31.21.70 Installing Cookbook Gems:
172.31.21.70 Compiling Cookbooks...
172.31.21.70 Converging 0 resources
172.31.21.70
172.31.21.70 Running handlers:
172.31.21.70 Running handlers complete
172.31.21.70 Chef Client finished, 0/0 resources updated in 10 seconds

中間有一些警告訊息,因為他會讀到舊的 elasticsearch_ik_bak ,不知道這是什麼機制,試試看把舊的移走:

$ mv ~/learn-chef/cookbooks/elasticsearch_ik_bak /tmp/

再試一次:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
Uploading elasticsearch_ik [0.1.0]
ERROR: Version 0.1.0 of cookbook elasticsearch_ik is frozen. Use --force to override.
WARNING: Not updating version constraints for elasticsearch_ik in the environment as the cookbook is frozen.
ERROR: Failed to upload 1 cookbook.
172.31.21.70 Starting Chef Client, version 13.3.42
172.31.21.70 resolving cookbooks for run list: ["elasticsearch_ik"]
172.31.21.70 Synchronizing Cookbooks:
172.31.21.70 - elasticsearch_ik (0.1.0)
172.31.21.70 Installing Cookbook Gems:
172.31.21.70 Compiling Cookbooks...
172.31.21.70 Converging 0 resources
172.31.21.70
172.31.21.70 Running handlers:
172.31.21.70 Running handlers complete
172.31.21.70 Chef Client finished, 0/0 resources updated in 09 seconds

沒有再讀到舊的部分了,但還是上傳失敗,說 “elasticsearch_ik is frozen”,要加 --force 強制更新。

但與其硬是把 0.1.0 複寫掉,不如來更新 metadata.rb 的版號吧。打開 metadata.rbversion 更新為 0.2.0:

name 'elasticsearch_ik'
maintainer 'The Authors'
maintainer_email 'you@example.com'
license 'All Rights Reserved'
description 'Installs/Configures elasticsearch_ik'
long_description 'Installs/Configures elasticsearch_ik'
version '0.2.0'
chef_version '>= 12.1' if respond_to?(:chef_version)

上傳至 Chef server:

$ knife cookbook upload elasticsearch_ik
Uploading elasticsearch_ik [0.2.0]
Uploaded 1 cookbook.

看起來是成功了,再重跑 knife ssh 的部分:

$ knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 [2017-08-26T19:08:54+00:00] ERROR: Cookbook elasticsearch not found. If you're loading elasticsearch from another cookbook, make sure you configure the dependency in your metadata
172.31.21.70 [2017-08-26T19:08:54+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

它說 “Cookbook elasticsaerch not found”,要在 metadata 裡設定相依套件。看來單純 include_recipe 是不夠的。

4. 更新 metadata.rb

研究一下人家的 metadata.rb 是怎麼寫的:

# Encoding: utf-8
name 'elasticsearch'
maintainer 'Karel Minarik'
maintainer_email 'karel.minarik@elasticsearch.org'
license 'Apache 2.0'
description 'Installs and configures Elasticsearch'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '3.2.1'

supports 'amazon'
supports 'centos'
supports 'debian'
supports 'fedora'
supports 'redhat'
supports 'ubuntu'

depends 'apt'
depends 'yum'
depends 'chef-sugar'
depends 'ark'


issues_url 'https://github.com/elastic/cookbook-elasticsearch/issues'
source_url 'https://github.com/elastic/cookbook-elasticsearch'

chef_version '>= 12.5' if respond_to?(:chef_version)

顯然可以用 depends 語法來宣告相依套件是哪些,那就來修改自己的 metadata.rb

name 'elasticsearch_ik'
maintainer 'The Authors'
maintainer_email 'you@example.com'
license 'All Rights Reserved'
description 'Installs/Configures elasticsearch_ik'
long_description 'Installs/Configures elasticsearch_ik'
version '0.2.0'
chef_version '>= 12.1' if respond_to?(:chef_version)
depends 'elasticsearch'

存檔後上傳並更新 node:

$ knife cookbook upload elasticsearch_ik --force; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
$ knife cookbook upload elasticsearch_ik --force; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
Uploading elasticsearch_ik [0.2.0]
Uploaded 1 cookbook.
172.31.21.70 Starting Chef Client, version 13.3.42
172.31.21.70 resolving cookbooks for run list: ["elasticsearch_ik"]
172.31.21.70 Synchronizing Cookbooks:
172.31.21.70 - apt (6.1.3)
172.31.21.70 - elasticsearch (3.2.1)
172.31.21.70 - yum (5.0.1)
172.31.21.70 - chef-sugar (3.4.0)
172.31.21.70 - ark (3.1.0)
172.31.21.70 - build-essential (8.0.3)
172.31.21.70 - seven_zip (2.0.2)
172.31.21.70 - windows (3.1.2)
172.31.21.70 - ohai (5.2.0)
172.31.21.70 - mingw (2.0.1)
172.31.21.70 - elasticsearch_ik (0.2.0)
(...略)
172.31.21.70 [2017-08-26T19:15:19+00:00] ERROR: elasticsearch_install[elasticsearch] (elasticsearch::default line 17) had an error: Chef::Exceptions::Package: yum_package[elasticsearch] (/var/chef/cache/cookbooks/elasticsearch/libraries/provider_install.rb line 58) had an error: Chef::Exceptions::Package: Installed package elasticsearch-5.5.1-1 is newer than candidate package elasticsearch-5.5.0-1
172.31.21.70 [2017-08-26T19:15:19+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

前面看起來還不錯,還後面還是失敗了,原因是 “elasticsearch-5.5.1–1 is newer than candidate package elasticsearch-5.5.0–1”,因為我之前就裝了 5.5.1–1 這個 package 但現在預設的版本比較舊,是 5.5.0–1,不過看來 include_recipe 的問題是已經解決了。

5. 設定 attritubes

產生 attributes 目錄

再稍微觀察一下 supermarket 頁面上的說明,在 elasticsearch_install 這個 resource 有 version 參數:

elasticsearch_install 'my_es_installation' do
type 'package' # type of install
version '5.5.0'
action :install # could be :remove as well
end

那應該就可以修改預設值來複寫掉這個參數。

要怎麼修改呢?說明的一開始有介紹一些屬性可供使用:

default['elasticsearch']['user'] = {}
default['elasticsearch']['install'] = {}
default['elasticsearch']['configure'] = {}
default['elasticsearch']['service'] = {}
default['elasticsearch']['plugin'] = {}

看起來就是設定 default['elasticsearch']['install'] 這東西了。但 default 這東西應該要是 node 的一個屬性才對,因為

編輯 default.rb

default['elasticsearch']['install']['version'] = '5.5.1'include_recipe 'elasticsearch'

上傳並更新 node:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 [2017-08-26T19:28:53+00:00] ERROR: undefined local variable or method `default' for cookbook: elasticsearch_ik, recipe: default :Chef::Recipe
172.31.21.70 [2017-08-26T19:28:53+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

他說 default 這東西未定義,看來用法不對。

google 了一陣子,找到一篇 aws 的文件,裡面說:

An attribute file is a Ruby application that assigns values to one or more attributes. It must be in the cookbook’s attributes folder.

原來 cookbook 還可以有一個叫 attributes 的資料夾!看了 elastic/cookbook-elasticsearch 的 GitHub 還真的有。舉一反三,說不定有 chef generate attribute 的指令,檔案的話就學他叫 default.rb

$ chef generate attribute default
Recipe: code_generator::attribute
* directory[/home/yo/learn-chef/cookbooks/elasticsearch_ik/attributes] action create
- create new directory /home/yo/learn-chef/cookbooks/elasticsearch_ik/attributes
- restore selinux security context
* template[/home/yo/learn-chef/cookbooks/elasticsearch_ik/attributes/default.rb] action create
- create new file /home/yo/learn-chef/cookbooks/elasticsearch_ik/attributes/default.rb
- update content in file /home/yo/learn-chef/cookbooks/elasticsearch_ik/attributes/default.rb from none to e3b0c4
(diff output suppressed by config)
- restore selinux security context

哈,還真的被我猜中了,Chef 自動產生了一個 attributes/ 目錄!

設定 version

打開 attributes/default.rb 來寫入我的設定值:

default['elasticsearch']['install']['version'] = '5.5.1'

上傳並更新 node:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 [2017-08-26T20:05:50+00:00] ERROR: could not find filename for attribute .default.rb.swp in cookbook elasticsearch_ik
172.31.21.70 [2017-08-26T20:05:50+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

失敗了,原因是找不到 .default.rb.swp ,怪了,這不是 vim 用的暫存檔嗎?怎麼沒有被 Chef 自動忽略掉?

算了反正就先把它關掉從 vim 關掉,然後重跑一次:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 -## GC configuration
172.31.21.70 +-Xms495m
172.31.21.70 +-Xmx495m
(...略)
172.31.21.70 --- /etc/elasticsearch/elasticsearch.yml 2017-08-26 14:12:37.823090312 +0000
172.31.21.70 +++ /etc/elasticsearch/.chef-elasticsearch20170826-3976-10jkcpv.yml 2017-08-26 20:06:52.575265411 +0000
172.31.21.70 @@ -1,2 +1,14 @@
172.31.21.70 -network.host: 172.31.21.70
(...略)
172.31.21.70 Chef Client finished, 13/47 resources updated in 13 seconds

結果成功了,然後也在其中看到他幫我把記憶配置設定成 495 mb,但 network.host 的設定也被刪掉了。

因為有幾個設定檔被更新了,所以先重新啟動 elasticsearch:

$ knife ssh 'name:es-1' 'sudo service elasticsearch restart' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
172.31.21.70 Restarting elasticsearch (via systemctl): [ OK ]

先進 node 看看狀態如何:

$ sudo service elasticsearch status
● elasticsearch.service - Elasticsearch
Loaded: loaded (/usr/lib/systemd/system/elasticsearch.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2017-08-26 20:13:46 UTC; 4s ago
Docs: http://www.elastic.co
Process: 4959 ExecStartPre=/usr/share/elasticsearch/bin/elasticsearch-systemd-pre-exec (code=exited, status=0/SUCCESS)
Main PID: 4961 (java)
CGroup: /system.slice/elasticsearch.service
└─4961 /bin/java -Xms495m -Xmx495m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75...
Aug 26 20:13:46 ip-172-31-21-70 systemd[1]: Starting Elasticsearch...
Aug 26 20:13:46 ip-172-31-21-70 systemd[1]: Started Elasticsearch.
Aug 26 20:13:46 ip-172-31-21-70 elasticsearch[4961]: OpenJDK 64-Bit Server VM warning: If the number of ...s=N
Hint: Some lines were ellipsized, use -l to show in full.

重啟成功。但此時在工作站 curl 172.31.21.70:9200 抓不到東西是正常的,因為預設的 host 是 localhost,所以接下來我們再更新 network.host 的設定應該就可以了。

設定 network.host 及 allocated_memory

找一下關於 elasticsearch_configure 的說明,在 GitHub repository 裡找到一段:

elasticsearch_configure 'elasticsearch' do
allocated_memory '512m'
configuration ({
'cluster.name' => 'escluster',
'node.name' => 'node01',
'http.port' => 9201
})
end

那就來修改 recipes/default.rb ,加入一段設定:

elasticsearch_configure 'elasticsearch' do
allocated_memory '512m'
configuration ({
'cluster.name' => 'development',
'network.host' => '172.31.21.70'
})
end

上傳並更新 node:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
Uploading elasticsearch_ik [0.2.0]
Uploaded 1 cookbook.
172.31.21.70 [2017-08-26T20:27:28+00:00] FATAL: Errno::ENOMEM: Cannot allocate memory - fork(2)

誒,怎麼搞的記憶不夠了?連到 node 裡用 free -m 看一下,的確是沒剩多少記憶體了,只好再增加設定,把記憶體用量關小一點:

elasticsearch_configure 'elasticsearch' do
allocated_memory '256m'
configuration ({
'cluster.name' => 'development',
'network.host' => '172.31.21.70'
})
end

更新 node 之前必須先把 elasticsearch service 停掉,把記憶體釋放出來,不然會一直吐失敗訊息:

$ knife ssh 'name:es-1' 'sudo service elasticsearch stop' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
172.31.21.70 Stopping elasticsearch (via systemctl): [ OK ]

上傳並更新 node:

$ knife cookbook upload elasticsearch_ik; knife ssh 'name:es-1' 'sudo chef-client' --ssh-user centos --identity-file ~/.ssh/test.pem --attribute ipaddress
(...略)
172.31.21.70 --Xms495m
172.31.21.70 --Xmx495m
172.31.21.70 +-Xms256m
172.31.21.70 +-Xmx256m

(...略)
172.31.21.70 +network.host: 172.31.21.70
(...略)
172.31.21.70 Chef Client finished, 5/45 resources updated in 13 seconds

看起來沒問題,檔案有被更新,最後 curl 看看能不能抓到東西:

$ curl 172.31.21.70:9200
{
"name" : "es-1",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "gBbJ_Y_3SJW3n2wzu905cg",
"version" : {
"number" : "5.5.1",
"build_hash" : "19c13d0",
"build_date" : "2017-07-18T20:44:24.823Z",
"build_snapshot" : false,
"lucene_version" : "6.6.0"
},
"tagline" : "You Know, for Search"
}

沒有問題,破關!

到此,利用官方提供的 cookbook 來安裝一台 elasticsearch node 的任務就完成囉!可以跟前一篇的結果比較一下,這次的 recipes/default.rb 總共只寫了 9 行程式碼就結束了:

include_recipe 'elasticsearch'elasticsearch_configure 'elasticsearch' do
allocated_memory '256m'
configuration ({
'cluster.name' => 'development',
'network.host' => '172.31.21.70',
})
end

NOTE: 其實開頭少了一行 package 'java-1.8.0-openjdk' ,因為實戰 part1 已經安裝過這個套件,所以這邊就漏掉了。

attributes/default.rb 則只有一行:

default['elasticsearch']['install']['version'] = '5.5.1'

另外還有 metadata.rb 新加的一行:

depends 'elasticsearch'

心得

  • 有官方的 cookbook 就先考慮使用,會方便蠻多的。
  • 原來 resource 是可以自己定義的,但短時間內應該還不太會碰需要自己寫的狀況吧。
  • attributes/ 底下的檔案不會自動忽略編輯器用的暫存檔,所以編輯完要記得關掉。
  • 學完 Learn Chef Rally 的前 3 個 module,還沒有提到 include_recipedependsattributes 這些用法。這些還沒學到的東西,雖然要自己摸索也是摸得出來,但可能就會漏東漏西,或某些用法不在正軌上,看來還是要找時間回去進修。

--

--