本文将结合实际经验介绍 Chef 环境的安装过程,尤其是在无网络连接情况下的安装;接着描述 Chef 如何使用,包括 Chef 本身的使用,和在一个应用中如何调用 Chef;最后会
重点介绍一下 Chef 的异常处理机制。通过本文,读者将了解到 Chef 的工作机制,和如何使用 Chef 来自动化完成服务器的配置
管理工作。Chef 是
一款自动化服务器配置管理工具,可以对所管理的对象实行自动化配置,如系统管理,安装软件等。Chef 由三大组件组成:Chef Server、Chef Workstation 和 Chef Node。
Chef Server 是核心服务器,维护了一套配置脚本(Cookbook),与每个被管节点(Chef Node)交互并给出配置指令。
Chef Workstation 提供了我们与 Chef Server 交互的接口:我们在 Workstation 上创建定义 Cookbook,并将 Cookbook 上传到 Chef Server 上以保证被管机器能从 Chef Server 上取得最新的配置指令。
Chef Node 是安装了 chef-client 并注册了的被管理节点,可以是物理机或者虚拟机或者其他对象。Chef Node 每次运行 chef-client 时都会从 Chef Server 端取得最新的配置指令(Cookbook)并按照指令配置自己。
一套 Chef 环境包含一个 Chef Server,至少一个 Chef Workstation,以及一到多个 Chef Node。
Chef 环境的安装
Chef 环境的安装步骤一般是:先安装 Chef Server,然后配置 Chef Workstation, 最后根据需要在客户端机器上安装 Chef Client 并将其注册成 Chef Node。Chef Server 和 Chef Workstation 可以配在一台机器上,也可以分开配置。由 Chef Server、Chef Workstation 和多个 Chef Node 组成整个 Chef 环境。
在 Chef 的官网上有详细的 Chef 安装步骤说明,官网提供的是在有外部网络环境的前提下利用网络自动下载和安装软件。本文将根据实践提供一个无外部网络环境下的 Chef 环境安装过程。
介质准备
由于服务器是无外部网络环境的,事先将所需的软件包下载到本地准备好。
从 Chef 官网(http://www.opscode.com/chef/install)下载 Chef Server 和 Chef Client 的安装包以及 Chef Repository 包。
Chef Server 安装包
打开http://www.opscode.com/chef/install, 点击"Chef Server"页,选择合适的操作系统,版本等,选择最新的 Chef Server 版本下载。
Chef Client 安装包
打开http://www.opscode.com/chef/install, 点击"Chef Client"页,选择合适的操作系统,版本等,选择最新的 Chef Client 版本下载。
从https://github.com/opscode/chef-repo下载 Chef Repository (chef-repo-master.zip )
由于我们服务器的操作系统是 64 位的 Redhat 6.4,所以在下载的时候选择 Enterprise Linux 版本 6,下载下来的 Chef Server 和 Chef Client 安装包都是 RPM 格式,下面的安装就以我们的环境为例: Chef Server 安装包为 chef-server-11.0.11-1.el6.x86_64.rpm, Chef Client 安装包为 chef-11.10.4-1.el6.x86_64.rpm。
安装 Chef Server
将 Chef Server 的安装包上传到 Chef Server 服务器上。登陆到 Chef Server 服务器上,按照如下步骤配置 Chef Server:
安装 Chef Server 安装包:
$ rpm -ihv chef-server-11.0.11-1.el6.x86_64.rpm
配置 Chef Server 11.x: (确保防火墙已关闭)
$ sudo chef-server-ctl reconfigure
此命令会创建 Chef Server11.x 的所有必需组件,包括 Erchef、RabbitMQ,、PostgreSQL 等。
验证服务器的 hostname 是合法的带域名的全名称。
可运行以下命令将 hostname 加到/etc/hosts 文件中:
$ echo -e "Chef_Server_IP `hostname` `hostname -s`" | sudo tee -a /etc/hosts
验证 Chef Server 11.x 是否安装成功。
可通过两种方式验证:一是在 Chef Server 上运行"$ sudo chef-server-ctl test"命令,此命令会运行 chef-pedant 的测试组件并报告所有组件正常工作,安装正确。二是直接在浏览器中打开 Chef Server 的页面: https://Chef_Server_IP,若能出现登录界面,说明 Chef Server 已经正确启动。
安装 Chef Workstation
将 Chef Client 安装包和 Chef Repository 上传到 Chef Workstation 服务器上。登陆到 Chef Workstation 服务器上,按照如下步骤配置 Chef Workstation:
安装 Chef Client 安装包:
# rpm -ihv chef-11.10.4-1.el6.x86_64.rpm
验证 chef-client 已经安装成功:
# chef-client -v
Chef: 11.10.4
确定一个作为 Chef Repository 的目录,如创建/home/chef 目录(后面将以此为例)。将 Chef Repository 包(chef-repo-master.zip)解压并拷贝到/home/chef 目录,重命名为 chef-repo。 在/home/chef 下创建.chef 目录。 将 Chef Server 上的 admin.pem 和 chef-validator.pem 文件(位于/etc/chef-server) 拷贝到 Chef Repository 的.chef 目录中。 运行"knife configure --initial" 命令配置 Chef Workstation,示例如下:
清单 1.配置 Chef Workstation
[root@chefwst .chef]# knife configure --initialWhere should I put the config file? (/root/.chef/knife.rb) /home/chef/chef-repo/.chef/knife.rbPlease enter the chef server URL: [https://localhost:443] https://Chef_Server_IP:443Please enter a name for the new user: [root]Please enter the existing admin name: [admin]Please enter the location of the existing admin's private key: [/etc/chef-server/admin.pem] /home/chef/chef-repo/.chef/admin.pemPlease enter the validation clientname: [chef-validator]Please enter the location of the validation key: [/etc/chef-server/chef-validator.pem] /home/chef/chef-repo/.chef/chef-validator.pemPlease enter the path to a chef repository (or leave blank): /home/chef/chef-repoCreating initial API user...Please enter a password for the new user:Created user[root]Configuration file written to /home/chef/chef-repo/.chef/knife.rb[root@chefwst .chef]# lsadmin.pem chef-validator.pem knife.rb root.pem 添加环境变量:
# echo 'export PATH="/opt/chef/embedded/bin:$PATH"' >> ~/.bash_profile && source ~/.bash_profile
验证 Chef Workstation 是否配置成功:
一个 Workstation 安装成功的标志是可以使用"Knife"命令与 Server 端进行通信。运行"knife client list"和"knife user list"进行验证,如清单 2 所示。
清单 2. 验证 Chef Workstation
[root@chefwst ~]# cd /home/chef/chef-repo[root@chefwst chef-repo]# knife client listchef-validatorchef-webui[root@chefwst chef-repo]# knife user listadminroot
安装 Chef Client
将 Chef Client 安装包上传到目标机器上,登陆到此机器上,按照如下步骤配置 Chef Client:
安装 Chef Client 安装包:
# rpm -ihv chef-11.10.4-1.el6.x86_64.rpm
验证 chef-client 已经安装成功:
# chef-client -v
Chef: 11.10.4
确保 Chef Client 机器时钟与 Server 的时钟是同步的 (相差少于 15 分钟) 将安装了 Chef Client 的此机器注册成一个 Chef Node。
在 Chef Workstation 上运行 bootstrap 命令:
# knife bootstrap Chef_Client_IP -x username -P password
bootstrap 命令会检查客户端有没有安装 chef-client 软件,如果没有安装,会从网络上直接下载安装包去安装然后注册;如果已经安装了,则会直接将这个客户端注册成一个 Chef Node。可以接着在 workstation 上执行 node list 命令查看是否多了一个 node:
# knife node list
Chef 的使用
Chef 环境安装完成以后,我们来看看如何使用这套环境来进行配置管理。总体来说,Chef 的配置过程是:
在 Workstation 上定义各个 Chef Client 应
该如何配置自己,然后将这些信息上传到 Server 端。 每个 Chef Client 连到 Server 查看如何配置自己,然后进行自我配置。
在 Workstation 上使用 Cookbook 来定义配置方法。Cookbook 使用 Ruby 脚本定义对 Chef Client 的各种操作,具体 Cookbook 的写法本文不做叙述。一旦 Cookbook 写好之后,就可以重复使用,可以对多个 Chef Client 进行批量配置。一般从创建 Cookbook 到使用 Cookbook 会包括以下几个过程。
在 Workstation 上创建 Cookbook
使用 knife 命令可以快速创建一个 Cookbook,如:
# knife cookbook create db2
编辑 Cookbook
根据实际需要,编辑 Cookbook 里的 Recipe,可以定义各种对服务器的配置操作,如系统管理,安装软件等。
同步 Cookbook
将在 Workstation 上写的 Cookbook 同步上传到 Server 上,可以通过 upload 命令实现:
# knife cookbook upload db2
此命令将最新的 db2 Cookbook 上传到 Server 端,这样 Client 端就能从 Server 端得到最新的配置指令。
也可以将所有的 Cookbook 都一起上传到 Server 端:
# knife cookbook upload --all
将 Cookbook 添加到要配置的 Node 的 Run List 中,如:
# knife node run_list add chef-node2 recipe[db2]
此命令将名为 db2 的 Cookbook 下的默认 Recipe(default.rb)添加到名为 chef-node2 的 Node 的 run_list 中。
也可以指定 Cookbook 里的某个特定 Recipe 添加,如:
# knife node run_list add chef-node2 recipe[db2::createdb]
此命令就将特定的 Recipe(created.rb)添加到 Node 的 Run List 里。可以通过 knife 的 node show 命令查看某个 Node 的具体信息:
# knife node show chef-node2
此命令可以看到 Node 的 Run List。
查看更详细的 Node 信息可以加上-l 参数:
# knife node show –l chef-node2
运行 Cookboo
在 Chef Node 上直接运行chef-client命令,Chef Client 就会从 Server 端下载最新的配置脚本,然后按照配置脚本配置自己(即脚本运行的过程)。
除掉在 Client 端直接运行chef-client命令,也可以在 Workstation 上运行 knife ssh 命令来达到同样的效果。不同的是,在 Client 端运行 chef-client 命令只是对自己一个 Node 进行配置,而在 Workstation 上运行的 knife ssh 命令可以同时对多个 Client 端进行批量配置。
k
Chef 的 API 调用
在实际使用中,我们经常需要将 Chef 集成到已有的系统中,这个时候就需要调用 Chef 的 API 来完成。Chef 本身提供了 REST API,可以方便的被调用。只是有少许特殊功能 REST API 不能完成(如注册 Chef Node),还需要调用 Chef 的命令行。本节先介绍 Chef 的 REST API,然后讨论 Chef 的命令调用。
调用 Chef REST API
Chef 的 REST API 提供了对 Chef 内对象的增删改查操作,如增加、删除一个节点、修改节点属性;查询一个 Cookbook 等。具体的每个 API 可以在 Chef 官网中找到,本章节我们主要对调用一个 REST API 的具体过程做出说明。
首先,调用 Chef 的 REST API 之前需要与 Chef Server 端建立认证。
Chef 的认证是基于公私钥的非对称加密机制对用户进行认证。Chef Server 为每个客户端(Workstation,Node 或是其他向 Chef Server 发送请求的应用)生成一对独立的公钥和私钥,将私钥返回给客户端而自己持有所有客户端的公钥。当持有私钥的客户端发送请求时,必须用自己的私钥对请求内容制作数字签名,并随同请求一起发送。Chef Server 用该客户端的公钥对请求中的数字签名进行验证,如果成功,则认为请求发送方可以信任。
这里的一个特殊情况是,当客户端第一次向 Chef Server 发送请求,即请求为自己生成公钥私钥对时,尚未持有自己的私钥。此时客户端必须通过某种方式获取 Chef Server 的默认私钥,通常命名为 Chef-validator.pem。客户端必须用这一私钥执行上述签名过程,否则将无法建立与 Chef Server 的信任。
认证过程如下图所示,
图 1 Chef 认证流程
在上图的第四步,数字签名的必须包含如清单 3 所示的内容。签名的值被放到一个特定的 HTTP Header 'X-Ops-Authorization-N'里发送。这里 N 表示这个 header 可以出现多次,每次出现时它的值不能超过 60 个字符。一个典型的带有签名的客户端请求如清单 4 所示。
这里我们可以看到,被用作数字签名的属性,在请求中再次出现了,包括 HTTP Method,X-Ops-Content-Hash,X-Ops-Timestamp,X-Ops-UserId。而 Hash Path 可以通过计算请求路径直接得到。这样 Chef Server 就可以用 X-Ops-Sign 中指定的算法,这里为 SHA1,重新执行哈希过程以验证请求内容是否被第三方篡改过。
清单 3.Chef 所要求的数字签名内容
Method:HTTP_METHODHashed Path:HASHED_PATHX-Ops-Content-Hash:HASHED_BODYX-Ops-Timestamp:TIMEX-Ops-UserId:USERID
清单 4.带有数字签名的请求范例
GET /organizations/organization_name/nodes HTTP/1.1 Accept: application/json Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3 X-Ops-Sign: algorithm=sha1;version=1.0; X-Ops-Userid: user_id X-Ops-Timestamp: 2013-03-12T17:13:28Z X-Ops-Content-Hash: 2jmj7l5rfasfgSw0ygaVb/vlWAghYkK/YBwk= X-Ops-Authorization-1: BE3NnHeh5yFTiT3ifuwLSPCCYasdfXaRN5oZb4c6hbW0aefI X-Ops-Authorization-2: sL4j1qtEZzi/2WeF67UuytdsdfgbOc5CjgECQwqrym9gCUON X-Ops-Authorization-3: yf0p7PrLRCNasdfaHhQ2LWoE/+kTcu0dkasdfvaTghfCDC57 X-Ops-Authorization-4: 155i+ZlthfasfhbfrtukusbIUGBKUYFjhbvcds3k0i0gqs+V X-Ops-Authorization-5: /sLcR7JjQky7sdafIHNEEBQrISktNCDGfFI9o6hbFIayFBx3 X-Ops-Authorization-6: nodilAGMb166@haC/fttwlWQ2N1LasdqqGomRedtyhSqXA== Host: api.opscode.com:443 X-Chef-Version: 11.4.0User-Agent: Chef Knife/11.4.0 (ruby-1.9.2-p320; ohai-6.16.0; x86_64-darwin11.3.0; +http://opscode.com)
这一认证过程同样发生在用户使用 Knife 命令行或者 Chef 管理页面的时候,只是这些情况下用户无需关心认证的细节。
认证完成后,客户端就可以向 Chef Server 请求对各种 Chef 资源的访问和管理权。以 Node 这一资源为例,Chef Server 提供了如下 API,分别用于获取所有 Nodes,以及对单个 Node 的创建、获取、修改和删除操作,如表 1 所示。
表 1 Chef Server 提供的 Node 资源 REST API
HTTP METHOD URL REQUEST BODY RESPONSE BODY GET /nodes {
"latte": "http://localhost:4000/nodes/latte"
} POST /nodes {
"name": "latte",
"chef_type": "node",
"json_class": "Chef::Node",
"attributes": {
"hardware_type": "laptop"
},
"overrides": {},
"defaults": {},
"run_list": [ "recipe[unicorn]" ]
} {
"uri": "http://localhost:4000/nodes/latte"
} DELETE /nodes/NAME {
"overrides": {},
"name": "latte",
"chef_type": "node",
"json_class": "Chef::Node",
"attributes": {
"hardware_type": "laptop"
},
"run_list": [
"recipe[apache2]"
],
"defaults": {}
} GET /nodes/NAME {
"name": "node_name",
"chef_environment": "_default",
"run_list": [
"recipe[recipe_name]"
]
"json_class": "Chef::Node",
"chef_type": "node",
"automatic": { ... },
"normal": { "tags": [ ] },
"default": { },
"override": { }
} PUT /nodes/NAME {
"overrides": {},
"name": "latte",
"chef_type": "node",
"json_class": "Chef::Node",
"attributes": {
"hardware_type": "laptop"
},
"run_list": [
'recipe[cookbook_name::recipe_name],
role[role_name]'
],
"defaults": {}
} Response codes:
200 OK
401 Unauthorized
403 Forbidden
404 Not Found
413 Request entity too large
通过调用 Chef 的 REST API,就可以完成对 Chef 资源的管理。
调用 Chef 命令
为了实现上层系统调用 Chef 的完全自动化,有时候需要自动化 Chef Client 的配置。将一个非 Chef Client 的普通机器自动注册成 Chef Client,能提高整个系统的自动化程度。Chef 的 REST API 没有提供注册 Chef Client 的功能,而 Chef 的 bootstrap 命令是用来完成这个工作的。所以上层系统需要使用某些机制(如使用 JSch)来在 Chef Workstation 上运行 bootstrap 命令。
通常我们用如下命令来将一个普通机器注册成 Chef Node:
knife bootstrap client_IP -x username -P password
如果客户端已经安装了 chef-client 软件,此命令会直接将这个客户端注册成一个 Chef Node;如果客户端没有安装 chef-client 软件,此命令会试图从网络上直接下载安装包去安装 chef-client 然后注册。对于没有外部网络连接的客户端,又没有安装 chef-client 软件,我们可以自定义 bootstrap 所用的模板,让其不从网络下载 chef-client,而直接从本地服务器下载 chef-client 进行安装(前提是配置一个本地服务器如 HTTP 服务器,将 chef-client 软件预先放到服务器上)。如果是默认安装,bootstrap 所用的模板位于 Workstation 的/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.10.4/lib/chef/knife/bootstrap/目录下。我们可以在此目录下新建一个模板,命名为 ubuntu12.04-gems-mine,在原有的 ubuntu12.04-gems 模板基础上进行修改,如将安装 chef-client 软件的部分自定义为清单 5 所示。
清单 5.自定义 bootstrap 模板
if [ ! -f /usr/bin/chef-client ]; then mkdir -p /tmp/chef-installer cd /tmp/chef-installer #Copy chef client rpm from HTTP file server wget http://http_server_ip/chef/chef-11.12.2-1.el6.x86_64.rpm rpm -ivh chef-*.rpm rm -rf /tmp/chef-installerfi
然后我们使用自定义的模板来运行 bootstrap 命令:
# knife bootstrap client_ip -x username -P password –d ubuntu12.04-gems-mine
这样在 bootstrap 一个普通的没有安装 chef-client 的机器时,Chef 就会从本地的服务器上下载 chef-client 软件(无需外部网络连接),安装在客户机上,然后注册成 Chef Node。
可见,对于没有提供 Chef REST API 的一些特殊 Chef 功能,可以通过调用 Chef 的命令行来完成。
Chef 的异常处理机制
Chef Client 运行完相关配置后,运行结果是成功还是失败,成功或者失败之后怎么处理,这些在集成 Chef 的系统中非常重要。一方面,上层系统需要监控 Chef 的运行状态;另外一方面,对于 Chef 的运行结果,上层系统一般需要做一些处理。为此,Chef 提供了两种类型的 Handler 来分别处理失败和成功的运行结果:Exception Handler 和 Report Handler。Chef 提供一个基础的 chef_handler 资源,我们可以自定义自己的 Handler 来支持业务需求。自定义 Handler 需要继承 Chef 提供的基础 Handler 类。下面我们自定义一个调用某业务 REST API 的 Handler 名为"CallRestAPI":
清单 6.自定义 Chef Handler require 'chef/handler'
require 'net/http'class CallRestAPI < Chef::Handler def initialize(config = {}) @uri = config[:_uri] @username = config[:_username] @password = config[:_password] @ossreqid = config[:_ossrequestid] end def report if run_status.success? status = "Success" extra = "" else status = "Failed" extra = "#{run_status.exception}" end uri = @uriusername = @usernamepassword = @passwordossreqid = @ossreqid begin uri = URI(uri) http = Net::HTTP.new(uri.host, uri.port) url = "/api/auth" data = "{\"username\":\"#{username}\",\"password\":\"#{password}\"}" header = {'Content-Type' => 'application/json'} resp = http.post(url, data, header) mytoken = resp.body url = "/api/requests?requestId=#{ossreqid}&status=#{status}&message=#{extra}" header = {'
Cookie' => " mytoken =#{mytoken}"} resp = http.send_request('PUT', url, nil, header) rescue => e Chef::Log.error("Error call rest api: #{e}")end endend
在这个自定义的 Handler 中,首先初始化传来的参数。然后根据 Chef 内置的变量(run_status)来判断运行结果是成功还是失败,如果失败,还可以得到失败的异常消息。接着就是调用具体业务的 REST API 将此结果返回。对于这样一个自定义的 Handler,我们可以将其作为一个文件放到一个 Cookbook 的 files 的 default 目录下(假设命名为 chef-handler-mine.rb),然后在此 Cookbook 的 Recipe 中启动这个 Handler。启动示例如下:
清单 7.启动(调用)自定义 Chef Handler include_recipe "chef_handler"
cookbook_file "#{node['chef_handler']['handler_path']}/chef-handler-mine.rb" do source "chef-handler-mine.rb" mode "0755"end.run_action(:create)chef_handler "CallRestAPI " do source "#{node['chef_handler']['handler_path']}/chef-handler-mine.rb" arguments [ :_uri => node['uri'], :_username => node['username'], :_password => node['password'], :_ossrequestid => node['ossrequest_requestid'], ] supports :report => true, :exception => true action :enableend.run_action(:enable)
在清单 7 中,首先将自定义的 Handler 传到 Chef Client 端,然后启动这个 Handler。启动之后,当此 Recipe 运行完成之后,无论成功失败(因为将 report 和 exception 两者都设置成了 true),都会自动调用这个自定义 Handler,将结果返回给上层业务系统。
如果多个 Cookbook 需要相同的 Handler 机制,我们可以将这样的 Handler 抽取出来写成一个单独的 Cookbook,比如,创建一个名为 myhandler 的 Cookbook,然后将自定义的"CallRestAPI"Hander 放到 files 的 default 目录下,再将清单 7 中的代码写到 myhandler 的 default 的 Recipe 中。在其他需要调用这个 Handler 的 Cookbook 里,只需要加上 include_recipe "myhandler"就可以了。
结束语
本文介绍了 Chef 环境的安装与使用方法,主要是根据实际经验来介绍的,文中更多的是举例和成功实践。如果要全面详细的了解 Chef 的各个组件如何配合工作,Chef 提供了哪些内置的资源方便使用者开发 Cookbook,可以参考 Chef 的官网。在当前大势所趋的云环境中,自动化部署也是其中一个重要工作,对于虚机的部署可以调用相关的虚拟化平台接口,而对于虚机的配置,以及软件和应用的自动化部署,就能用到 Chef 这个自动化工具了,而事实也证明,Chef 在当下正变得越来越流行,已被很多企业级云平台采用,相信它会越来越广泛地应用到自动化领域中。