Ansible最佳实践,


原文在这里哈!

这篇最佳实践是官网给出的一个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

如果你用的是云服务提供商,你不应该用静态文件管理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”文件中加载变量。

最外层的playbook根据角色分类

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

部署VS配置结构

上述设置模拟了一个典型的配置布局。进行多层部署时,在层与层之间需要一些其他的playbook来展开应用。在本例中,‘site.yml’ 可以通过如‘deploy_exampledotcom.yml’ 的playbook来进行扩充,但是基本的概念还是可以直接使用的。

可以把playbook看成一种运动。你不需要在所有的场合都使用一种play来应对需要的设置。你可以在不同的需求和场合使用不同的play。

Ansible允许使用同样的工具进行部署和配置,所以你可能需要重复利用分组,只要把OS配置保存在aap部署的单独文件中。

模拟环境VS生产环境

保持模拟环境(或测试环境)和生产环境分离的一种好的方法是在不同环境使用不同的inventory文件。这种方式可以使用-i选项来部署不同的环境。把它们放在一个文件里也可以带来惊喜哟!

生产环境应用之前,在模拟环境中测试是一个不错的选择。两种环境没必要使用一样的大小,你可以使用分组变量来控制环境的区别。

无缝更新应用

理解一下‘serial’ 关键字,如果需要更新一堆web服务器,可以使用‘serial’来控制一次更新多少台机器。

详情查看Delegation, Rolling Updates, and Local Actions。

总是使用state参数

‘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安装ansible模块

如果一个playbook有一个对应YAML文件的”./library” 目录,这个目录可以在ansible的相应路径下自动添加模块。这个方法可以很好地把模块和playbook联系到一起。在本章开头的目录结构中,你可以找到示例。

空格和注释

鼓励使用空格将东西分隔开,鼓励使用以#开头的注释。

总是命名任务

对于一个给定的任务,有可能会省略掉’name’关键字,但是非常建议使用’name’描述任务执行的目的等。Playbook运行时,任务名称会显示出来。

保持简洁

尽可能把代码写的简洁一些。不需要一次把ansible的所有特性的都用上,只需要使用那些对你有帮助的。比如,当你使用一个外部的inventory文件时,你可能不需要一次同时使用vars, vars_files, vars_prompt, –extra-vars 。

如果感觉代码很复杂的话,尽可能的去简化它。

版本控制

建议使用版本控制工具。把你的playbook和inventory文件放在git(或者其他版本控制系统)中,进行修改时提交一下。这可以让你对于何时何原因修改了你的自动化配置代码有迹可循。

变量和vaults关键字

一般运维中,通常使用grep或者类似的工具来寻找你的ansible代码中的变量。自从vaults隐藏了这些变量,最好使用间接层。当运行一个playbook时,ansible会查找非加密文件中的变量和加密文件中的敏感变量。

最好的实践方法是,首先在group_vars/目录下创建一个以分组命名的子目录。在子目录中,创建两个名为vars 和vault的文件。在vars文件中,定义所有需要的变量,包括敏感变量。然后,把所有的敏感变量拷贝到vault文件中,给这些变量名加上vault_ 前缀。你应该把vars文件中的变量调整为与vault_开头的变量一一对应,并且确保vault文件采用了vault加密。

这个实践方法对于变量文件和vault文件的数量和命名没有任何限制。

相关内容

    暂无相关文章