# 使用Chef
### 以Chef Server管理模式為例
我們剛才創建的cookbook的名字叫: apache-tutorial-1。它的目的就是要在指定的node上面安裝apache,并且啟動服務。
### 1 編寫recipe
讓我們編寫第一個recipe。試想一下,如果手工來安裝apache,需要下面三步動作。
- 安裝Apache
- 啟動Apache并且把它添加到開機啟動
- 配置home page
現在我們用recipe實現,打開cookbooks/apache-tutorial-1/recipes/default.rb文件,輸入以下Ruby代碼:
~~~
package 'apache2' do
action :install
end
service 'apache2' do
action [ :enable, :start ]
end
cookbook_file '/var/www/index.html' do
source 'index.html'
mode '0644'
end
~~~
我來解釋下上面的代碼:
在Chef中,package是一種資源類型,它告訴chef-client運行時候的「包」的名字,以及執行的動作,這段代碼里,是告訴chef-client,要安裝papache2. package在執行的時候,會自動的調用底層的provider來匹配相應的服務器平臺。
service會告訴chef-client啟動哪個服務。會調用底層的ohai組件來收集相關平臺信息,從而正確啟動相應的服務。這段代碼里,是告訴chef-client,想要啟動apache2.
cookbook_file會告訴chef-client來拷貝index.html文件到/var/www/index.html中,并且設置文件權限為0644.
這只是個簡單的recipe,讓我們繼續。
把下面html代碼添加到cookbook/apache-tutorial-1/files/default/index.html中:
~~~
<html>
<body>
<h1>Hello, world!</h1>
</body>
</html>
~~~
### 2 上傳Cookbook
我們寫完了這個cookbook,就可以把它上傳到我們之前搭建的Chef Server上面(非chef-solo模式)。在chef-repo目錄中執行下面命令:
~~~
$ knife cookbook upload apache-tutorial-1
~~~
然后打開我們的Chef Server Webui: [https://192.168.33.11/cookbooks](https://192.168.33.11/cookbooks)
就可以看到我們上傳的Cookbook了,如圖:

### 3 設置Run list
還是在Chef Server Webui上面,點擊:[https://192.168.33.11/nodes](https://192.168.33.11/nodes)
選擇你要安裝的node, 點擊編輯(edit),如圖:

在編輯界面,如圖把apache-tutorial-1移動到右邊,點擊保存。

~~~
說明: Run list就是一個運行列表,包含了所有需要安裝的role,以及按依賴順序把recipe排列。
~~~
### 執行cookbook配置node
cookbook設置完畢,我們就執行下面命令來配置node:
~~~
$ knife ssh 192.168.33.12 'sudo chef-client' -m -x vagrant -P vagrant
~~~
這個命令會在ip為192.168.33.12的node上面執行sudo chef-client命令來配置節點。當然,你也可以登陸到那臺虛擬機上面手工執行sudo chef-client命令。
命令執行成功之后,你登陸到192.168.33.12那臺虛擬機上面,就會看到apatch2已經正常運行了。

### 以OpsCode Chef為例
過程和自建Chef Server類似,可以自行練習。
### 以Chef Solo管理模式為例
當你對上面Chef Server模式有了一定了解,那么Chef Solo理解起來也不吃力了。
Chef Solo因為沒有Server,所以我們直接在node機器上來執行。
我們繼續拿上面用過的node2為例子。
**注意: 以下操作都在node2虛擬機上面**
~~~
$ mkdir -p /etc/chef/coobooks/nginx/recipes
~~~
必須要在/etc/chef目錄下面。
~~~
cd /etc/chef/coobooks/nginx/recipes
sudo touch default.rb
sudo vi default.rb
~~~
寫入下面Ruby代碼:
~~~
package 'nginx'
~~~
然后回到/etc/chef 目錄,創建node.json文件,然后編輯run list:
~~~
{ "run_list": ["recipe[nginx]"] }
~~~
然后在/etc/chef目錄下,建立solo.rb文件:
~~~
cookbook_path File.expand_path("../cookbooks", __FILE__)
json_attribs File.expand_path("../node.json", __FILE__)
~~~
用來指定cookbooks和node.json的位置。
然后我們執行命令:
~~~
$ sudo chef-solo solo.rb
~~~
安裝成功。

*chef solo*
### 啟動服務
我們需要在nginx被安裝完之后,可以自動啟動。
再次打開 /etc/chef/cookbooks/nginx/recipes/default.rb修改代碼為下面幾行:
~~~
package 'nginx'
service 'nginx' do
supports [:status, :restart, :reload]
action :start
end
~~~
我們在chef server那節里介紹過service這個資源的含義。supports,用來告訴Chef,支持status、restart、reload這三個命令。最后用action來告訴Chef,當前動作是start。
然后在/etc/chef下執行:
~~~
$ sudo chef-solo solo.rb
~~~
就能看到nginx被正常啟動了。 很酷。
### 增加用戶
用戶,是我們經常使用的,Chef也提供了User資源,來幫助你管理這些Chef使用者的用戶。
比如,我們可以在/etc/chef/cookbooks下面再創建一個名為user的recipes,
~~~
$ sudo mkdir /etc/chef/cookbooks/user/recipes
$ cd /etc/chef/cookbooks/user/recipes
$ sudo touch default.rb
~~~
在default.rb中輸入下面代碼:
~~~
user 'blackanger' do
password "$1$3YbJDvy2$JD2o6dEFjGvayRgmZWU030"
gid "admin"
home "/home/blackanger"
supports manage_home: true
end
~~~
說明: password,告訴Chef,此為用戶的密碼,是經過openssl加密的,使用此命令來加密:[openssl passwd -1 "passwd"]。 gid,告訴Chef,要把這個用戶加到admin組里面,home,是指定用戶的home目錄, supports,指定管理其home的權限。
然后再打開/etc/chef/node.json, 加入recipes['user']:
~~~
{ "run_list": ["recipe[nginx]", "recipe[user]"] }
~~~
執行命令:
~~~
$ sudo chef-solo solo.rb
~~~
我們就可以看到執行結果了:

*add user*
### 設定node.json
我們雖然創建好了用戶,但是仔細想想,這種方式不太好,假如我們配置多臺機器呢? user recipes里寫的太死,代碼無法重用了。 好在,我們Chef還可以允許我們把這種用戶信息寫到node.json里。
打開/etc/chef/node.json:
~~~
{
"run_list": ["recipe[nginx]", "recipe[user]"],
"user": {
"name": "blackanger",
"password": "$1$3YbJDvy2$JD2o6dEFjGvayRgmZWU030"
}
}
~~~
然后回到user recipes的default.rb文件中修改為:
~~~
user node[:user][:name] do
password node[:user][:password]
gid "admin"
home "/home/#{node[:user][:name]}"
supports manage_home: true
end
~~~
然后執行:
~~~
$ sudo chef-solo solo.rb
~~~
成功!是不是很酷啊!
### 使用template
回到剛才nginx的例子, 我們還需要給nginx設置一個配置文件,這個時候,就要用到template資源了。
~~~
$ sudo mkdir /etc/chef/cookbooks/nginx/templates/default
$ sudo touch /etc/chef/cookbooks/nginx/templates/default/nginx.conf.erb
~~~
注意,我們創建了一個nginx.conf.erb文件, 如果對Rails有了解的朋友,應該指定,erb是一個模板引擎,它可以支持你在這種模板里面變量運算、替換等操作。
打開nginx.conf.erb,輸入下面代碼:
~~~
server {
server_name 192.168.33.12;
root /home/<%= node[:user][:name] %>/demo;
}
~~~
注意:上面形如 <% ... %> 這樣格式的代碼,就是erb的語法,可以把需要替換的變量放到里面。
然后我們在回到nginx的recipe中告訴Chef應該使用這個template:
打開:/etc/chef/cookbooks/nginx/recipes/default.rb, 添加下面代碼:
~~~
template "/etc/nginx/sites-enabled/nginx.conf" do
source 'nginx.conf.erb'
notifies :restart, 'service[nginx]', :immediately
end
~~~
說明:"/etc/nginx/sites-enabled/nginx.conf" 是告訴Chef,template文件要保存的路徑。 source告訴Chef去哪找這個template, notifies是一個callback,告訴Chef要馬上重啟nginx服務。
然后我們分別使用directory和file資源來添加一個html文件,寫法同上,下面給出完成的nginx recipe:
~~~
package 'nginx'
service 'nginx' do
supports [:status, :restart, :reload]
action :start
end
directory "/home/#{node[:user][:name]}/demo" do
owner node[:user][:name]
end
file "/home/#{node[:user][:name]}/demo/index.html" do
owner node[:user][:name]
content "<h1>Hello blackanger!</h1>"
end
template "/etc/nginx/sites-enabled/nginx.conf" do
source 'nginx.conf.erb'
notifies :restart, 'service[nginx]', :immediately
end
~~~
然后執行我們的老命令: sudo chef-solo solo.rb, 愿望達成:

*run nginx recipe*
驗證:
~~~
$ curl http://127.0.0.1
<h1>Hello blackanger!</h1>
~~~
至此,我們就學會了使用chef-solo來安裝「一臺」機器。如果你想安裝多臺, 那么繼續按照上面的步驟,安裝chef-solo,然后復制cookbooks,一臺一臺的執行命令。
這樣是不是還是很麻煩呢?
那當然了! 辛虧我們還有knife,這個超級助手。
### chef-solo with knife
Knife是一套強大的命令行Chef管理工具。我們可以使用knife來幫助我們簡化麻煩的工作,讓我們不需要通過Chef Server就可以直接和需要配置的服務器交互。
因為我們使用的chef-solo方式,所以我們先安裝一個gem:
~~~
$ gem install knife-solo
~~~
安裝好之后執行命令:
~~~
$ knife solo init knife-solo-demo
~~~
就會幫助我們生成一個solo目錄:

注意: 在使用這個命令之前,要確保你的Ruby環境是否唯一,否則可能會出現找不到knife子命令solo的錯誤。
這個目錄中,.chef/文件夾下面包含一個knife.rb文件。這個文件在我們講過的Chef Server模式下也是一樣存在的,具體的配置文檔可以去[官網](http://docs.getchef.com/config_rb_knife.html)查看。這里生成的knife.rb文件中包含下面代碼:
~~~
cookbook_path ["cookbooks", "site-cookbooks"]
node_path "nodes"
role_path "roles"
environment_path "environments"
data_bag_path "data_bags"
#encrypted_data_bag_secret "data_bag_key"
knife[:berkshelf_path] = "cookbooks"
~~~
這里cookbook_path是相對于倉庫根目錄的路徑,指定了角色中使用的Cookbook信息node_path和role_path分別定義了節點和角色的存儲位置,都是相對于根目錄的路徑。這里有個berkshelf_path, berkshelf是一個第三方cookbook管理工具,我們的cookbooks目錄,就是存放這些第三方cookbook,而site-cookbooks是用來存放我們自己編寫的cookbook的地方。
然后我們使用命令:
~~~
$ knife solo bootstrap vagrant@192.168.33.11
~~~
就可以在另一臺vagrant機器(192.168.33.11)上配置chef-solo。它會幫我們安裝chef-client, 創建node,創建cookbooks。
knife solo bootstrap實際上執行了兩個命令:
-
knife solo prepare
這個命令主要是初始化服務器,安裝chef,配置node,上傳cookbook
-
knife solo cook
這個命令主要是上傳cookbook,并且執行指令 chef-solo.
有了這個工具,就方便很多了。
- 序
- Chapter 1: 初識Chef
- 一些背景
- Chef vs Puppet
- Chapter 2: Chef應用
- Chef架構
- Chef能做什么
- Chef組件
- Chef環境安裝
- chef-server
- opscode-chef
- chef-solo
- Chef實戰
- 實戰前的必修理論
- 使用Chef
- Chapter 3: Ruby基礎
- 對象與方法
- 標識符
- 類與模塊
- 數據類型
- 真與假
- 控制語句
- 代碼塊
- Chapter 4: Chef源碼架構
- Rubygems與gem
- bundler
- Chef源碼組織
- Chapter 5: Rails基礎
- Rails是什么
- MVC架構
- Restful
- Rails組成與項目結構
- Chapter 6: Chef Server WebUI
- Chef Server Webui組織結構
- Chef Rest API
- 參考