当前位置: 代码迷 >> 综合 >> Ansible 事实 循环 条件判断
  详细解决方案

Ansible 事实 循环 条件判断

热度:36   发布时间:2023-12-04 04:41:16.0

Ansible事实

Ansible事实是Ansible在受管主机上自动检测到的变量。事实中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用。

为受管主机收集的一些事实可能包括:

  • 主机名称
  • 内核版本
  • 网络接口
  • IP地址
  • 操作系统版本
  • 各种环境变量
  • CPU数量
  • 提供的或可用的内存
  • 可用磁盘空间

借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。例如:

  • 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
  • 可以根据通过事实报告的可用内存来自定义MySQL配置文件
  • 可以根据事实的值设置配置文件中使用的IPv4地址

通常,每个play在执行第一个任务之前会先自动运行setup模块来收集事实。

查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。

下表显示了可能从受管节点收集的并可在playbook中使用的一些事实:
Ansible事实的示例

事实 变量
短主机名 ansible_facts[‘hostname’]
完全限定域名 ansible_facts[‘fqdn’]
IPv4地址 ansible_facts[‘default_ipv4’][‘address’]
所有网络接口的名称列表 ansible_facts[‘interfaces’]
/dev/vda1磁盘分区的大小 ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]
DNS服务器列表 ansible_facts[‘dns’][‘nameservers’]
当前运行的内核版本 ansible_facts[‘kernel’]

[root@master xm]# cat fact.yml 
---
- hosts: 192.168.72.137tasks:- debug:var: ansible_facts[root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [debug] ***************************************************************************************************************************
ok: [192.168.72.137] => {"ansible_facts": {"all_ipv4_addresses": ["192.168.72.137"],"all_ipv6_addresses": ["fe80::7014:45ea:bce2:63"],"ansible_local": {},
........

将事实替换为动态的值

[root@master xm]# cat fact.yml 
---
- hosts: 192.168.72.137tasks:- debug:msg: >The IPv4 address of {
   { ansible_facts['default_ipv4']['address'] }} is {
   { ansible_facts['fqdn'] }}[root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [debug] ***************************************************************************************************************************
ok: [192.168.72.137] => {"msg": "The IPv4 address of 192.168.72.137 is localhost.localdomain\n"
}PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   //可以用setup模块显示所有事实
[root@master xm]# ansible 192.168.72.137 -m setup
Enter passphrase for key '/root/.ssh/id_rsa': 
192.168.72.137 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.72.137"],"ansible_all_ipv6_addresses": ["fe80::7014:45ea:bce2:63"],"ansible_apparmor": {"status": "disabled"},"ansible_architecture": "x86_64","ansible_bios_date": "07/22/2020","ansible_bios_version": "6.00",
........

关闭事实收集

关闭事实可以提高运行速度(gather_facts: no) 也可以使用setup模块来手动收集事实

[root@master xm]# cat fact.yml 
---
- hosts: 192.168.72.137gather_facts: notasks:- setup:- debug:var:ansible_facts[root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [setup] ***************************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [debug] ***************************************************************************************************************************
ok: [192.168.72.137] => {"ansible_facts": {"all_ipv4_addresses": ["192.168.72.137"],"all_ipv6_addresses": ["fe80::7014:45ea:bce2:63"],
.......

自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上。这些事实整合到setup模块在受管主机上运行时收集的标准事实列表中。它们让受管主机能够向Ansible提供任意变量,以用于调整play的行为。

自定义事实可以在静态文件中定义,格式可为INI文件或采用JSON。它们也可以是生成JSON输出的可执行脚本,如同动态清单脚本一样。

有了自定义事实,我们可以为受管主机定义特定的值,供play用于填充配置文件或有条件地运行任务。动态自定义事实允许在play运行时以编程方式确定这些事实的值,甚至还可以确定提供哪些事实。

默认情况下,setup模块从各受管主机的/etc/ansible/facts.d目录下的文件和脚本中加载自定义事实。各个文件或脚本的名称必须以.fact结尾才能被使用。动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件。

以下是采用INI格式编写的静态自定义事实文件。INI格式的自定义事实文件包含由一个部分定义的顶层值,后跟用于待定义的事实的键值对:

[root@master xm]# cat xxx.fact 
[bbc]
package = httpd
service = httpd
state = started
enabled = yes[root@master xm]# cat fact.yml 
---
- hosts: 192.168.72.137vars:dir: /etc/ansible/facts.dfile: xxx.facttasks:- name: create dirfile:state: directoryrecurse: yespath: "{
   { dir }}"- name: create filecopy:src: "{
   { file }}"dest: "{
   { dir }}"[root@master xm]# ansible-playbook fact.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
ok: [192.168.72.137]TASK [create dir] **********************************************************************************************************************
ok: [192.168.72.137]TASK [create file] *********************************************************************************************************************
changed: [192.168.72.137]PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

魔法变量

一些变量并非事实或通过setup模块配置,但也由Ansible自动设置。这些魔法变量也可用于获取与特定受管主机相关的信息。

最常用的有四个:

魔法变量 说明
hostvars 包含受管主机的变量,可以用于获取另一台受管主机的变量的值。如果还没有为受管主机收集事实,则它不会包含该主机的事实
group_names 列出当前受管主机所属的所有组
groups 列出清单中的所有组和主机
inventory_hostname 包含清单中配置的当前受管主机的主机名称。因为各种原因有可能与事实报告的主机名称不同

另外还有许多其他的“魔法变量”。有关更多信息,请参见以下链接:https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

循环

通过利用循环,我们无需编写多个使用同一模块的任务。例如,他们不必编写五个任务来确保存在五个用户,而是只需编写一个任务来对含有五个用户的列表迭代,从而确保它们都存在。

Ansible支持使用loop关键字对一组项目迭代任务。可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。

简单循环

[root@master xm]# cat sb.yml 
---
- hosts: 192.168.72.137tasks:- name:user:name: "{
   { item }}"state: presentloop:- xxx- zzz[root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [user] ****************************************************************************************************************************
changed: [192.168.72.137] => (item=xxx)
changed: [192.168.72.137] => (item=zzz)PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

循环散列或字典列表

loop列表不需要是简单值列表。在以下示例中,列表中的每个项实际上是散列或字典。示例中的每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过item.name和item.groups变量来检索。

[root@master xm]# cat sb.yml 
---
- hosts: 192.168.72.137tasks:- name:user:name: "{
   { item.name }}"groups: "{
   { item.groups }}"state: presentloop:- name: xmgroups: xxx- name: slfgroups: zzz[root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [user] ****************************************************************************************************************************
changed: [192.168.72.137] => (item={'name': 'xm', 'groups': 'xxx'})
changed: [192.168.72.137] => (item={'name': 'slf', 'groups': 'zzz'})PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

将Register变量与Loop一起使用

[root@master xm]# cat sb.yml 
---
- hosts: 192.168.72.137tasks:- name:user:name: "{
   { item.name }}"groups: "{
   { item.groups }}"state: presentloop:- name: xmgroups: xxx- name: slfgroups: zzzregister: result- name:debug:var: result[root@master xm]# ansible-playbook sb.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [user] ****************************************************************************************************************************
ok: [192.168.72.137] => (item={'name': 'xm', 'groups': 'xxx'})
ok: [192.168.72.137] => (item={'name': 'slf', 'groups': 'zzz'})TASK [debug] ***************************************************************************************************************************
ok: [192.168.72.137] => {"result": {"changed": false,"msg": "All items completed","results": [
.......

条件判断

Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在Ansible安装或配置服务前确定受管主机上的可用内存。

我们可以利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔值的运算符。

以下场景说明了在Ansible中使用条件的情况:

  • 可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
  • Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如,如果某一程序失败,则将路过批处理。
  • 可以利用Ansible事实来确定受管主机网络配置,并决定要发送的模板文件(如,网络绑定或中继)。
  • 可以评估CPU的数量,来确定如何正确调节某一Web服务器。
  • 将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的MD5检验以和查看服务是否已更改。

条件任务语法

when语句用于有条件地运行任务。它取要测试的条件为值。如果条件满足,则运行任务。如果条件不满足,则跳过任务。

//值为true是才会运行,值为false时跳过
[root@master xm]# cat sb.yml 
---
- hosts: 192.168.72.137vars:slf: truetasks:- name:user:name: "{
   { item.name }}"groups: "{
   { item.groups }}"state: presentloop:- name: xmgroups: xxx- name: slfgroups: zzzwhen: slf

示例条件

操作 示例
等于(值为字符串) ansible_machine == “x86_64”
等于(值为数字) max_memory == 512
小于 min_memory < 128
大于 min_memory > 256
小于等于 min_memory <= 256
大于等于 min_memory >= 512
不等于 min_memory != 512
变量存在 min_memory is defined
变量不存在 min_memory is not defined
布尔变量是True。1、True或yes的求值为True memory_available
布尔变量是False。0、False或no的求值为False not memory_available
第一个变量的值存在,作为第二个变量的列表中的值 ansible_distribution in supported_distros

测试多个条件

一个when语句可用于评估多个条件。使用and和or关键字组合条件,并使用括号分组条件。

如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企业linux或Fedora,则下述条件得到满足:

when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"

使用and运算时,两个条件都必须为真,才能满足整个条件语句。例如,如果远程主机是红帽企业Linux7.5主机,并且安装的内核是指定版本,则将满足以下条件:

when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"

when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:

when:- ansible_distribution_version == "7.5"- ansible_kernel == "3.10.0-327.el7.x86_64"

这种格式提高了可读性,而可读性是良好编写Ansible Playbook的关键目标。

通过使用括号分组条件,可以表达更复杂的条件语句。例如,如果计算机上运行的是红帽企业Linux7或Fedora28,则下述条件语句得到满足。此示例使用大于字符,这样长条件就可以在playbook中分成多行,以便于阅读。

when: >( ansible_distribution == "Redhat" andansible_distribution_major_version == "7" )or( ansible_distribution == "Fedora" andansible_distribution_major_version == "28" )

组合循环和有条件任务

---
- hosts: 192.168.72.137tasks:- name:yum:name: mariadb-serverstate: latestloop: "{
   { ansible_mounts }}"when: item.mount == "/" and item.size_available > 10000000[root@master xm]# ansible-playbook xx.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
ok: [192.168.72.137]TASK [yum] *****************************************************************************************************************************
ok: [192.168.72.137] => (item={'mount': '/', 'device': '/dev/mapper/rhel-root', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 50390917120, 'size_available': 47464321024, 'block_size': 4096, 'block_total': 12302470, 'block_available': 11587969, 'block_used': 714501, 'inode_total': 24616960, 'inode_available': 24552800, 'inode_used': 64160, 'uuid': '22c13e5c-701e-4552-94a1-520b0b285506'})
skipping: [192.168.72.137] => (item={'mount': '/boot', 'device': '/dev/nvme0n1p1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 1063256064, 'size_available': 876052480, 'block_size': 4096, 'block_total': 259584, 'block_available': 213880, 'block_used': 45704, 'inode_total': 524288, 'inode_available': 523987, 'inode_used': 301, 'uuid': 'cd44aec7-b06c-4df8-9fa6-187c3222f148'}) PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   //当vsftpd运行时,重启httpd
---
- hosts: 192.168.72.137tasks:- name:shell: systemctl status vsftpdignore_errors: yesregister: result- name:service:name: httpdstate: restartedwhen: result.rc == 0[root@master xm]# ansible-playbook xx.yml PLAY [192.168.72.137] ******************************************************************************************************************TASK [Gathering Facts] *****************************************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.72.137]TASK [shell] ***************************************************************************************************************************
fatal: [192.168.72.137]: FAILED! => {"changed": true, "cmd": "systemctl status vsftpd", "delta": "0:00:00.009824", "end": "2021-07-25 06:32:27.841754", "msg": "non-zero return code", "rc": 4, "start": "2021-07-25 06:32:27.831930", "stderr": "Unit vsftpd.service could not be found.", "stderr_lines": ["Unit vsftpd.service could not be found."], "stdout": "", "stdout_lines": []}
...ignoringTASK [service] *************************************************************************************************************************
skipping: [192.168.72.137]PLAY RECAP *****************************************************************************************************************************
192.168.72.137             : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=1