实施任务控制务

实施任务控制务

一、编写循环和条件任务

1. 利用循环迭代任务

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

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

1.1 简单循环

简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。

请思考以下代码片段,它使用两次service模块来确保两个网络服务处于运行状态:


[root@ansible opt]# vim site.yaml 
---
  - hosts: httpd
    tasks:
      - name: Apache is running
        service:
          name: httpd
          state: started
      - name: Php is runing
        service:
          name: php-fpm
          state: started

[root@ansible opt]# ansible-playbook  site.yaml    

PLAY [httpd] **************************************************************************************************

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

TASK [Apache is running] **************************************************************************************
ok: [httpd]

TASK [Php is runing] ******************************************************************************************
ok: [httpd]

PLAY RECAP ****************************************************************************************************
httpd                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


这两个任务可以重新编写为使用一个简单循环,从而只需一个任务来确保两个服务都在运行:

[root@ansible opt]# vim site.yaml 

---
  - hosts: httpd
    tasks:
      - name: Service is running
        service:
          name: "{{ item  }}"
          state: started
        loop:
          - httpd
          - php-fpm

[root@ansible opt]# ansible-playbook  site.yaml    

PLAY [httpd] **************************************************************************************************

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

TASK [Apache is running] **************************************************************************************
ok: [httpd] => (item=httpd)
ok: [httpd] => (item=php-fpm)

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

可以通过一个变量提供loop所使用的列表。在以下示例中,变量start_service含有需要处于运行状态的服务的列表。

[root@ansible opt]# vim site.yaml 
---
  - hosts: httpd
    vars:
      start_service:
        - httpd
        - php-fpm
    tasks:
      - name: Service is running
        service:
          name: "{{ item  }}"
          state: started
        loop: "{{ start_service  }}"

[root@ansible opt]# ansible-playbook  site.yaml    

PLAY [httpd] **************************************************************************************************

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

TASK [Service is running] *************************************************************************************
ok: [httpd] => (item=httpd)
ok: [httpd] => (item=php-fpm)

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


1.2 循环散列或字典列表

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

[root@ansible opt]# vim site.yaml 
---
  - hosts: httpd
    tasks:

       - name: Create groups
         group:
            name: "{{ item }}"
            state: present
            system: yes
         loop:
           - jery
           - tom

       - name: Users exist and are in the correct groups
         user:
           name: "{{ item.name  }}"
           state: present
           system: yes
           groups: "{{ item.groups  }}"
         loop:
           - name: alice
             groups: jery    #组jery必须事先存在,否则会报错
           - name: natasha
             groups: tom  #组tom必须事先存在,否则会报错




[root@ansible opt]# ansible-playbook  site.yaml    

PLAY [httpd] **************************************************************************************************

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

TASK [Create groups] ******************************************************************************************
changed: [httpd] => (item=jery)
changed: [httpd] => (item=tom)

TASK [Users exist and are in the correct groups] **************************************************************
changed: [httpd] => (item={'name': 'alice', 'groups': 'jery'})
changed: [httpd] => (item={'name': 'natasha', 'groups': 'tom'})

PLAY RECAP ****************************************************************************************************
httpd                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


这一示例中结果是用户alice存在且为组jery的成员,并且用户joe存在且为组tom的成员

1.3 较早样式的循环关键字

在Ansible2.5之前,大多数playbook使用不同的循环语法。提供了多个循环关键字,前缀为whth_,后面跟Ansible查找插件的名称。这种循环语法在现有playbook中很常见,但在将来的某个时候可能会被弃用。

较早样式的Ansible循环

循环关键字描述
with_items行为与简单列表的loop关键字相同,例如字符串列表或散列/字典列表。但与loop不同的是,如果为with_items提供了列表的列表,它们将被扁平化为单级列表。循环变量item保存每次迭代过程中使用的列表项。
with_file此关键字需要控制节点文件名列表。循环变量item在每次迭代过程中保存文件列表中相应文件的内容。
with_sequence此关键字不需要列表,而是需要参数来根据数字序列生成值列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值。

playbook中的with_items的示例如下所示:

[root@ansible opt]# vim site1.yaml 
---
  - hosts: httpd
    tasks:
    vars:
      data: 
        - user1
        - user2
        - user3
    tasks:
      - name: "with_items "
        debug:
          msg: "{{  item }} "
        with_items:  "{{  data   }}"

[root@ansible opt]# ansible-playbook  site1.yaml    
[WARNING]: While constructing a mapping from /opt/site1.yaml, line 2, column 5, found a duplicate dict key
(tasks). Using last defined value only.

PLAY [httpd] **************************************************************************************************

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

TASK [with_items] *********************************************************************************************
ok: [httpd] => (item=user1) => {
    "msg": "user1 "
}
ok: [httpd] => (item=user2) => {
    "msg": "user2 "
}
ok: [httpd] => (item=user3) => {
    "msg": "user3 "
}

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   



1.4 将Register变量与Loop一起使用

register关键字也可以捕获循环任务的输出。以下代码片段显示了循环任务中register变量的结构:

[root@ansible opt]# vim site1.yaml 

---
  - name: Loop Register Test
    hosts: httpd
    gather_facts: no
    tasks:

      - name: Looping Echo Task
        shell: "echo  You are the best  {{ item  }}  "
        loop:
          - one
          - two
        register: echo_results


      - name: Show echo_results variable
        debug:
          var: echo_results

[root@ansible opt]# ansible-playbook  site1.yaml    

PLAY [Loop Register Test] *************************************************************************************

TASK [Looping Echo Task] **************************************************************************************
changed: [httpd] => (item=one)
changed: [httpd] => (item=two)

TASK [Show echo_results variable] *****************************************************************************
ok: [httpd] => {
    "echo_results": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_facts": {
                    "discovered_interpreter_python": "/usr/libexec/platform-python"
                },
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo  You are the best  one  ",
                "delta": "0:00:00.002302",
                "end": "2021-07-25 18:45:54.261775",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo  You are the best  one  ",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "one",
                "rc": 0,
                "start": "2021-07-25 18:45:54.259473",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "You are the best one",
                "stdout_lines": [
                    "You are the best one"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo  You are the best  two  ",
                "delta": "0:00:00.002334",
                "end": "2021-07-25 18:45:54.532415",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo  You are the best  two  ",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "two",
                "rc": 0,
                "start": "2021-07-25 18:45:54.530081",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "You are the best two",
                "stdout_lines": [
                    "You are the best two"
                ]
            }
        ]
    }
}

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


在上面的例子中,results键包含一个列表。在下面,修改了playbook,使第二个任务迭代此列表:

[root@ansible opt]# vim site1.yaml 

---
  - name: Loop Register Test
    hosts: httpd
    gather_facts: no
    tasks:

      - name: Looping Echo Task
        shell: "echo  You are the best  {{ item  }}  "
        loop:
          - one
          - two
        register: echo_results


      - name: Show echo_results variable
        debug:
           msg: "STDOUT from previous task: {{ item.stdout }}"
        loop: "{{ echo_results['results'] }}"


[root@ansible opt]# ansible-playbook  site1.yaml    

PLAY [Loop Register Test] *************************************************************************************

TASK [Looping Echo Task] **************************************************************************************
changed: [httpd] => (item=one)
changed: [httpd] => (item=two)

TASK [Show echo_results variable] *****************************************************************************
ok: [httpd] => (item={'cmd': 'echo  You are the best  one  ', 'stdout': 'You are the best one', 'stderr': '', 'rc': 0, 'start': '2021-07-25 18:48:22.158506', 'end': '2021-07-25 18:48:22.160898', 'delta': '0:00:00.002392', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'echo  You are the best  one  ', '_uses_shell': True, 'warn': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['You are the best one'], 'stderr_lines': [], 'ansible_facts': {'discovered_interpreter_python': '/usr/libexec/platform-python'}, 'failed': False, 'item': 'one', 'ansible_loop_var': 'item'}) => {
    "msg": "STDOUT from previous task: You are the best one"
}
ok: [httpd] => (item={'cmd': 'echo  You are the best  two  ', 'stdout': 'You are the best two', 'stderr': '', 'rc': 0, 'start': '2021-07-25 18:48:22.412420', 'end': '2021-07-25 18:48:22.415124', 'delta': '0:00:00.002704', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'echo  You are the best  two  ', '_uses_shell': True, 'warn': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['You are the best two'], 'stderr_lines': [], 'failed': False, 'item': 'two', 'ansible_loop_var': 'item'}) => {
    "msg": "STDOUT from previous task: You are the best two"
}

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


2. 有条件地运行任务

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

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

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

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

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

可以测试的一个最简单条件是某一布尔变量是True还是False。以下示例中的when语句导致任务仅在run_my_task为True时运行:

[root@ansible opt]# vim site1.yaml 

---
  - name: Simple Boolean Task Demo
    hosts: httpd
    vars:
      run_my_task: True

    tasks:
      - name: httpd package is installed
        yum:
          name: httpd
        when: run_my_task

[root@ansible opt]# ansible-playbook site1.yaml 

PLAY [Simple Boolean Task Demo] *******************************************************************************

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

TASK [httpd package is installed] *****************************************************************************
ok: [httpd]

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

以下示例测试my_service变量是否具有值。若有值,则将my_service的值用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。

[root@ansible opt]# vim site1.yaml 

---
  - name: Simple Boolean Task Demo
    hosts: httpd
    vars:
      my_service: httpd

    tasks:
      - name:   "{{ my_service }} package is installed "
        yum:
          name: "{{ my_service }} "
        when: my_service is defined


[root@ansible opt]# ansible-playbook site1.yaml 

PLAY [Simple Boolean Task Demo] *******************************************************************************

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

TASK [httpd package is installed] *****************************************************************************
ok: [httpd]

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


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

操作示例
等于(值为字符串)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的求值为Truememory_available
布尔变量是False。0、False或no的求值为Falsenot memory_available
第一个变量的值存在,作为第二个变量的列表中的值ansible_distribution in supported_distros

上表中的最后一个条目初看起来有些令人困惑。下例演示了它的工作原理。

在示例中,ansible_distribution变量是在Gathering Facts任务期间确定的事实,用于标识托管主机的操作系统分支。变量supported_distrosplaybook创建,包含该playbook支持的操作系统分发列表。如果ansible_distribution的值在supported_distros列表中,则条件通过且任务运行。

---
- name: Demonstrale the "in" keyword
  hosts: httpd
  gather_facts: yes
  vars:
    supported_distros:
      - RedHat
      - Fedora
  tasks:
    - name: Install httpd using yum, where supported
      yum:
        name: httpd
        state: present
      when: ansible_distribution in supported_distros

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

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

2.2 测试多个条件

一个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" and
    ansible_distribution_major_version == "7" )
  or
  ( ansible_distribution == "Fedora" and
    ansible_distribution_major_version == "28" )

3. 组合循环和有条件任务

循环和条件可以组合使用。

在下例中,yum模块将安装mariadb-server软件包,只要/上挂载的文件系统具有超过300MB的可用空间。ansible_mounts事实是一组字典,各自代表一个已挂载文件系统的相关事实。循环迭代列表中每一字典,只有找到了代表两个条件都为真的已挂载文件系统的字典时,条件语句才得到满足。

[root@ansible ~]# vim /opt/playbook/test.yaml

---
- name: 1
  hosts: httpd
  tasks:
    - name: install
      yum:
        name: mariadb
        state: latest
      loop: "{{ ansible_mounts }}"
      when: item.mount == "/" and item.size_available > 200000000

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [1] ******************************************************************************************************

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

TASK [install] ************************************************************************************************
ok: [httpd] => (item={'mount': '/', 'device': '/dev/mapper/rhel-root', 'fstype': 'xfs', 'options': 'rw,relatime,attr2,inode64,noquota', 'size_total': 39696474112, 'size_available': 37353590784, 'block_size': 4096, 'block_total': 9691522, 'block_available': 9119529, 'block_used': 571993, 'inode_total': 19392512, 'inode_available': 19343635, 'inode_used': 48877, 'uuid': '2afc014c-0a72-4330-8078-bc5fde7f1ca5'})
skipping: [httpd] => (item={'mount': '/home', 'device': '/dev/mapper/rhel-home', 'fstype': 'xfs', 'options': 'rw,relatime,attr2,inode64,noquota', 'size_total': 19379781632, 'size_available': 19210797056, 'block_size': 4096, 'block_total': 4731392, 'block_available': 4690136, 'block_used': 41256, 'inode_total': 9467904, 'inode_available': 9467893, 'inode_used': 11, 'uuid': '43b690cc-b51e-4f2e-b1e7-5521c21c4b1a'}) 
skipping: [httpd] => (item={'mount': '/boot', 'device': '/dev/nvme0n1p1', 'fstype': 'xfs', 'options': 'rw,relatime,attr2,inode64,noquota', 'size_total': 1063256064, 'size_available': 876265472, 'block_size': 4096, 'block_total': 259584, 'block_available': 213932, 'block_used': 45652, 'inode_total': 524288, 'inode_available': 523987, 'inode_used': 301, 'uuid': 'dbc3ec58-5bd7-4d97-a149-511915913407'}) 

PLAY RECAP ****************************************************************************************************
httpd                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

对某个任务结合使用when和loop时,将对每个项检查when语句。

下面是组合使用条件和注册变量的另一个示例。

[root@ansible ~]# vim /opt/playbook/test.yaml
---
- name: 1
  hosts: httpd
  tasks:
    - name: install mariadb-server
      yum:
        name: mariadb-server
        state: present
      register: result

    - name: install mariadb
      yum:
        name: mariadb
        state: latest
      when: result.rc == 0

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [1] ******************************************************************************************************

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

TASK [install mariadb-server] *********************************************************************************
changed: [httpd]

TASK [install mariadb] ****************************************************************************************
ok: [httpd]

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

二、实施处理程序

1. ansible处理程序

Ansible模块设计为具有幂等性。这表示,在正确编写的playbook中,playbook及其任务可以运行多次而不会改变受管主机,除非需要进行更改使受管主机进入所需的状态。

但在时候,在任务确实更改系统时,可能需要运行进一步的任务。例如,更改服务配置文件时可能要求重新加载该服务以便使其更改的配置生效。

处理程序是响应由其他任务触发的通知的任务。仅当任务在受管主机上更改了某些内容时,任务才通知其处理程序。每个处理程序具有全局唯一的名称,在playbook中任务块的末尾触发。如果没有任务通过名称通知处理程序,处理程序就不会运行。如果一个或多个任务通知处理程序,处理程序就会在play中的所有其他任务完成后运行一次。因为处理程序就是任务,所以可以在处理程序中使用他们将用于任何其他任务的模块。通常而言,处理程序被用于重新引导主机和重启服务。

处理程序可视为非活动任务,只有在使用notify语句显式调用时才会被触发。在下列代码片段中,只有配置文件更新并且通知了该任务,restart apache处理程序才会重启Apache服务器:

[root@ansible ~]# vim /opt/playbook/test.yaml
---
- hosts: httpd
  tasks:
    - name:
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "Listen "
        line: "Listen 8080"

    - name:
      service:
        name: httpd
        state: restarted

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 


PLAY [httpd] *********************************************************

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

TASK [lineinfile] **************************************************************
changed: [httpd]

TASK [service] *****************************************************************
changed: [httpd]

PLAY RECAP *********************************************************************
httpd            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

一个任务可以在其notify部分中调用多个处理程序。Ansible将notify语句视为数组,并且迭代处理程序名称:


[root@ansible ~]# vim /opt/playbook/test.yaml
---
- hosts: httpd
  tasks:
    - name:
      file: 
        path: /opt/123
        state: directory
      notify: h1

    - name:
      file:
        path: /opt/456
        state: directory
      notify: h2

  handlers:
    - name: h1
      file: 
        path: /opt/abc
        state: touch
    - name: h2
      file:
        path: /opt/def
        state: touch

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] *********************************************************

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

TASK [file] ********************************************************************
changed: [httpd]

TASK [file] ********************************************************************
changed: [httpd]

RUNNING HANDLER [h1] ***********************************************************
changed: [httpd]

RUNNING HANDLER [h2] ***********************************************************
changed: [httpd]

PLAY RECAP *********************************************************************
httpd            : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

2. 使用处理程序的好处

使用处理程序时需要牢记几个重要事项:

  • 处理程序始终按照play的handlers部分指定的顺序运行。它们不按在任务中由notify语句列出的顺序运行,或按任务通知它们的顺序运行。
  • 处理程序通常在相关play中的所有其他任务完成后运行。playbook的tasks部分中某一任务调用的处理程序,将等到tasks下的所有任务都已处理后才会运行。
  • 处理程序名称存在于各play命名空间中。如果两个处理程序被错误地给予相同的名称,则仅会运行一个。
  • 即使有多个任务通知处理程序,该处理程序依然仅运行一次。如果没有任务通知处理程序,它就不会运行。
  • 如果包含notify语句的任务没有报告changed结果(例如,软件包已安装并且任务报告ok),则处理程序不会获得通知。处理程序将被跳过,直到有其他任务通知它。只有相关任务报告了changed状态,Ansible才会通知处理程序。

处理程序用于在任务对受管主机进行更改时执行额外操作。它们不应用作正常任务的替代。

三、处理任务失败

1. 管理play中的任务错误

Ansible评估任务的返回代码,从而确定任务是成功还是失败。通常而言,当任务失败时,Ansible将立即在该主机上中止play的其余部分并且跳过所有后续任务。

但有些时候,可能希望即使在任务失败时也继续执行play。例如,或许预期待定任务有可能会失败,并且希望通过有条件地运行某项其他任务来修复。

Ansible有多种功能可用于管理任务错误。

2. 忽略任务失败

默认情况下,任务失败时play会中止。不过,可以通过忽略失败的任务来覆盖此行为。可以在任务中使用ignore_errors关键字来实现此目的。

下列代码片段演示了如何在任务中使用ignore_errors,以便在任务失败时也继续在主机上执行playbook。例如,如果notapkg软件包不存在,则yum模块将失败,但若将ignore_errors设为yes,则执行将继续。

[root@ansible ~]# vim /opt/playbook/test.yaml
- hosts: httpd
  tasks:
    - name:
      yum:  
        name: www //随便取得名字
        state: present
      ignore_errors: yes //忽略错误

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] *********************************************************

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

TASK [yum] *********************************************************************
fatal: [httpd]: FAILED! => {"changed": false, "failures": ["No package zzz available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring

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

3. 任务失败后强制执行处理程序

通常而言,如果任务失败并且play在该主机上中止,则收到play中早前任务通知的处理程序将不会运行。如果在play中设置force_handlers: yes关键字,则即使play因为后续任务失败而中止也会调用被通知的处理程序。

下列代码片段演示了如何在play中使用force_handlers关键字,以便在任务失败时也强制执行相应的处理程序:

[root@ansible playbook]# vim /opt/playbook/test.yaml

---
- hosts: httpd
  force_handlers: yes
  tasks:
    - name: touch 123
      shell: touch 123
      notify: restart httpd

    - name: restart php
      service:
        name: php
        state: started


  handlers:
    - name:  restart httpd
      service:
        name: httpd
        state: restarted


[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] **************************************************************************************************

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

TASK [touch 123] **********************************************************************************************
[WARNING]: Consider using the file module with state=touch rather than running 'touch'.  If you need to use
command because file is insufficient you can add 'warn: false' to this command task or set
'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [httpd]

TASK [restart php] ********************************************************************************************
fatal: [httpd]: FAILED! => {"changed": false, "msg": "Could not find the requested service php: host"}

RUNNING HANDLER [restart httpd] *******************************************************************************
changed: [httpd]

PLAY RECAP ****************************************************************************************************
httpd                      : ok=3    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   



请记住,处理程序会在任务报告changed结果时获得通知,而在任务报告ok或failed结果时不会获得通知。

4. 指定任务失败条件

可以在任务中使用failed_when关键字来指定表示任务已失败的条件。这通常与命令模块搭配使用,这些模块可能成功执行了某一命令,但命令的输出可能指示了失败。

例如,可以运行输出错误消息的脚本,并使用该消息定义任务的失败状态。下列代码片段演示了如何在任务中使用failed_when关键字:


tasks:
  - name: Run user creation script
    shell: /usr/local/bin/create_users.sh
    register: command_result
    failed_when: "'Password missing' in command_result.stdout"

fail模块也可用于强制任务失败。上面的场景也可以编写为两个任务:

[root@ansible playbook]# vim /opt/playbook/test.yaml
---
- hosts: httpd
  tasks:
    - name:
      shell: cat 123
      register: result

    - name:
      fail: 
        msg: "GG"
      when: "'没有那个文件或目录' in result.stdout"
      

[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] *********************************************************

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

TASK [script] ******************************************************************
changed: [httpd]

TASK [fail] ********************************************************************
fatal: [httpd]: FAILED! => {"changed": false, "msg": "GG"} //错误变成了GG

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

5. 指定何时任务报告 “Changed” 结果

当任务对托管主机进行了更改时,会报告 changed 状态并通知处理程序。如果任务不需要进行更改,则会报告ok并且不通知处理程序。

changed_when关键字可用于控制任务在何时报告它已进行了更改。例如,下一示例中的shell模块将用于获取供后续任务使用的Kerberos凭据。它通常会在运行时始终报告changed。为抵制这种更改,应设置changed_when: false,以便它仅报告ok或failed。

---
- hosts: httpd
  tasks:
    - name:
      shell: echo "789" > 123
      register: result
      changed_when: "'789' in result.stdout"
      notify:
        - restart
          
  handlers:
    - name: restart
      service:
        name: httpd
        state: restarted
        
[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] *********************************************************

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

TASK [shell] ******************************************************************
changed: [httpd]

RUNNING HANDLER [restart] ******************************************************
changed: [httpd]

PLAY RECAP *********************************************************************
httpd            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

6. Ansible块和错误处理

在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式。例如,任务块可以含有when关键字,以将某一条件应用到多个任务

通过块,也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行。总结:

  • block:定义要运行的主要任务
  • rescue:定义要在block子句中定义的任务失败时运行的任务
  • always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败

---
- hosts: httpd
  tasks:
    - name:
      block:
        - name: block
          shell: echo 'block'
          
          
      rescue:
        - name: rescue
          shell: echo 'rescue'
          
      always:
        - name: always
          shell: echo 'always'


[root@ansible opt]# ansible-playbook /opt/playbook/test.yaml 

PLAY [httpd] *********************************************************

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

TASK [block] *******************************************************************
changed: [httpd]

TASK [always] ******************************************************************
changed: [httpd]

PLAY RECAP *********************************************************************
httpd            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值