Chef 基本流程
工具介绍
Chef 和 Chef Solo
Chef 是一个自动化工具,用 Opscode 开发,使用 Ruby DSL 以一种可重复使用的格式表示配置服务器所需的命令
Chef 经常运行在服务器集群的枢纽,统筹安排其他服务器的配置
可以在单机模式下使用 Chef (Chef Solo)
在这种模式下,我们在本地开发平台上定义服务器的各种角色和设置,然后根据需要手动应用这些设置
如果项目不断扩大也不用担心,使用 Chef Solo 制定的自动化配置大多数都可以在 Chef 中使用
Knife 和 Knife Solo
Knife 是一个命令行工具,为本地开发环境中的 Chef 仓库和远程服务器之间提供交互接口
一般情况下,远程服务器必须是主 Chef 服务器。不过 Knife Solo 允许在单机模式下使用 Chef, 可以直接和需要配置的服务器交互
Berkshelf
在 Chef 中,安装各组件所需的命令成为「配方」
Berkshelf 就像是配方的 Bundler
我们使用 Berkshelf 获取配置服务器所需的 Chef 配方(特定的版本)
Chef 术语介绍
-
配方 (recipe)
定义安装单个组件(例如 Ruby, mysql-server, Monit 等)所需的命令
-
食谱 (cookbook)
相关的配方集合,例如,mysql 食谱中包含 mysql-server 配方和 mysql-client 配方
-
节点 (node)
要配置的远程服务器
-
角色 (role)
一系列配方的组合,应用在节点上
例如,Postgres Server 角色可能会包含安装 postgres-server 的配方,安装和设置防火墙的操作,以及安装合适的服务器监控工具的操作
-
数据包 (data bag)
配方所用的元数据,保存为 JSON 格式文件。例如,要创建的用户列表,以及相应的公钥
-
Chef 仓库 (chef repository)
一系列节点和角色定义
创建 Chef Solo 仓库
-
新建文件夹
mkdir chef_repo cd chef_repo
-
安装工具
-
初始化 bundler
bundle init
-
在刚生成的 Gemfile 文件中写入如下内容:
source 'https://rubygems.org' gem 'knife-solo', '0.3.0' gem 'chef', '~> 11.10.0' gem 'chef-zero', '1.7.2' gem 'berkshelf', '~> 2.0.14'
-
然后运行如下命令来安装
bundle install
-
初始化仓库
使用 bundle exec 确保使用的 Gemfile 中定义的 gem 版本来执行命令
bundle exec knife solo init .
生成若干文件,结构如下:
chef_repo
├── .chef
│ └── knife.rb
├── .gitignore
├── Berksfile
├── Gemfile
├── Gemfile.lock
├── cookbooks
│ └── .gitkeep
├── data_bags
│ └── .gitkeep
├── environments
│ └── .gitkeep
├── nodes
│ └── .gitkeep
├── roles
│ └── .gitkeep
└── site-cookbooks
└── .gitkeep
下面来说说各个文件及文件夹的作用
knife.rb
Knife 是个命令行工具,可以和远程服务器上的 Chef 交互
Knife Solo 添加了额外的命令,可以在本地开发设备上直接和要配置的服务器交互(而不用通过 Chef 中央服务器中转)
knife.rb 中包含针对我们这个仓库的 Knife 设置。这个文件在使用 Chef 中央服务器时更有用,并不符合我们使用的单机模式,但却是 Knife 的主要设置(也是唯一的设置文件)
Knife 的全部设置选项可以在 http://docs.opscode.com/config_rb_knife.html 中查看
目前,我们要设置的重要选项如下:
-
cookbook_path
这个选项是个数组,包含很多相对于仓库根目录的路径,指定角色中使用的食谱和节点定义的存储位置 -
node_path
相对仓库根目录的路径,指定节点定义存储的位置 -
role_path
相对仓库根目录的路径,指定角色定义存储的位置 -
data_bag_path
相对仓库根目录的路径,指定数据包的存储位置
一般情况下,都可以直接使用默认值
Berksfile
在 Berkfile 中定义 Chef 仓库要使用的食谱,以及各自的版本。然后执行 berks install
命令安装这些食谱。如果食谱在 metadata.rb 文件用 depends 定义了依赖食谱,Berkshelf 会负责安装这些依赖食谱
和 Bundler 一样,Berkshelf 也会生成一个 Berksfile.lock 文件,写入食谱及相应的版本号
cookbooks
cookbooks 文件夹用来存放使用 Berkshelf 安装的他人编写的食谱
如想存放自己编写的食谱,应放在 site-cookbooks 文件夹
千万别在 cookbooks 文件夹中存放没有使用 Berkshel 来管理的食谱,因为,每次运行 knife solo 后,cookbooks 文件夹中的内容都会被抹去重新写入
data_bags
如果食谱中要用到大量数据,就可以将数据存放在这里
environments
对应现实中的工作流,比如说生产环境,准备环境,测试环境,开发环境
nodes
存放节点文件
roles
存放角色文件
site-cookbooks
用来存放自己编写的食谱
食谱介绍
以 redis-tlq 食谱为例:https://github.com/TalkingQuickly/redis-tlq
将其作为自己编写的食谱放到 site-cookbooks
文件夹下
文件结构如下:
redis-tlq
├── attributes
│ └── default.rb
├── metadata.rb
├── recipes
│ └── default.rb
└── templates
└── default
├── redis-server.erb
└── redis.conf.erb
下面是各个文件及文件夹的说明
metadata.rb
元数据文件中详细说明了这个食谱的作用,编写人,依赖件以及版本号
metadata.rb:
# 食谱的名称
name "redis-tlq"
maintainer "Ben Dixon"
maintainer_email "[email protected]"
description "Installs redis from rwky's ppa"
# 食谱的版本号
version "0.0.6"
# 配方的名称
recipe "redis-tlq", "Installs redis"
# 指定这个食谱支持的操作系统
# 这个设置很重要,因为我们这个简单的食谱只支持 Ubuntu, 会用到一些只有 Ubuntu 才支持的命令
supports "ubuntu"
使用 Berkshelf 时,version 和 recipe 这两行是关键的设置,Berkshelf 会查看 metadata.rb 文件,确认要使用的食谱中是否有匹配的配方
还有一个选项 depends 这里没提到,用来指定要依赖的其他食谱
recipes/default.rb
Chef 食谱必须包含一个名为 default 的配方
如果在节点或角色定义中使用到了某个食谱,而没有指定要用哪个配方,就会使用这个默认配方
cookbooks/redis-tlq/recipes/default.rb:
# use redis from ppa rather than the one
# available in the package manager. rwky
# builds the stable version and is kept
# consistently up to date. We need python-software-properties
# for add-apt-repository to work
# Chef 首先会使用操作系统的包管理工具检查是否安装了 python-software-properties 代码包
# 如果没有,那就安装
package 'python-software-properties'
# 告诉 Chef, 其中包含的命令是在 bash 中执行的
# 随后的文本会在配方执行时显示出来,让用户知道配方正在做什么
bash 'adding stable redis ppa' do
# 以 root 这个用户身份去执行
user 'root'
code <<-EOC
add-apt-repository ppa:chris-lea/redis-server
apt-get update
EOC
end
package 'redis-server'
# 上述操作只是安装了 Redis, 我们还要对其进行一些自定义设置,
# 再添加初始化脚本用于启动、停止 Redis 服务器,这就轮到 template 出场了
#
# 这段代码的作用如下:
# 1. 在 redis-tlq/template/default 文件夹中寻找文件 redis.conf.erb
# 2. 解析这个文件中的所有 erb 代码,然后把文件复制到节点上,存放位置为 /etc/redis/redis.conf
# 3. 把这个文件的拥有者设为 root 用户组的 root, 权限为 0644
template "/etc/redis/redis.conf" do
owner "root"
group "root"
mode "0644"
source "redis.conf.erb"
notifies :run, "execute[restart-redis]", :immediately
end
# ...
模板文件夹的说明
templates 文件夹中的子文件夹对应的是服务器上所安装的操作系统
如果使用的是 Ubuntu 12.04, 上面的 template 代码块就会在下面的路径中按顺序寻找 redis.conf.erb 文件:
templates/ubuntu-12.04/redis.conf.erb
templates/ubuntu/redis.conf.erb
remplates/default/redis.conf.erb
如果要编写复杂的食谱,照应多种操作系统,这种处理方式的强大就体现出来了
目前,我们仅仅使用 default 文件夹
属性
配方可以使用属性 (attribute) 进行定制。属性可以在多个地方设定,包括:
- 节点定义
- 角色定义
- 食谱默认值
一般情况下,在食谱中会为属性指定默认值,如果需要,可以覆盖默认值
在 templates/default/redis.conf.erb
文件中有这么一段代码:
<% unless node[:redis] && node[:redis][:dont_bind] %>
bind 127.0.0.1
<% end %>
如果在节点中有如下代码:
{
"redis": {
"dont_bind": true
}
}
那么就执行 bind 127.0.0.1
这段代码
食谱属性的默认值
在食谱中可以为属性定义默认值。这往往也是推荐的做法,大多数用户只要做少许改动就可使用
属性的默认值在 attributes/default.rb
文件中定义:
# whethere to prevent redis from binding to 127.0.0.1
default[:redis][:dont_bind] = false
上面代码的作用是:
会把 false
应用到食谱 node[:redis][:dont_bind]
,除非默认值被覆盖了
关于属性的更多说明,请阅读 OpsCode 网站上的文档:
http://docs.opscode.com/essentials_cookbook_attribute_files.html
角色介绍
新建角色文件
touch roles/redis-server.json
写入如下内容:
{
"name": "redis-server",
"description": "Redis server",
"default_attributes": {
"redis": {
"dont_bind": false
}
},
"run_list": [
"recipe[redis-tlq]",
"recipe[monit-tlq]",
"recipe[monit_configs-tlq::redis-server]"
],
"json_class": "Chef::Role"
}
代码解释如下:
-
name
指定角色的名称
当 Chef 在运行列表中见到rolo[redis-server]
时,就会寻找名为 redis-server 的角色。寻找的范围是 knife.rb 中role_path
对应文件夹中所有的 JSON 文件 -
description
描述这个角色的作用
-
default_attributes
用来设置默认属性
-
run_list
是一个数组,Chef 会依次安装执行列出的配方和角色
角色使用如下格式:
role[role_name]
配方使用如下格式:
recipe[cookbook_name::recipe]
-
“json_class”: “Chef::Role”
如果使用 JSON 格式,那么必须加上这一行
如果使用 Ruby 格式,则不用加
因为使用到了 2 个额外的食谱 monit-tlq 和 monit_configs-tlq
前者用于监控系统的各种参数,如果进程崩溃或者超出了预设的参数,Monit 会重启该进程
后者是针对系统组件的 Monit 设置
所以我们需要在仓库根目录的 Berksfile
文件中加入如下内容:
cookbook 'monit-tlq', github: 'TalkingQuickly/monit-tlq', branch: 'master'
cookbook 'monit_configs-tlq', github: 'TalkingQuickly/monit_configs-tlq', branch: 'master'
配置远程服务器
-
把公钥复制到远程服务器
ssh-copy-id root@yourserverip
-
在远程服务器上安装 Chef, 在本地仓库根目录执行如下命令:
bundle exec knife solo prepare root@yourserverip
若使用 Vagrant 可以用
vagrant ssh-config
命令查看 ssh 配置,然后执行如下类似命令:bundle exec knife solo prepare [email protected] -p 2222 -i /Users/mwum/.vagrant.d/insecure_private_key
安装时间稍长,耐心等待吧
上述命令的作用如下:
- 在 nodes 文件夹中新建文件 yourserverip.json
- 通过 SSH 登入远程服务器
- 检测操作系统的类型
- 安装相应的 Chef 版本
可能会遇到的问题
如果不确定是什么问题,可以在安装 Chef 的命令加上 -VV
选项来查看 DEBUG 信息
-
远程服务器的网络有问题,需要设置代理
-
用 ssh 登录远程服务器
-
在 ~/.bashrc 中加入如下内容:
export http_proxy="http://your-proxy.company.org:80" export https_proxy="http://your-proxy.company.org:80" export HTTP_PROXY="http://your-proxy.company.org:80" export HTTPS_PROXY="http://your-proxy.company.org:80"
-
退出远程服务器,重新安装 Chef
-
应用配方
-
配置节点文件
nodes/yourserverip.json
, 加入如下内容:{ "redis": { "dont_bind": false }, "run_list": [ "role[redis-server]" ] }
代码上半段是属性定义,
run_list
的作用和角色文件中的一样 -
应用配方,在仓库根目录下执行如下命令:
bundle exec knife solo cook root@yourserverip
这个命令的作用如下:
- 根据 Berksfile 文件通过 Berkshelf 下载食谱(相当于此命令 `berks vendor cookbookss/)
- 通过 SSH 登入远程服务器
- 把本地的配方文件复制到远程服务器
- 应用配方,并显示输出结果
如果要经常重用某个节点定义,可以将其另存为,例如 nodes/side_project_server.json, 然后执行如下命令:
bundle exec knife solo cook root@yourserverip nodes/side_project_server.json
参考图书:
《Rails 程序部署之道》