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

Luyo
Luyo
Aug 26, 2017 · 31 min read

上一篇 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 這些用法。這些還沒學到的東西,雖然要自己摸索也是摸得出來,但可能就會漏東漏西,或某些用法不在正軌上,看來還是要找時間回去進修。

verybuy-dev

VeryBuy 研發手札

)

Luyo

Written by

Luyo

Founder, Developer of VeryBuy — https://www.verybuy.cc

verybuy-dev

VeryBuy 研發手札

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade