模板管理、清单管理、并行方式

模板管理、清单管理、并行方式

一、模板管理

1. ansible template作用

在实际的工作中由于每台服务器的环境配置都可能不同,但是往往很多服务的配置文件都需要根据服务器环境进行不同的配置,比如nginx最大进程数,redis最大内存等。为了解决这个问题可以使用ansible的template模块,该模块和copy模块作用基本一样,都是把管理端的文件复制到客户端主机上,但是区别在于template模块可以通过变量来获取配置值,支持多种判断、循环、逻辑运算等,而copy只能原封不动的把文件内容复制过去。需要注意的是template只能在playbook中运行,不能使用命令行方式。

2. ansible template使用方法

多数情况下都会建立一个templates目录并和playbook同级,这样playbook可以直接引用和寻找这个模板文件,如果在别的路径需要单独指定。模板文件后缀名为.j2

2.1 构建jinja2模板

在jinja2中,存在三种语法:

  1. 控制结构 {% %}
  2. 变量取值 {{ }}

jinja2模板中使用{{ }}语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染时,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的python数``据类型比如列表、字段、对象等

  1. 注释 {# #}

注意:包含jinja2模板的文件不需要有任何特定的文件扩展名(.j2)。但是提供此类文件扩展名会让你更容易记住它是模板文件

# {{ ansible_managed }}

Port {{ ssh_port }}
ListenAddress {{ ansible_facts['default_ipv4']['address'] }}

2.2 部署jinja2模板

jinja2模板是功能强大的工具,可用于自定义要在受管主机上部署的配置文件。创建了适用于配置文件的jinja2模板后,它可以通过template模块部署到受管主机上,该模块支持将控制节点中的本地文件转移到受管主机。

tasks:
  - name: moban
    template:
      src: /tmp/test.j2
      dest: /tmp/test.txt

template模块还允指定已部署文件的所有者、组、权限和SELINUX上下文,就像file模块一样。它也可以取用validate选项运行任意命令,在将文件复制到位之前检查该文件的语法是否正确

2.3 使用循环

jinja2使用for语句来提供循环功能。在下例中,user变量替换为users变量中包含的所有值,一行一个值

{% for user in users %}
    {{ user }}
{% endfor %}

以下示例模板使用for语句逐一运行users变量中的所有值,将myuser替换为各个值,但值为root时除外

{# for statement #}
{% for myuser in users if not myuser == "root" %}
User number {{ loop.index }} - {{ myuser }}
{% endfor %}

loop.index变量扩展至循环当前所处的索引号。它在循环第一次执行时值为1,每一次迭代递增1

2.4 使用条件句

jinja2使用if语句来提供条件控制。如果满足某些条件,这允许用户在已部署的文件中放置一行

在以下示例中,仅当finished变量的值为true时,才可将reult变量的值放入已部署的文件

{% if finished %}
{{ result }}
{% endif %}

注意:在Ansible模板中我们可以使用jinja2循环和条件,但不能在Ansible Playbook中使用

二、清单管理

1. 利用主机模式选择主机

1.1 引用清单主机

主机模式用于指定要作为play或临时命令的目标的主机。在最简单的形式中,清单中受管主机或主机组的名称就是指定该主机或主机组的主机模式

在play中,hosts指定要针对其运行play的受管主机。对于临时命令,以命令行参数形式将主机模式提供给ansible命令

web.com
date.com

[lab]
labhost1.com
labhost2.com

[test]
test1.com
test2.com

[new]
192.168.100.110
192.168.100.120

1.2 受管主机

最基本的主机模式是单一受管主机名称列在清单中。这将指定该主机是清单中ansible命令要执行操作的唯一主机。

在该playbook运行时,第一个gathering facts任务应在与主机模式匹配的所有受管主机上运行。此任务旗舰的故障可能导致受管主机从play中移除

如果清单中明确列出了IP地址,而不是主机名, 则可以将其用作主机模式。如果IP地址未列在清单中,我们就无法用它来指定主机,即使该IP地址会在DNS中解析到这个主机名

---
- hosts: 192.168.100.110


[root@localhost ~]# ansible-playbook playbook.yml 
PLAY [192.168.100.110] ***************************************************************

TASK [Gathering Facts] **************************************************************
ok: [192.168.100.110]

PLAY RECAP **************************************************************************
192.168.100.110             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

注意:

在清单中通过IP地址引用受管主机存在一个问题,那就是难以记住play或临时命令所针对的主机使用了哪个IP地址。但是,如果没有可解析的主机名,我们可能必须先按IP地址指定主机进行连接

1.3 使用组指定主机

当组名称用作主机模式时,它指定ansible将对属于该组的成员的主机执行操作

---
- hosts: test

记住,有一个名为all的特别组,它匹配清单中的所有受管主机

---
- hosts: all

还有一个名为ungrouped的特别组,它包括清单中不属于任何其他组的所有受管主机

---
- hosts: ungrouped

1.4 使用通配符匹配多个主机

若要达成与all主机模式相同的目标,另一种方法是使用“ * ”通配符,它将匹配任意字符串。如果主机模式只是带引号的星号,则清单中的所有主机都将匹配。

---
- hosts: '*'

重要

一些在主机模式中使用的字符对shell也有意义。通过ansible使用主机模式从命令行运行临时命令时,这可能会有问题。建议大家在命令行中使用单引号括起使用的主机模式,防止它们被shell意外扩展。

类似的,如果在ansible playbook中使用了任何特殊通配符或列表字符,必须将主机模式放在单引号里,确保能够正确解析主机模式

---
- hosts: '!test1.com,development'

也可以使用“ * ”字符匹配包含特定子字符串的受管主机或组

例如,以下通配符主机模式匹配以.com结尾的所有清单名称

---
- hosts: '*.com'

重要

通配符主机模式匹配所有清单名称、主机和主机组。它们不区别名称是DNS名、IP地址还是组,这可能会导致一些意外的匹配

1.5 列表

可以通过逻辑列表来引用清单中的多个条目。主机模式的逗号分隔列表匹配符合任何这些主机模式的所有主机

如果提供受管主机的逗号分隔列表,则所有这些受管主机都将是目标

---
- hosts: labhost1.com,test1.com,192.168.100.110

如果提供组的逗号分隔列表,则属于任何这些组的所有主机都将是目标

---
- hosts: lab,test

也可以使用冒号:来替代逗号。不过,逗号是首选的分隔符,特别是将IPv6地址用作受管主机名称时。

如果列表中的某一项以与符号&开头,则主机必须与该项匹配才能匹配主机模式。它的工作方式类似于逻辑AND

例如,根据我们的示例清单,以下主机模式将匹配lab组中同时也属于test组的计算机

---
- hosts: lab,&test

我们也可以通过主机模式&lab,test或test,&lab指定test组中的计算机只有在同时也属于lab组时才匹配

通过在主机模式的前面使用表示从列表中排除匹配某一模式的主机。它的工作方式类似于逻辑NOT

根据示例清单,以下示例匹配test组中定义的所有主机,但lab2.com除外

---
- hosts: test,!lab2.com

也可以使用模式!lab2.com,test来获得相同的结果

最后一个示例演示了使用匹配测试清单中的所有主机的主机模式,test组中的受管主机除外

---
- hosts: all,!test

三、并行方式

3.1 使用分叉在ansible中配置并行

当Ansible处理playbook时,会按顺序运行每个play。确定play的主机列表之后,Ansible将按顺序运行每个任务。通常,所有主机必须在任何主机在play中启动下一个任务之前成功完成任务。

理论上,Ansible可以同时连接到play中的所有主机以执行每项任务。这非常适用于小型主机列表。但如果该play以数百台主机为目标,则可能会给控制节点带来沉重负担。

Ansible所进行的最大同时连接数由Ansible配置文件中的forks参数控制。默认情况下设为5,这可通过以下方式之一来验证。

[root@localhost ~]# grep forks /etc/ansible/ansible.cfg 
#forks          = 5

[root@localhost ~]# ansible-config dump|grep -i forks
DEFAULT_FORKS(default) = 5

[root@localhost ~]# ansible-config list|grep -i forks
DEFAULT_FORKS:
  description: Maximum number of forks Ansible will use to execute tasks on target
  - {name: ANSIBLE_FORKS}
  - {key: forks, section: defaults}
  name: Number of task forks

例如,假设Ansible控制节点配置了5个forks的默认值,并且play具有10个受管主机。Ansible将在前5个受管主机上执行play中的第一个任务,然后在其他5个受管主机上对第一个任务执行第二轮。在所有受管主机上执行第一个任务后,Ansible将继续一次在5受管主机的组中的所有受管主机上执行下一个任务。Ansible将依次对每个任务执行此操作,直到play结束。

forks的默认值设置得非常保守。如果你的控制节点正在管理Linux主机,则大多数任务将在受管主机上运行,并且控制节点的负载较少。在这种情况下,通常可以将forks的值设置得更高,可能接近100,然后性能就会提高。

如果playbook在控制节点上运行很多代码,则应明智地提高forks限值。如果使用Ansible管理网络路由器和交换机,则大多数模块在控制节点上运行而不是在网络设备上运行。由于这会增加控制节点上的负载,因此其支持forks数量增加的能力将显著低于仅管理Linux主机的控制节点。

可以从命令行覆盖Ansible配置文件中forks的默认设置。ansible和ansible-playbook命令均提供-f或–forks选项以指定要使用的forks数量。

3.2 管理滚动更新

通常,当Ansible运行play时,它会确保所有受管主机在启动任何主机进行下一个任务之前已完成每个任务。在所有受管主机完成所有任务后,将运行任何通知的处理程序。

但是,在所有主机上运行所有任务可能会导致意外行为。例如,如果play更新负载均衡Web服务器集群,则可能需要在进行更新时让每个Web服务器停止服务。如果所有服务器都在同一个play中更新,则它们可能全部同时停止服务。

避免此问题的一种方法是使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。

在下面的示例中,Ansible一次在两个受管主机上执行play,直至所有受管主机都已更新。Ansible首先在前两个受管主机上执行play中的任务。如果这两个主机中的任何一个或两个都通知了处理程序,则Ansible将根据这两个主机的需要运行处理程序。在这两个受管主机上执行完play时,Ansible会在接下来的两个受管主机上重复该过程。Ansible继续以这种方式运行play,直到所有受管主机都已更新。

---
- name: gundonggengxin
  hosts: test
  serial: 2
  tasks:
  - name: gengxin httpd
    yum:
      name: httpd
      state: latest
    notify: restart apache
    
  handlers:
  - name: restart apache
    service:
      name: httpd
      state: restarted

假设上一示例中的webservers组包含5个Web服务器,它们位于负载均衡器后面。将serial参数设置为2后,play一次将运行两台Web服务器。因此,5台Web服务器中的大多数服务器将始终可用。

相反,如果不使用serial关键字,将同时在5台Web服务器上执行play和生成的处理程序。这可能会导致服务中断,因为Web服务将在所有Web服务器上同时重新启动。

重要

出于某些目的,每批主机算作在主机子集上运行的完整play。这意味着,如果整个批处理失败,play就会失败,这将导致整个playbook运行失败。

在设置了serial: 2的上一个场景中,如果出现问题并且处理的前2个主机的play失败,则playbook将中止,其余3个主机将不会通过play运行。这是一个有用的功能,因为只有一部分服务器会不可用,使服务降级而不是中断。

serial关键字也可以指定为百分比。此百分比应用于play中的主机总数,以确定滚动更新批处理大小。无论百分比为何,每一工序的主机数始终为1或以上。