原文在这里哈!
这篇最佳实践是官网给出的一个Ansible结构的示例,非常的实用,节约代码。看完后,你可能会惊诧于ansible居然这么强大?!
翻译中有一些关键字使用的是原文英文,因为不好翻,如有疑问,请戳最上的原文。翻译不当的地方,欢迎多多指教哈!
**
**
本文给出了一些关于如何充分利用Ansible和Ansible playbook的建议。
你可以在我们的ansible-examples 库中找到一些示例playbook来证明这些最佳实践。(注意:这些例子可能不会使用最新版的所有功能,但还是很有参考价值。)
以下章节展示了其中一种组织playbook内容的方法。
你应该根据自己的需求来选择使用ansible的方法,而不是生搬硬套我们的方法,所以按照自己想要的,大胆地修改方法结构吧。
你肯定会想要使用roles特性,在playbook主页上它也占了不少篇幅。详见 Playbook Roles and Include Statements。你一定要使用roles。Roles真的很棒。Roles真的很棒。Roles真的很棒。重要的话说三遍!
目录的最上一层会包含类似以下的文件目录:
production # production服务器的inventory文件
staging # staging环境使用的inventory文件
group_vars/
group1 # 在这个文件里,我们将变量分配给特殊的组
group2 # ""
host_vars/
hostname1 # 如果系统需要具体的变量,可以放在这里
hostname2 # ""
library/ # 任何自定义模块,可以放在这里(可选项)
filter_plugins/ # 任何自定义的过滤插件,可以放在这里(可选项)
site.yml # 核心playbook
webservers.yml # webserver层的playbook
dbservers.yml # dbserver层的playbook
roles/
common/ # 这一层文件夹代表一个 "role"
tasks/ #
main.yml # <-- 有需要的话,tasks文件可以包含更小的文件
handlers/ #
main.yml # <-- 触发器文件
templates/ # <-- 使用template资源时,需要用的文件
ntp.conf.j2 # <------- template文件名需要以.j2结尾
files/ #
bar.txt # <-- 使用copy相关模块时,需要用的文件
foo.sh # <-- 使用脚本资源时,需要用的脚本
vars/ #
main.yml # <-- 与所属role相关的变量
defaults/ #
main.yml # <-- 与所属role相关的优先度更低的默认变量
meta/ #
main.yml # <-- role的附属依赖
library/ # roles也可以包含自定义模块
lookup_plugins/ # 或者其他类型的插件,比如查找
webtier/ # 该层级属于webtier角色,结构和上面的“common”角色相同
monitoring/ # ""
fooapp/ # ""
另一种结构,你可以把每个inventory文件和它的group_vars/host_vars文件放在单独的目录中。如果你的group_vars / host_vars在不同环境中没有那么多相同的值,这是特别有用的。 布局可能看起来像这样:
inventories/
production/
hosts # production服务器的inventory文件
group_vars/
group1 # 在这个文件里,我们将变量分配给特殊的组
group2 # ""
host_vars/
hostname1 # 如果系统需要具体的变量,可以放在这里
hostname2 # ""
staging/
hosts # staging环境使用的inventory文件
group_vars/
group1 # 在这个文件里,我们将变量分配给特殊的组
group2 # ""
host_vars/
stagehost1 # 如果系统需要具体的变量,可以放在这里
stagehost2 # ""
library/
filter_plugins/
site.yml
webservers.yml
dbservers.yml
roles/
common/
webtier/
monitoring/
fooapp/
这种布局可以给大型开发环境提供更多灵活性,另外不同的环境的inventory变量完全分隔、互不影响Alternative。缺点是由于有很多文件,不便于维护。
如果你用的是云服务提供商,你不应该用静态文件管理inventory。详见Dynamic Inventory。
动态inventory不仅适用于云平台,如果在基础架构中需要使用另一个系统维护一个典型的系统列表,那么通常情况下,动态inventory也是一个不错的选择。
使用静态inventory的时候,经常会被问到如何区分不同类型的环境。下面的例子会给出一个不错的解决方法。类似的分组方法也适用于动态inventory(举例,如果使用“environment:production”作为 AWS标签,系统会自动发现名为“ec2_tag_environment_production”的分组)。
下面来看一个静态inventory的例子,production 文件包含了所有的production 主机。
建议在定义分组的时候,基于主机(角色)的作用,也可以基于地理布局或数据中心的位置(如果可实现的话)。
# 文件: production
[atlanta-webservers]
www-atl-1.example.com
www-atl-2.example.com
[boston-webservers]
www-bos-1.example.com
www-bos-2.example.com
[atlanta-dbservers]
db-atl-1.example.com
db-atl-2.example.com
[boston-dbservers]
db-bos-1.example.com
# 所有位置的webservers
[webservers:children]
atlanta-webservers
boston-webservers
# 所有位置的dbservers
[dbservers:children]
atlanta-dbservers
boston-dbservers
# 在亚特兰大的所有服务器
[atlanta:children]
atlanta-webservers
atlanta-dbservers
# 在波士顿的所有服务器
[boston:children]
boston-webservers
boston-dbservers
本章节沿用之前的例子。
分组对整体组织有好处,但不是所有的分组都适合。你可以给分组分配一些变量!例如,atlanta有自己的NTP服务器,因此在设定ntp.conf时,我们需要用到这些服务器。如下所示:
---
# file: group_vars/atlanta
ntp: ntp-atlanta.example.com
backup: backup-atlanta.example.com
变量不仅适用于地理位置信息!也许web服务器有一些不适用于数据库服务器的配置:
---
# file: group_vars/webservers
apacheMaxRequestsPerChild: 3000
apacheMaxClients: 900
如果需要使用默认值或始终为true的值,应该把它们放到group_vars/all文件中:
---
# file: group_vars/all
ntp: ntp-boston.example.com
backup: backup-boston.example.com
在host_vars文件中可以定义特殊的硬件变量,但除非有需要的话最好不要这样做:
---
# file: host_vars/db-bos-1.example.com
foo_agent_port: 86
bar_agent_port: 99
另外,如果使用动态的inventory资源,动态的分组也会被自动创建。例如,如果使用标签“class:webserver”,将会自动从“group_vars/ec2_tag_class_webserver”文件中加载变量。
site.yml中包含了一个定义了整个基本结构的playbook。注意使用的代码超级超级少,因为它只是包含了其他的playbook。记住,playbook只是一些任务的清单。
---
# file: site.yml
- include: webservers.yml
- include: dbservers.yml
在最外层的webservers.yml文件中,我们简单地把web主机组的配置分配给其所属的角色。而且所需要代码也是难以置信的少。例如:
---
# file: webservers.yml
- hosts: webservers
roles:
- common
- webtier
这边的想法是可以选择运行site.yml实现所有配置或者运行webservers.yml实现部分配置。后者类似于ansible的”–limit”参数,但是更加直观一些:
ansible-playbook site.yml --limit webservers
ansible-playbook webservers.yml
下面是一个任务文件示例,它可以解释角色是如何起作用。这边的普通角色(common role)只设置了NTP,但是如果需要的话可以进行更多设置:
---
# file: roles/common/tasks/main.yml
- name: be sure ntp is installed
yum: name=ntp state=installed
tags: ntp
- name: be sure ntp is configured
template: src=ntp.conf.j2 dest=/etc/ntp.conf
notify:
- restart ntpd
tags: ntp
- name: be sure ntpd is running and enabled
service: name=ntpd state=started enabled=yes
tags: ntp
下面是一个触发器文件示例。起审查作用的触发器只有在任务返回已修改成功时才会被触发,通常是在任务执行完执行。
---
# file: roles/common/handlers/main.yml
- name: restart ntpd
service: name=ntpd state=restarted
更多详细信息,请查看 Playbook Roles and Include Statements。
以上分享了基本的组织结构。
这种布局适用于哪种场景呢?很多种!如果我想重新配置所有设置,只需要:
ansible-playbook -i production site.yml
如果重新配置所有的NTP设置呢,也很简单:
ansible-playbook -i production site.yml --tags ntp
如果只重新配置web主机呢?:
ansible-playbook -i production webservers.yml
如果是在波士顿的web主机呢?:
ansible-playbook -i production webservers.yml --limit boston
如果先配置前十个主机,在配置接下来十个呢?:
ansible-playbook -i production webservers.yml --limit boston[1-10]
ansible-playbook -i production webservers.yml --limit boston[11-20]
当然使用基本的ad-hoc命令也是可以的:
ansible boston -i production -m ping
ansible boston -i production -m command -a '/sbin/reboot'
另外还有一些有用的命令(v1.1及以上):
# 如果只需要执行ntp任务,运行下面的命令可以确认即将执行的任务名称
ansible-playbook -i production webservers.yml --tags ntp --list-tasks
# 如果只在波士顿的机器执行,运行下面的命令可以确认需要执行的主机
ansible-playbook -i production webservers.yml --limit boston --list-hosts
上述设置模拟了一个典型的配置布局。进行多层部署时,在层与层之间需要一些其他的playbook来展开应用。在本例中,‘site.yml’ 可以通过如‘deploy_exampledotcom.yml’ 的playbook来进行扩充,但是基本的概念还是可以直接使用的。
可以把playbook看成一种运动。你不需要在所有的场合都使用一种play来应对需要的设置。你可以在不同的需求和场合使用不同的play。
Ansible允许使用同样的工具进行部署和配置,所以你可能需要重复利用分组,只要把OS配置保存在aap部署的单独文件中。
保持模拟环境(或测试环境)和生产环境分离的一种好的方法是在不同环境使用不同的inventory文件。这种方式可以使用-i选项来部署不同的环境。把它们放在一个文件里也可以带来惊喜哟!
生产环境应用之前,在模拟环境中测试是一个不错的选择。两种环境没必要使用一样的大小,你可以使用分组变量来控制环境的区别。
理解一下‘serial’ 关键字,如果需要更新一堆web服务器,可以使用‘serial’来控制一次更新多少台机器。
详情查看Delegation, Rolling Updates, and Local Actions。
‘state’参数对于很多模块是可选的, 无论’state = present’还是’state = absent’,都能使你的playbook变得更清楚,特别是一些模块支持额外的状态的时候。
我们有点过多的重复这个注意点,但是它值得被多次强调。一个系统可以使用多个分组。查看 Inventory 和 Patterns。在示例中,分组一直重复使用类似于webservers和dbservers的命名方式,这是一个很有用的概念。
这可以让playbook基于角色匹配对象机器,也可以使用分组变量文件来分配角色的特殊变量。
详情查看 Playbook Roles and Include Statements。
当处理一个随操作系统的不同而发生变化的变量的时候,有一个不错的办法就是使用group_by 模块。
这使得动态组的主机满足特定的条件,即使inventory文件中没有定义该组。
---
# 和所有主机通信,以便了解它们
- hosts: all
tasks:
- group_by: key=os_{{ ansible_distribution }}
# 现在只和CentOS机器通信
- hosts: os_CentOS
gather_facts: False
tasks:
- # 只在CentOS上执行的任务
将所有的系统根据操作系统分类到一个动态组中。
如果需要特定组设置,也可以使用如下方式:
---
# file: group_vars/all
asdf: 10
---
# file: group_vars/os_CentOS
asdf: 42
在上面的示例中,CentOS机器使用值为42的asdf变量,但是其他使用值为10的asdf变量。这不仅可以用于设置变量,也可以在某个特定系统使用特定角色。
或者,如果只需要变量的话:
- hosts: all
tasks:
- include_vars: "os_{{ ansible_distribution }}.yml"
- debug: var=asdf
这将会根据OS的名称引入变量。
如果一个playbook有一个对应YAML文件的”./library” 目录,这个目录可以在ansible的相应路径下自动添加模块。这个方法可以很好地把模块和playbook联系到一起。在本章开头的目录结构中,你可以找到示例。
鼓励使用空格将东西分隔开,鼓励使用以#开头的注释。
对于一个给定的任务,有可能会省略掉’name’关键字,但是非常建议使用’name’描述任务执行的目的等。Playbook运行时,任务名称会显示出来。
尽可能把代码写的简洁一些。不需要一次把ansible的所有特性的都用上,只需要使用那些对你有帮助的。比如,当你使用一个外部的inventory文件时,你可能不需要一次同时使用vars, vars_files, vars_prompt, –extra-vars 。
如果感觉代码很复杂的话,尽可能的去简化它。
建议使用版本控制工具。把你的playbook和inventory文件放在git(或者其他版本控制系统)中,进行修改时提交一下。这可以让你对于何时何原因修改了你的自动化配置代码有迹可循。
一般运维中,通常使用grep或者类似的工具来寻找你的ansible代码中的变量。自从vaults隐藏了这些变量,最好使用间接层。当运行一个playbook时,ansible会查找非加密文件中的变量和加密文件中的敏感变量。
最好的实践方法是,首先在group_vars/目录下创建一个以分组命名的子目录。在子目录中,创建两个名为vars 和vault的文件。在vars文件中,定义所有需要的变量,包括敏感变量。然后,把所有的敏感变量拷贝到vault文件中,给这些变量名加上vault_ 前缀。你应该把vars文件中的变量调整为与vault_开头的变量一一对应,并且确保vault文件采用了vault加密。
这个实践方法对于变量文件和vault文件的数量和命名没有任何限制。