文章目录
模板管理,清单管理,并行方式
1.jinja简介
Ansible将jinja2模板系统用于模板文件。Ansible还使用jinja2语法来引用playbook中的变量。
变量和逻辑表达式置于标记或分隔符之间。例如,jinja2模板将**{% EXPR %}用于表达式或逻辑(如循环),而{{ EXPR }}则用于向最终用户输出表达式或变量的结果。后一标记在呈现时将被替换为一个或多个值,对最终用户可见。使用{# COMMENT #}**语法括起不应出现在最终文件中的注释。
在下例中,第一行中含有不会包含于最终文件中的注释。第二行中引用的变量被替换为所引用的系统事实的值。
[root@centos8-1 ansible]# cat files/hosts //将本地的hosts,copy一份到当前目录,然后编写内容如下
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{# 这是你一个注释哦~~~ #}
{{ ansible_facts['default_ipv4']['address' ] }} {{ansible_facts['fqdn'] }}
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# cat zhixing.yml //play脚本的写法
---
- name:
hosts: 192.168.100.147
tasks:
- name: test
template:
src: files/hosts
dest: /etc/hosts
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# ansible-playbook zhixing.yml //执行成功
PLAY [192.168.100.147] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.100.147]
TASK [test] ********************************************************************
changed: [192.168.100.147]
PLAY RECAP *********************************************************************
192.168.100.147 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@localhost ~]# cat /etc/hosts //此时查看已经写入成功,前面是ip地址,后面是主机名。
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.147 localhost.localdomain
[root@localhost ~]#
2.构建jinja2模板
jinja2模板由多个元素组成:数据、变量和表达式。在呈现jinja2模板时,这些变量和表达式被替换为对应的值。模板中使用的变量可以在playbook的vars部分中指定。可以将受管主机的事实用作模板中的变量。
请记住,可以使用ansible system_hostname -i inventory_file -m setup命令来获取与受管主机相关的事实。
[root@centos8-1 ansible]# ansible apache -m setup
192.168.100.147 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.100.147"
],
"ansible_all_ipv6_addresses": [
"fe80::79b2:48d0:db63:ff3e"
此处内容省略。。。。。。
我们还可以ansible apache -m setup | less
下例演示了如何使用变量及Ansible从受管主机检索的事实创建**/etc/ssh/sshd_config的模板。当执行相关的playbook**时,任何事实都将被替换为所配置的受管主机中对应的值。
注意:包含jinja2模板的文件不需要有任何特定的文件扩展名(如.j2)。但是,提供此类文件扩展名会让你更容易记住它是模板文件。
3.部署jjinja2模板
jinja2模板是功能强大的工具,可用于自定义要在受管主机上部署的配置文件。创建了适用于配置文件的jinja2模板后,它可以通过template模板部署到受管主机上,该模块支持将控制节点中的本地文件转移到受管主机。
若要使用template模块,请使用下列语法。与src键关联的值指定来源jinja2模板,而与dest键关联的值指定要在目标主机上创建的文件。
[root@centos8-1 ansible]# cat zhixing.yml
---
- name:
hosts: 192.168.100.147
tasks:
- name: test
template:
src: files/hosts
dest: /etc/hosts
[root@centos8-1 ansible]#
template模块还允许指定已部署文件的所有者、组、权限和SELINUX上下文,就像file模块一样。它也可以取用validate选项运行任意命令(如visudo -c),在将文件复制到位之前检查该文件的语法是否正确。
如果还有不懂,我们可以执行ansible-doc template
[root@centos8-1 ansible]# ansible-doc template
> TEMPLATE (/usr/lib/python3.6/site-packages/>
Templates are processed by the L(Jinja2 >
language,http://jinja.pocoo.org/docs/). >
template formatting can be found in the >
Documentation,http://jinja.pocoo.org/doc>
Additional variables listed below can be>
`ansible_managed' (configurable via the >
`ansible.cfg') contains a string which c>
the template name, host, modification ti>
file and the owner uid. `template_host' >
of the template's machine. `template_uid>
id of the owner. `template_path' is the >
`template_fullpath' is the absolute path>
`template_destpath' is the path of the t>
system (added in 2.8). `template_run_dat>
the template was rendered.
4.管理模板文件
为避免系统管理员修改Ansible部署的文件,最好在模板顶部包含注释,以指示不应手动编辑该文件。
可使用ansible_managed指令中设置的"Ansible managed"字符串来执行此操作。这不是正常变量,但可以在模板中用作一个变量。ansible_managed指令在ansible.cfg文件中设置:
ansible_managed = Ansible managed
要将ansible_managed字符串包含在jinja2模板内,请使用下列语法:
{{ ansible_managed }} //注意一定要前后空格,官方格式。
4.1.控制结构
用户可以在模板文件中使用jinja2控制结构,以减少重复输入,为play中的每个主机动态输入条目,或者有条件地将文本插入到文件中。
5.使用循环
jinja2使用for语句来提供循环功能。在下例中,user变量替换为users变量中包含的所有值,一行一个值。
[root@centos8-1 ansible]# cat hosts_facts/user.yml //首先我们得写一个变量,里面有四个用户
users:
- shen
- long
- fei
- run
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2 //for语句循环
{% for user in users %}
{{ user }}
^^^^^^^^^^^^^^^^^
{% endfor %}
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# cat zhixing.yml //play写法
---
- name:
hosts: 192.168.100.147
vars_files: //在这里我们用到了变量, 得告诉它我得变量在哪里!
hosts_facts/user.yml
tasks:
- name: test
template:
src: hosts_facts/ceshi.j2 //.j2文件存放路径
dest: /tmp/abc //受管主机路径
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# ansible-playbook zhixing.yml //执行成功
PLAY [192.168.100.147] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.100.147]
TASK [test] ********************************************************************
changed: [192.168.100.147]
PLAY RECAP *********************************************************************
192.168.100.147 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@localhost tmp]# cat abc //执行结果在这里
shen
^^^^^^^^^^^^^^^^^
long
^^^^^^^^^^^^^^^^^
fei
^^^^^^^^^^^^^^^^^
run
^^^^^^^^^^^^^^^^^
[root@localhost tmp]#
以下示例模板使用for语句逐一运行users变量中的所有值,将myuser替换为各个值,但值为root时除外
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2
{% for user in users if not user == "long" %} //等于long这个用户,不执行它。
{{ user }}
^^^^^^^^^^^^^^^^^
{% endfor %}
[root@centos8-1 ansible]#
[root@localhost tmp]# cat abc //执行结果
shen
^^^^^^^^^^^^^^^^^
fei
^^^^^^^^^^^^^^^^^
run
^^^^^^^^^^^^^^^^^
[root@localhost tmp]#
或者在这里我们把not去掉,然后执行它。
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2
{% for user in users if user == "long" %} //如果等于long,执行long
{{ user }}
^^^^^^^^^^^^^^^^^
{% endfor %}
[root@localhost tmp]# cat abc //结果如下
long
^^^^^^^^^^^^^^^^^
[root@localhost tmp]#
循环次数
格式如下:
{# for statement #}
{% for myuser in users if not myuser == "root" %}
User number {{ loop.index }} - {{ myuser }}
{% endfor %}
实操
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2
{% for user in users if not user == "long" %}
user number {{ loop.index }} - {{ user }}
^^^^^^^^^^^^^^^^^
{% endfor %}
[root@centos8-1 ansible]#
[root@localhost tmp]# cat abc //结果如下
user number 1 - shen //这里得1表示执行循环得次数1次,下面以此内推。
^^^^^^^^^^^^^^^^^
user number 2 - fei
^^^^^^^^^^^^^^^^^
user number 3 - run
^^^^^^^^^^^^^^^^^
[root@localhost tmp]#
loop.index变量扩展至循环当前所处的索引号。它在循环第一次执行时值为1,每一次迭代递增1。
再如,此模板也使用了for语句,并且假定使用的清单文件中已定义了myhosts变量。此变量将包含要管理的主机的列表。使用下列for语句时,文件中将列出清单myhosts组内的所有主机。
{% for myhost in groups['myhosts'] %}
{{ myhost }}
{% endfor %}
举一个更实际的例子,用户可以使用该模板从主机事实动态生成**/etc/hosts**文件。假设playbook如下:
[root@centos8-1 ansible]# cat zhixing.yml
---
- name:
hosts: 192.168.100.147
vars_files:
hosts_facts/user.yml
tasks:
- name: test
template:
src: hosts_facts/ceshi.j2
dest: /tmp/abc
下述三行templates/hosts.j2模板从all组中的所有主机构造文件。(由于变量名称的长度,模板的中间行非常长。)它迭代组中的每个主机以获得**/etc/hosts**文件的三个事实。
格式:
{% for host in groups['all'] %}
{{ hostvars['host']['ansible_facts']['default_ipv4']['address'] }} {{ hostvars['host']['ansible_facts']['fqdn'] }} {{ hostvars['host']['ansible_facts']['hostname'] }}
{% endfor %}
6.使用条件语句
jinja2使用if语句来提供条件控制。如果满足某些条件,这允许用户在已部署的文件中放置一行。
在以下示例中,仅当finished变量的值为True时,才可将result变量的值放入已部署的文件。
格式:
{% if finished %}
{{ result }}
{% endif %}
实操:
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2
{% if users %}
{{ users }}
{% endif %}
[root@centos8-1 ansible]#
此处得playbook和上面一模一样
[root@localhost tmp]# cat abc //结果如下
['shen', 'long', 'fei', 'run']
[root@localhost tmp]#
注意,在Ansible模板中我们可以使用jinja2循环和条件,但不能在Ansible Playbook中使用。
7.变量过滤器
jinja2提供了过滤器,更改模板表达式的输出格式(例如,输出到果JSON)。有适用于JSON等语言的过滤器。to_json过滤器使用JSON格式化表达式输出。
格式:
{{ output | to_json }}
实操:
[root@centos8-1 ansible]# cat hosts_facts/user.yml //变量文件这样写
users:
- name: shen
age: 45
- name: long
age: 18
- name: fei
age: 42
- name: run
age: 46
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2 //按照格式写
{{ users | to_json}}
[root@centos8-1 ansible]#
[root@localhost tmp]# cat abc //执行后格式如下
[{"name": "shen", "age": 45}, {"name": "long", "age": 18}, {"name": "fei", "age": 42}, {"name": "run", "age": 46}]
[root@localhost tmp]#
也有其他过滤器,如to_nice_json,它们将表达式输出格式化为JSON人类可读格式。
格式:
{{ output | to_nice_json }}
实操:
[root@centos8-1 ansible]# cat hosts_facts/ceshi.j2
{{ users | to_nice_json}}
[root@centos8-1 ansible]#
[root@localhost tmp]# cat abc //执行结果
[
{
"age": 45,
"name": "shen"
},
{
"age": 18,
"name": "long"
},
{
"age": 42,
"name": "fei"
},
{
"age": 46,
"name": "run"
}
]
[root@localhost tmp]#
8.变量测试
在Ansible Playbook中与when子句一同使用的表达式是jinja2表达式。用于测试返回值的内置Ansible测试包括failed、changed、successded和skipped。以下任务演示了如何在条件表达式内使用测试。
tasks:
...output omitted...
- debug: msg="the execution was aborted"
when: returnvalue is failed
利用主机模式选择主机
9.引用清单主机
主机模式用于指定要作为play或临时命令的目标的主机。在最简单的形式中,清单中受管主机或主机组的名称就是指定该主机或主机组的主机模式。
在play中,hosts指定要针对其运行play的受管主机。对于临时命令,以命令行参数形式将主机模式提供给ansible命令。
本节中将通篇使用以下示例清单来演示主机模式。
[root@localhost ~]# cat myinventory
web.example.com
data.example.com
[lab]
labhost1.example.com
labhost2.example.com
[test]
test1.example.com
test2.example.com
[datacenter1]
labhost1.example.com
test1.example.com
[datacenter2]
labhost2.example.com
test2.example.com
[datacenter:children]
datacenter1
datacenter2
[new]
172.16.103.129
172.16.103.130
要演示如何解析主机模式,我们将执行Ansible Playbook的playbook.yml,使用不同的主机模式来以此示例清单中受管主机的不同子集作为目标。
10.受管主机
最基本的主机模式是单一受管主机名称列在清单中。这将指定该主机是清单中ansible命令要执行操作的唯一主机。
在该playbook运行时,第一个Gathering Facts任务应在与主机模式匹配的所有受管主机上运行。此任务期间的故障可能导致受管主机从play中移除。
如果清单中明确列出了IP地址,而不是主机名,则可以将其用作主机模式。如果IP地址未列在清单中,我们就无法用它来指定主机,即使该IP地址会在DNS中解析到这个主机名。
以下示例演示了如何使用主机模式来引用清单中包含的IP地址。
[root@centos8-1 ansible]# cat hosts_facts/apa.yml
---
- hosts: 192.168.100.147 //受管主机IP地址
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# ansible-playbook hosts_facts/apa.yml
PLAY [192.168.100.147] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.100.147]
PLAY RECAP *********************************************************************
192.168.100.147 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@centos8-1 ansible]#
注意:
在清单中通过IP地址引用受管主机存在一个问题,那就是难以记住play或临时命令所针对的主机使用了哪个IP地址。但是,如果没有可解析的主机名,我们可能必须按IP地址指定主机以进行连接。
可以通过设置ansible_host主机变量,在清单中将某一别名指向特定的IP地址。例如,你的清单中可以有一个名为dummy.example的主机,然后通过创建含有以下主机变量的host_vars/dummy.example文件,将使用该名称的连接指向IP地址192.168.100.147:
ansible_host: 192.168.100.147
11.使用组指定主机
当组名称用作主机模式时,它指定Ansible将对属于该组的成员的主机执行操作。
---
- hosts: lab
记住,有一个名为all的特别组,它匹配清单中的所有受管主机。
---
- hosts: all
还有一个名为ungrouped的特别组,它包括清单中不属于任何其他组的所有受管主机:
---
- hosts: ungrouped
12.使用通配符匹配多个主机
若要达成与all主机模式相同的目标,另一种方法是使用*
通配符,它将匹配任意字符串。如果主机模式只是带引号的星号,则清单中的所有主机都将匹配。
---
- hosts: '*' //这里得*指的是所有得意思,也就是在所有主机上都执行写入得命令
[root@centos8-1 ansible]# cat zhixing.yml
---
- name:
hosts: ‘*’ //所有主机执行
vars_files:
hosts_facts/user.yml
tasks:
- name: test
template:
src: hosts_facts/ceshi.j2
dest: /tmp/abc
[root@centos8-1 ansible]#
重点:
一些在主机模式中使用的字符对shell也有意义。通过ansible使用主机模式从命令行运行临时命令时,这可能会有问题。建议大家在命令行中使用单引号括起使用的主机模式,防止它们被shell意外扩展。
类似的,如果在Ansible Playbook中使用了任何特殊通配符或列表字符,必须将主机模式放在单引号里,确保能够正确解析主机模式。
也可使用*
字符匹配包含特定子字符串的受管主机或组。
例如,以下通配符主机模式匹配以**.example.com**结尾的所有清单名称:
---
- hosts: '*.example.com'
以下示例使用通配符主机模式来匹配开头为**192.168.2.**的主机或主机组的名称:
[root@centos8-1 ansible]# cat zhixing.yml
---
- name:
hosts: 192.168.100.*
vars_files:
hosts_facts/user.yml
tasks:
- name: test
template:
src: hosts_facts/ceshi.j2
dest: /tmp/abc
[root@centos8-1 ansible]#
重点:
通配符主机模式匹配所有清单名称、主机和主机组。它们不区别名称是DNS名、IP地址还是组,这可能会导致一些意外的匹配。
例如,根据示例清单,比较上一示例中指定datacenter主机模式的结果和data主机模式的结果:
[root@centos8-1 ansible]# cat zhixing.yml
---
- name:
hosts: 'data*'
vars_files:
hosts_facts/user.yml
tasks:
- name: test
template:
src: hosts_facts/ceshi.j2
dest: /tmp/abc
[root@centos8-1 ansible]#
13.列表
可以通过逻辑列表来引用清单中的多个条目。主机模式的逗号分隔列表匹配符合任何这些主机模式的所有主机。
如果提供受管主机的逗号分隔列表,则所有这些受管主机都将是目标:
---
- hosts: labhost1.example.com,test2.example.com,192.168.2.2
如果提供组的逗号分隔列表,则属于任何这些组的所有主机都将是目标:
---
- hosts: lab,datacenter1 //这里引用第九点引用清单里面得组
也可以混合使用受管主机、主机组和通配符,如下所示:
---
- hosts: 'lab,data*,192.168.2.2'
重点:
也可以用冒号(:)取代逗号。不过,逗号是首选的分隔符,特别是将IPv6地址用作受管主机名称时。
如果列表中的某一项以与符号(&)开头,则主机必须与该项匹配才能匹配主机模式。它的工作方式类似于逻辑AND。
例如,根据我们的示例清单,以下主机模式将匹配lab组中同时也属于datacenter1组的计算机:
---
- hosts: lab,&datacenter1
我们也可以通过主机模式**&lab,datacenter1或datacenter,&lab指定datacenter1组中的计算机只有在同时也属于lab**组时才匹配。
通过在主机模式的前面使用感叹号(!)表示从列表中排除匹配某一模式的主机。它的工作方式类似于逻辑NOT。
根据示例清单,以下示例匹配datacenter组中定义的所有主机,但test2.example.com除外:
---
- hosts: datacenter,!test2.example.com
也可以使用模式**’!test2.example.com,datacenter’**来获得相同的结果。
最后一个示例演示了使用匹配测试清单中的所有主机的主机模式,datacenter1组中的受管主机除外。
---
- hosts: all,!datacenter1
14.管理动态清单
前面我们用到的静态清单编写比较容易,对于管理小型基础架构而言也很方便。不过,如果要操作许多台计算机,或者在计算机更替非常快的环境中工作,可能难以让静态清单文件保持最新状态。
大多数大型IT环境中没有系统来跟踪可用的主机以及它们的组织方式。例如,可能有外部目录服务通过Zabbix等监控系统维护,或者位于FreeIPA或Active Directory服务器上。Cobbler等安装服务器或红帽卫星等管理服务可能跟踪部署的裸机系统。类似地,Amazon Web ServicesEC2或OpenStack部署等云服务,或者基于Vmware或红帽虚拟化的虚拟机基础架构可能是有关那些更替的实例和虚拟机的信息来源。
Ansible支持动态清单脚本,这些脚本在每当Ansible执行时从这些类型的来源检索当前的信息,使清单能够实时得到更新。这些脚本是可以执行的程序,能够从一些外部来源收集信息,并以JSON格式输出清单。
动态清单脚本的使用方式与静态清单文本文件一样。清单的位置可以直接在当前的ansible.cfg文件中指定,或者通过**-i**选项指定。如果清单文件可以执行,则它将被视为动态清单程序,Ansible会尝试运行它来生成清单。如果文件不可执行,则它将被视为静态清单。
清单位置可以在ansible.cfg配置文件中通过inventory参数进行配置。默认情况下,它被配置为**/etc/ansible/hosts**。
如果使用的目录系统或基础架构没有动态清单脚本,我们可以编写自定义清单程序。可以使用任何编程语言编写自定义程序,但传递适当的选项时必须以JSON格式返回清单信息。
ansible-inventory命令是学习如何以JSON格式编写Ansible清单的有用工具。
要以JSON格式显示清单文件的内容,请运行ansible-inventory --list命令。可以使用**-i**选项指定要处理的清单文件的位置,或仅使用当前Ansible配置设置的默认清单。
以下示例演示了如何使用ansible-inventory命令来处理INI样式的清单文件并以JSON格式输出。
[root@localhost ~]# cat inventory
workstation1.lab.example.com
[webservers]
web1.lab.example.com
web2.lab.example.com
[databases]
db1.lab.example.com
db2.lab.example.com
[root@localhost ~]# ansible-inventory -i inventory --list
脚本以适当的解释器行(例如,#!/usr/bin/python)开头并且可以执行,以便Ansible能够运行它。
在传递–list选项时,脚本必须显示清单中所有主机和组的JSON编码散列/字典。
在最简单的形式中,组可以是一个受管主机列表。在这个清单脚本的JSON编码输出示例中,webservers是一个主机组,该组内含web1.lab.example.com和web2.lab.example.com受管主机。databases主机组的成员有db1.lab.example.com和db2.lab.example.com主机。
[root@localhost ~]# ./inventoryscript --list
{
"webservers": ["web1.lab.example.com","web2.lab.example.com"],
"databases": ["db1.lab.example.com","db2.lab.example.com"]
}
此外,每个组的值可以是JSON散列/字典,含有由每一受管主机、任何子组和可能设置的任何组变量组成的列表。下一示例显示了一个比较复杂的动态清单的JSON编码输出。boston组具有两个子组(backup和ipa)、自己的三个受管主机,以及一个组变量集合(example_host: false)。
{
"webservers": [
"web1.lab.example.com",
"web2.lab.example.com"
],
"boston": {
"children": [
"backup",
"ipa"
],
"vars": {
"example_host": false
},
"hosts": [
"server1.demo.example.com",
"server2.demo.example.com",
"server3.demo.example.com",
]
},
"backup": [
"server4.demo.example.com"
],
"ipa": [
"server5.demo.example.com"
],
"_meta": {
"hostvars": {
"server5.demo.example.com": {
"ntpserver": "ntp.demo.example.com",
"dnsserver": "dns.demo.example.com"
}
}
}
}
该脚本也支持–host managed-host选项。此选项必须显示由与该主机关联的变量组成的JSON散列/字典,或者空白的JSON散列/字典。
[root@localhost ~]# ./inventoryscript --host server5.demo.example.com
{
"ntpserver": "ntp.demo.example.com",
"dnsserver": "dns.demo.example.com"
}
注意
通过–host hostname选项调用时,该脚本必须显示指定主机的变量的JSON散列/字典。如果不提供任何变量,则可能显示空白的JSON散列或字典。
另外,如果–list选项返回名为_meta的*元素,则可以在一次脚本调用中返回所有主机变量,从而提升脚本性能。此时,不会进行–host调用。
15.使用分叉在ansible中配置并行
当Ansible处理playbook时,会按顺序运行每个play。确定play的主机列表之后,Ansible将按顺序运行每个任务。通常,所有主机必须在任何主机在play中启动下一个任务之前成功完成任务。
理论上,Ansible可以同时连接到play中的所有主机以执行每项任务。这非常适用于小型主机列表。但如果该play以数百台主机为目标,则可能会给控制节点带来沉重负担。
Ansible所进行的最大同时连接数由Ansible配置文件中的forks参数控制。默认情况下设为5,这可通过以下方式之一来验证。
[root@centos8-1 ansible]# vim ansible.cfg //进入这个文件
#forks = 5 //默认就是5
我们也可以用命令来查看
[root@centos8-1 ansible]# ansible-config dump|grep -i forks
DEFAULT_FORKS(default) = 5
[root@centos8-1 ansible]#
[root@centos8-1 ansible]# 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
[root@centos8-1 ansible]#
例如,假设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数量。
16,管理滚动更新
通常,当Ansible运行play时,它会确保所有受管主机在启动任何主机进行下一个任务之前已完成每个任务。在所有受管主机完成所有任务后,将运行任何通知的处理程序。
但是,在所有主机上运行所有任务可能会导致意外行为。例如,如果play更新负载均衡Web服务器集群,则可能需要在进行更新时让每个Web服务器停止服务。如果所有服务器都在同一个play中更新,则它们可能全部同时停止服务。
避免此问题的一种方法是使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。
在下面的示例中,Ansible一次在两个受管主机上执行play,直至所有受管主机都已更新。Ansible首先在前两个受管主机上执行play中的任务。如果这两个主机中的任何一个或两个都通知了处理程序,则Ansible将根据这两个主机的需要运行处理程序。在这两个受管主机上执行完play时,Ansible会在接下来的两个受管主机上重复该过程。Ansible继续以这种方式运行play,直到所有受管主机都已更新。
---
- name: Rolling update
hosts: webservers
serial: 2 //使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。
tasks:
- name: latest apache httpd package is installed
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运行。这是一个有用的功能,因为只有一部分服务器会不可用,使服务降级而不是中断。
时,Ansible会在接下来的两个受管主机上重复该过程。Ansible继续以这种方式运行play,直到所有受管主机都已更新。
---
- name: Rolling update
hosts: webservers
serial: 2 //使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。
tasks:
- name: latest apache httpd package is installed
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或以上。