玩转Ansible条件任务而不踩坑

IT民工金鱼哥希望能以通俗易懂、诙谐幽默的方式把Ansible体系文章呈现给大家,让枯燥的知识点、让繁重的学习变的有趣一些。


文章目录

  • 玩转Ansible条件任务而不踩坑
  • 前言
  • 1. 条件任务语法
  • 简单的条件语句案例:
  • 测试可执行性:
  • 如果将False改为True:
  • 执行测试
  • 另一种写法(defined):
  • 如果定义my_server而不赋值:
  • 执行测试:
  • 如果我们给变量赋值:
  • 测试执行:
  • 如果这样编写playbook:
  • 2. 由此可以得知,when的条件语句有两种写法:
  • 3. 示例条件语法
  • ansible_distribution in supported_distros类型的例子:
  • 测试执行:
  • 总结


前言

对于一些比较复杂的内容,例如在shell当中,我们会使用各种条件判断来进行,在Ansible当中也是,Ansible可使用conditionals,在符合特定条件时执行任务或play。例如,可以利用一个条件在Ansible安装或配置服务前确定受管主机上的可用内存。

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

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

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

1. 条件任务语法

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

简单的条件语句案例:

这个条件语句的意思是当my_server生效时才安装httpd;如果设置成False那么my_server并不生效

---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    my_server: False
  tasks:
    - name: "{{ my_server }} is installed"
      yum:
        name: httpd
        state: latest
      when: my_server
测试可执行性:
[student@servera ~]$ ansible-playbook test.yml 
PLAY [when test] ***********************************************************************************

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

TASK [False is installed] **************************************************************************
skipping: [servera]

PLAY RECAP ***************************************************************************************
servera                    : ok=1    changed=0    unreachable=0    failed=0

# 测试执行时直接跳过了。

这里跳过的原因是因为要安装httpd服务的条件是my_server变量必须存在,而将my_server设置成False后,**my_server变量不生效就不满足when的条件,**所以即使这个playbook语法有问题也可以执行,但其中包含的任务是不能被执行的所以直接跳过了。

如果将False改为True:
[student@workstation ~]$ ansible-playbook -i hosts test.yml 
---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    my_server: True
  tasks:
    - name: "{{ my_server }} is installed"
      yum:
        name: httpd
        state: latest
      when: my_server
执行测试
PLAY [when test] ************************************************************************

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

TASK [True is installed] ****************************************************************
changed: [servera]

PLAY RECAP ******************************************************************************
servera                    : ok=2    changed=1    unreachable=0    failed=0

# 此时的状态变成了changed而不是skipped

当状态变为changed证明任务状态已经发生改变,httpd服务已经符合了安装条件

另一种写法(defined):
---
- name: when test
  hosts: servera
  remote_user: root
  tasks:
    - name: "ensure {{ my_server }} is installed"
      yum:
        name: "{{ my_server }}"
        state: latest
      when: my_server is defined

需要注意的是,这里的my_server变量依旧是没有被定义的

[student@servera ~]$ ansible-playbook test.yml 
PLAY [when test] ***********************************************************************************

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

TASK [ensure {{ my_server }} is installed] *******************************************************
skipping: [servera]

PLAY RECAP **************************************************************************************
servera                    : ok=1    changed=0    unreachable=0    failed=0

# 所以在执行测试时也跳过了任务

如果定义my_server而不赋值:
[student@servera ~]$ cat test.yml 
---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    my_server:
  tasks:
    - name: "ensure {{ my_server }} is installed"
      yum:
        name: "{{ my_server }}"
        state: latest
      when: my_server is defined

# 这里的条件是如果变量my_server被定义则执行安装的任务

执行测试:
[student@servera ~]$ ansible-playbook test.yml
PLAY [when test] ************************************************************************

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

TASK [ensure  is installed] *************************************************************
fatal: [servera]: FAILED! => {"changed": false, "module_stderr": "Shared connection to servera closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/root/.ansible/tmp/ansible-tmp-1599491486.79-25931710335605/AnsiballZ_yum.py\", line 113, in <module>\r\n    _ansiballz_main()\r\n  File \"/root/.ansible/tmp/ansible-tmp-1599491486.79-25931710335605/AnsiballZ_yum.py\", line 105, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/root/.ansible/tmp/ansible-tmp-1599491486.79-25931710335605/AnsiballZ_yum.py\", line 48, in invoke_module\r\n    imp.load_module('__main__', mod, module, MOD_DESC)\r\n  File \"/tmp/ansible_yum_payload_FaoojI/__main__.py\", line 1532, in <module>\r\n  File \"/tmp/ansible_yum_payload_FaoojI/__main__.py\", line 1527, in main\r\n  File \"/tmp/ansible_yum_payload_FaoojI/__main__.py\", line 374, in __init__\r\n  File \"/tmp/ansible_yum_payload_FaoojI/ansible_yum_payload.zip/ansible/module_utils/yumdnf.py\", line 79, in __init__\r\nTypeError: 'NoneType' object is not iterable\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
	to retry, use: --limit @/home/student/test.retry

PLAY RECAP ******************************************************************************
servera                    : ok=1    changed=0    unreachable=0    failed=1

# 在执行测试时会这样报错

如果定义的变量,却不给变量赋值。那么这个playbook是不能成功执行的,因为yum模块当中name是不能定义为空的。

如果我们给变量赋值:
---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    my_server: httpd
  tasks:
    - name: "ensure {{ my_server }} is installed"
      yum:
        name: "{{ my_server }}"
        state: latest
      when: my_server is defined

# 这里指定my_server为httpd

测试执行:
[student@servera ~]$ ansible-playbook test.yml 
PLAY [when test] ************************************************************************

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

TASK [ensure httpd is installed] ********************************************************
changed: [servera]

PLAY RECAP ******************************************************************************
servera                    : ok=2    changed=1    unreachable=0    failed=0

# 执行测试成功

如果这样编写playbook:
---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    my_server:
  tasks:
    - name: "ensure httpd is installed"
      yum:
        name: httpd
        state: latest
      when: my_server is defined

这里的条件是如果my_server变量为被定义则执行安装httpd的任务。在条件语句中只要满足when的条件就执行任务。

然后也添加了vars,这样就相当于定义了my_server,只不过此时my_server的值为空,此时仍可以安装httpd服务即使my_server的值为空,因为空值也是有定义。

[student@servera ~]$ ansible-playbook -i hosts test.yml 
PLAY [when test] ******************************************************************************

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

TASK [ensure httpd is installed] ***************************************************************
changed: [servera]

PLAY RECAP ***********************************************************************************
servera                    : ok=2    changed=1    unreachable=0    failed=0

2. 由此可以得知,when的条件语句有两种写法:

  1. 将变量作为条件任务的触发器,使用True/False控制条件的触发。
  2. 在playbook中定义变量并直接作为需要实施的任务,通过控制变量是否被定义(my_server is defined/my_server is not defined)来控制条件的触发。

下表显示了在处理条件时可使用的一些运算:

3. 示例条件语法

操作

示例

等于(值为字符串

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

比较难理解应该是最后的那个语法,可参考以下剧本的演示来进行理解。

ansible_distribution in supported_distros类型的例子:
---
- name: when test
  hosts: servera
  remote_user: root
  vars:
    opreate_system:
      - x86_64
      - x86_32

  tasks:
    - name: httpd installed
      yum:
        name: httpd
        state: present
      when: ansible_facts['architecture'] in opreate_system

这里的条件语句意思是,如果ansible_facts中的ansible_architecture的值能在opreate_system变量中找到符合的值,则安装httpd服务,opreate_system是自己指定的变量

测试执行:
[student@servera ~]$ ansible-playbook -i hosts test.yml 
PLAY [when test] ************************************************************************

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

TASK [httpd installed] ******************************************************************
changed: [servera]

PLAY RECAP ******************************************************************************
servera                    : ok=2    changed=1    unreachable=0    failed=0

注意when语句的缩进。由于when语句不是模块变量,它必须通过缩进到任务的最高级别,放置在模块的外面。

任务是YAML散列/字典,when语句只是任务中的又一个键,就如任务的名称以及它所使用的模块一样。通常的惯例是将可能存在的任何when关键字放在任务名称和模块(及模块参数)的后面

总结

  • 在一些复杂的调用,需要使用到条件任务来执行剧本任务。
  • 条件任务需要判断条件为真才执行。
  • 熟悉条件判断的各种语法。
  • 注意when语句的语法编写。
  • 若喜欢金鱼哥的文章,顺手点个赞。也可点个关注,因为后续会不断上干货。

提高ansible 执行速度_提高ansible 执行速度