1、引用清单主机
主机模式用于指定要作为play或临时命令的目标的主机。在最简单的形式中,清单中受管主机或主机组的名称就是指定该主机或主机组的主机模式。
在play中,hosts指定要针对其运行play的受管主机。对于临时命令,以命令行参数形式将主机模式提供给ansible命令。
[root@gusiwen ~]# 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
要演示如何解析主机模式,我们将执行Ansible Playbook的playbook.yml,使用不同的主机模式来以此示例清单中受管主机的不同子集作为目标。
2、受管主机
最基本的主机模式是单一受管主机名称列在清单中。这将指定该主机是清单中ansible命令要执行操作的唯一主机。
在该playbook运行时,第一个Gathering Facts任务应在与主机模式匹配的所有受管主机上运行。此任务期间的故障可能导致受管主机从play中移除。
如果清单中明确列出了IP地址,而不是主机名,则可以将其用作主机模式。如果IP地址未列在清单中,我们就无法用它来指定主机,即使该IP地址会在DNS中解析到这个主机名。
[root@gusiwen playbook]# cat playbook.yml --- - hosts: apache gather_facts: yes tasks: - name: ping: [root@gusiwen playbook]# ansible-playbook playbook.yml PLAY [httpd] ************************************************************************************** TASK [Gathering Facts] ***************************************************************************** ok: [192.168.191.140] TASK [ping] **************************************************************************************** ok: [192.168.191.140] PLAY RECAP ***************************************************************************************** 192.168.191.140 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 1
可以通过设置ansible_host主机变量,在清单中将某一别名指向特定的IP地址
[root@gusiwen playbook]# cat inventory --- - hosts: 192.168.191.140 [root@gusiwen playbook]# ansible all --list-hosts hosts (1): host [root@gusiwen playbook]# ansible all -m ping host | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": false, "ping": "pong" }
3、使用组指定主机
当组名称用作主机模式时,它指定Ansible将对属于该组的成员的主机执行操作
[root@localhost ~]# cat playbook.yml --- - hosts: web tasks: - name: ping: 当有一个名为all的特别组,它匹配清单中的所有受管主机
[root@localhost ~]# cat playbook.yml --- - hosts: all tasks: - name: ping:
还有一个名为ungrouped的特别组,它包括清单中不属于任何其他组的所有受管主机
[root@localhost ~]# cat playbook.yml --- - hosts: ungrouped tasks: - name: ping:
4.使用通配符匹配多个主机
若要达成与all主机模式相同的目标,另一种方法是使用*通配符,它将匹配任意字符串。如果主机模式只是带引号的星号,则清单中的所有主机都将匹配。
[root@localhost ~]# cat playbook.yml --- - hosts:'*' (此处的引号必须加) tasks: - name: ping:
重要提示:
一些在主机模式中使用的字符对shell也有意义。通过ansible使用主机模式从命令行运行临时命令时,这可能会有问题。建议大家在命令行中使用单引号括起使用的主机模式,防止它们被shell意外扩展。
类似的,如果在Ansible Playbook中使用了任何特殊通配符或列表字符,必须将主机模式放在单引号里,确保能够正确解析主机模式。
--- - hosts: '!test1.example.com,development'
也可使用*
字符匹配包含特定子字符串的受管主机或组。
--- - hosts: '*.example.com'
以下示例使用通配符主机模式来匹配开头为**192.168.191.**的主机或主机组的名称:
--- - hosts: '192.168.191.*'
以下示例使用通配符主机模式来匹配开头为datacenter的主机或主机组的名称。
--- - hosts: 'datacenter*'
重要:
通配符主机模式匹配所有清单名称、主机和主机组。它们不区别名称是DNS名、IP地址还是组,这可能会导致一些意外的匹配。
例如,根据示例清单,比较上一示例中指定datacenter主机模式的结果和data主机模式的结果:
--- - hosts: 'data*'
5、列表
可以通过逻辑列表来引用清单中的多个条目;主机模式的逗号分隔列表匹配符合任何这些主机模式的所有主机
也可以混合使用受管主机、主机组和通配符
[root@localhost ~]# cat playbook.yml --- - hosts: labhost.example.com,www.test.com,192.168.191.140 tasks: - name: ping: [root@gusiwen ~]# vim playbook.yml --- - hosts: '*.example.com,www.test.com,192.168.191.140' tasks: - name: ping: 注意: 也可以用冒号(:)取代逗号。不过,逗号是首选的分隔符,特别是将IPv6地址用作受管主机名称时。
如果列表中的某一项以与符号(&)开头,则主机必须与该项匹配才能匹配主机模式。它的工作方式类似于逻辑AND。
[root@localhost ~]# cat playbook.yml --- - hosts: host,&test tasks: - name: ping:
2、管理动态清单
1、动态生成清单
前面我们用到的静态清单编写比较容易,对于管理小型基础架构而言也很方便。不过,如果要操作许多台计算机,或者在计算机更替非常快的环境中工作,可能难以让静态清单文件保持最新状态。
大多数大型IT环境中没有系统来跟踪可用的主机以及它们的组织方式。例如,可能有外部目录服务通过Zabbix等监控系统维护,或者位于FreeIPA或Active Directory服务器上。Cobbler等安装服务器或红帽卫星等管理服务可能跟踪部署的裸机系统。类似地,Amazon Web ServicesEC2或OpenStack部署等云服务,或者基于Vmware或红帽虚拟化的虚拟机基础架构可能是有关那些更替的实例和虚拟机的信息来源。
Ansible支持动态清单脚本,这些脚本在每当Ansible执行时从这些类型的来源检索当前的信息,使清单能够实时得到更新。这些脚本是可以执行的程序,能够从一些外部来源收集信息,并以JSON格式输出清单。
动态清单脚本的使用方式与静态清单文本文件一样。清单的位置可以直接在当前的ansible.cfg文件中指定,或者通过-i选项指定。如果清单文件可以执行,则它将被视为动态清单程序,Ansible会尝试运行它来生成清单。如果文件不可执行,则它将被视为静态清单。
清单位置可以在ansible.cfg配置文件中通过inventory参数进行配置。默认情况下,它被配置为/etc/ansible/hosts。
2、开源社区脚本
开源社区向Ansible项目贡献了大量现有的动态清单脚本。它们没有包含在ansible软件包中。这些脚本可从Ansible GigHub网站(https://github.com/ansible/ansible/tree/devel/examples)获取。
3、编写动态清单文件
如果使用的目录系统或基础架构没有动态清单脚本,我们可以编写自定义清单程序。可以使用任何编程语言编写自定义程序,但传递适当的选项时必须以JSON格式返回清单信息。
ansible-inventory命令是学习如何以JSON格式编写Ansible清单的有用工具。
要以JSON格式显示清单文件的内容,请运行ansible-inventory --list命令。可以使用-i选项指定要处理的清单文件的位置,或仅使用当前Ansible配置设置的默认清单。
以下示例演示了如何使用ansible-inventory命令来处理INI样式的清单文件并以JSON格式输出。
[root@localhost ~]# vim 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
如果要自己编写动态清单脚本,可以通过部署动态清单来源[https://docs.ansible.com/ansible/latest/dev_guide/developing_inventory.html]获得更详细的信息。以下是一个简略概要。
脚本以适当的解释器行(例如,#!/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调用。
有关详细信息,请参见部署动态清单来源[https://docs.ansible.com/ansible/latest/dev_guide/developing_inventory.html]。
4、管理多个清单
Ansible支持在同一运行中使用多个清单。如果清单的位置是一个目录(不论是由-i选项设置的、是inventory参数的值,还是以某种其他方式设置的),将组合该目录中包含的所有清单文件(不论是静态还是动态)来确定清单
该目录中的可执行文件将用于检索动态清单,其他文件则被用作静态清单
清单文件不应依赖于其他清单文件或脚本来解析
注意:
清单文件的解析顺序不是由文档指定的。如果存在多个清单文件,它们会按照字母顺序进行解析
如果一个清单源依赖于另一个清单源的信息,则它们的加载顺序可能会确定清单文件是按预期工作还是引发错误。因此,务必要确保所有文件都自相一致,从而避免意外的错误
Ansible会忽略清单目录中以特定后缀结尾的文件。这可以通过在Ansible配置文件中的inventory_ignore_extensions指令来控制
[cloud-east] [servers] test.demo.example.com [servers:children] cloud-east
三、配置并行
1、使用分叉在ansible中配置并行
当Ansible处理playbook时,会按顺序运行每个play。确定play的主机列表之后,Ansible将按顺序运行每个任务
通常,所有主机必须在任何主机在play中启动下一个任务之前成功完成任务
注意:
理论上,Ansible可以同时连接到play中的所有主机以执行每项任务。这非常适用于小型主机列表。但如果该play以数百台主机为目标,则可能会给控制节点带来沉重负担
Ansible所进行的最大同时连接数由Ansible配置文件中的forks参数控制,默认情况下设为5(forks:分叉)
通过以下方式之一来验证:
[root@ansible playbook]# ansible-config dump|grep -i forks DEFAULT_FORKS(default) = 5 [root@ansible playbook]# grep forks /etc/ansible/ansible.cfg #forks = 5 [root@ansible playbook]# 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@ansible playbook]#
forks的默认值设置得非常保守。如果你的控制节点正在管理Linux主机,则大多数任务将在受管主机上运行,并且控制节点的负载较少。在这种情况下,通常可以将forks的值设置得更高,可能接近100,然后性能就会提高。
如果playbook在控制节点上运行很多代码,则应明智地提高forks限值。如果使用Ansible管理网络路由器和交换机,则大多数模块在控制节点上运行而不是在网络设备上运行。由于这会增加控制节点上的负载,因此其支持forks数量增加的能力将显著低于仅管理Linux主机的控制节点。
可以从命令行覆盖Ansible配置文件中forks的默认设置。ansible和ansible-playbook命令均提供-f或–forks选项以指定要使用的forks数量。
2、管理滚动更新
通常,当Ansible运行play时,它会确保所有受管主机在启动任何主机进行下一个任务之前已完成每个任务。在所有受管主机完成所有任务后,将运行任何通知的处理程序。
但是,在所有主机上运行所有任务可能会导致意外行为。例如,如果play更新负载均衡Web服务器集群,则可能需要在进行更新时让每个Web服务器停止服务。如果所有服务器都在同一个play中更新,则它们可能全部同时停止服务。
避免此问题的一种方法是使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。
实例:
[root@gusiwen playbook]# vim host.yml --- - hosts: all serial: 2 tasks: - name: install httpd yum: name: httpd state: present notify: - start httpd handlers: - name: start httpd service: name: httpd state: started
在上面的示例中,Ansible一次在两个受管主机上执行play,直至所有受管主机都已更新
更新机制:
Ansible首先在前两个受管主机上执行play中的任务。如果这两个主机中的任何一个或两个都通知了处理程序,则Ansible将根据这两个主机的需要运行处理程序。
在这两个受管主机上执行完play时,Ansible会在接下来的两个受管主机上重复该过程。Ansible继续以这种方式运行play,直到所有受管主机都已更新
假设上一示例中的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运行。这是一个有用的功能,因为只有一部分服务器会不可用,使服务降级而不是中断。