当前位置: 首页 > news >正文

【运维进阶】实施任务控制

实施任务控制

在 Ansible 中,“实施任务控制” 通常指的是对任务执行流程的控制,比如:

条件执行(when)

循环执行(with_items / loop)

错误处理(block / rescue / ignore_errors)

任务依赖(tags / handlers)

异步执行(async / poll)

任务注册与判断(register)

实验环境

[lth@controller ~ 20:55:49]$ mkdir web && cd web[lth@controller web 20:56:02]$ cat > ansible.cfg <<'EOF'
> [defaults]
> remote_user = lth
> inventory = ./inventory
> 
> [privilege_escalation]
> become = True
> become_user = root
> become_method = sudo
> become_ask_pass = False
> EOF
[lth@controller web 20:56:47]$ cat > inventory <<'EOF'
> controller
> node1
> node2
> node3
> node4
> EOF

编写循环任务

利用循环,管理员无需编写多个使用同一模块的任务。例如,确保存在五个用户,不需要编写五个任务,而是只需编写一个任务来对含有五个用户的列表迭代。

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

本节介绍迭代项目列表的简单循环。

简单循环

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

示例:

---
- name: add several usershosts: node1gather_facts: notasks:- name: add user janeuser:name: "jane"groups: "wheel"state: present- name: add user joeuser:name: "joe"state: presentgroups: "wheel"

使用loop循环改写:

- name: test loophosts: node1gather_facts: notasks:- name: add usersuser:name: "{{ item }}"groups: "wheel"state: presentloop:- jane- joe

或者

- name: test loophosts: node1gather_facts: notasks:- name: add usersuser:name: "{{ item }}"groups: "wheel"state: presentloop: "{{ users }}"vars:users:- jane- joe
复杂循环
[lth@controller web 21:46:47]$ cat playbook.yml # yaml格式起始行,一般不省略---# Playbook中第一个play# play具有属性:name,hosts,become,tasks,缩进一致# name属性,用于简要描述play- name: Enable intranet services# hosts属性,用于定义要在哪个受管理节点执行hosts: node1# tasks属性,用于描述play中任务,属性是列表格式tasks:# 第一个任务# 任务具有属性:涵name和模块名等。# name属性,用于简要描述任务- name: latest version of httpd and firewalld installed# 指明模块名,也就是要执行的任务yum:# 执行要操作的rpm包名称name:# rpm包名称是-开头的列表格式,或者逗号分隔的列表格式- httpd- firewalld# 定义软件包的状态,lastet代表升级为最新版本state: latest# 第二个任务- name: test html page is installed# copy模块,用于将content属性值写入到目标文件copy:content: "Welcome Lth WebSite!\n"dest: /var/www/html/index.html# 第三个任务- name: firewalld enabled and running# service模块,用于启用并启动firewalld服务service:name: "{{item}}"enabled: truestate: startedloop:- httpd- firewalld# 第四个任务# - name: firewalld permits access to httpd service#   # firewalld,用于放行http服务#   firewalld:#     service: http#     permanent: true#     state: enabled#     immediate: yes# 第五个任务- name: httpd enabled and running# service模块,用于启用并启动httpd服务service:name: httpdenabled: truestate: started# Playbook中第二个play,-开头表示列表- name: Test intranet web serverhosts: localhostbecome: notasks:- name: connect to intranet web server# uri模块,用于测试网站是否可以访问uri:url: http://node1return_content: yesstatus_code: 200# yaml格式结束行,一般省略...# 验证
[lth@controller web 21:46:58]$ ansible-playbook playbook.yml PLAY [Enable intranet services] *********************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [latest version of httpd and firewalld installed] **********************************************ok: [node1]TASK [test html page is installed] ******************************************************************changed: [node1]TASK [firewalld enabled and running] ****************************************************************ok: [node1] => (item=httpd)ok: [node1] => (item=firewalld)TASK [httpd enabled and running] ********************************************************************ok: [node1]PLAY [Test intranet web server] *********************************************************************TASK [Gathering Facts] ******************************************************************************ok: [localhost]TASK [connect to intranet web server] ***************************************************************ok: [localhost]PLAY RECAP ******************************************************************************************localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   node1                      : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
循环散列或字典列表

在 Ansible 中,循环处理字典列表或散列可通过loop实现:

  • 对字典列表(如[{key1: val1}, {key2: val2}]),直接用loop: "{{ 列表变量 }}",通过item.key访问各字段。
  • 对嵌套字典(如{key: {subkey: subval}}),先用dict2items过滤器转为[{key: ..., value: ...}]形式,再循环,通过item.keyitem.value.subkey访问。

可结合when条件过滤,或用map提取特定键值,实现批量操作。

在以下示例中,列表中的每个项实际上是散列或字典。

示例中的每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过item.name和item.groups变量来检索。

示例:

- name: test loophosts: node1gather_facts: notasks:- name: add usersuser:name: "{{ item.name }}"groups: "{{ item.groups }}"state: presentloop: - name: janegroups: wheel- name: joegroups: root

改写为:

---
- name: add several usershosts: node1gather_facts: novars:users:- name: janegroups: wheel- name: joegroups: roottasks:- name: add users user:name: "{{ item.name }}"state: presentgroups: "{{ item.groups }}"  loop: "{{ users }}"
Do-Until Loops

Do-Until 循环 是一种 后测试循环结构,意思是:

  • 先执行一次循环体
  • 然后再判断条件
  • 直到条件为真时,循环才会终止

示例:测试 node2,什么时候可以ping通。

[lth@controller web 21:51:20]$ vim playbook.yml 
[lth@controller web 21:51:25]$ cat playbook.yml 
---
- name: test loophosts: node1gather_facts: notasks:- shell: ping -c1 -w 2 node2register: resultuntil: result.rc == 0retries: 20delay: 1# delay 代表两次检测任务是否完成的间隔时间,默认值5秒。
...# 关闭node2测试
[lth@controller web 21:51:36]$ ansible-playbook playbook.yml PLAY [test loop] ************************************************************************************TASK [shell] ****************************************************************************************FAILED - RETRYING: command (20 retries left).FAILED - RETRYING: command (19 retries left).FAILED - RETRYING: command (18 retries left).FAILED - RETRYING: command (17 retries left).# 测试过程中打开node2后
[lth@controller web 21:51:47]$ ansible-playbook playbook.yml PLAY [test loop] ************************************************************************************TASK [shell] ****************************************************************************************FAILED - RETRYING: command (20 retries left).FAILED - RETRYING: command (19 retries left).FAILED - RETRYING: command (18 retries left).FAILED - RETRYING: command (17 retries left).FAILED - RETRYING: command (16 retries left).FAILED - RETRYING: command (15 retries left).FAILED - RETRYING: command (14 retries left).FAILED - RETRYING: command (13 retries left).FAILED - RETRYING: command (12 retries left).FAILED - RETRYING: command (11 retries left).FAILED - RETRYING: command (10 retries left).FAILED - RETRYING: command (9 retries left).FAILED - RETRYING: command (8 retries left).FAILED - RETRYING: command (7 retries left).FAILED - RETRYING: command (6 retries left).FAILED - RETRYING: command (5 retries left).changed: [node1]PLAY RECAP ******************************************************************************************node1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

编写条件任务

Ansible可使用conditionals在符合特定条件时执行任务或play。

例如,管理员可利用条件来区分不同的受管节点,并根据它们所符合的条件来分配功能角色。 Playbook变量、注册的变量和ANSIBLE FACTS都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔值的运算符。

用例:

  1. 定义变量min_memory,判断被管理节点可用内存是否满足该值。
  2. 捕获命令输出,判定task是否执行完成,以便决定是否进行下一步操作。
  3. 被管理节点上收集到的网络facts,判定是否适合哪种绑定(bonding或者trunking)。
  4. 根据CPU的数量决定如何调优web服务器。
  5. Registered变量与预定义的变量对比,判断是否有变化。例如文件的MD5值。
when 语句

ansible playbook 中使用 when 来运行条件任务。

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

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

常见判断
操作示例
等于(值为字符串)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
布尔变量值是1、True或yes的求值为memory_available
布尔变量值是0、False或no的求值为memory_available
memory_available变量值为真,最终结果为not memory_available
第一个变量的值存在,作为第二个变量的列表中的值ansible_distribution in supported_distros
布尔值变量判断

示例:

---
- name: test hosts: node1gather_facts: novars:run_my_task: truetasks:- name: test whendebug:msg: "Hello run my task"when: run_my_task

示例中的when语句导致:任务仅在run_my_task为 true时运行。

变量是否定义判断

变量判断:

  • defined == not undefined 变量定义返回真
  • undefined == not defined 变量未定义返回真
  • none 变量定义了,但是值是空值,返回真

示例1:

[lth@controller web 21:53:17]$ cat playbook.yml 
---
- hosts: node1gather_facts: novars:username: yuxbtasks:- debug:msg: "var: username is defined"when: username is defined
...# 执行
[lth@controller web 21:53:24]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [debug] ****************************************************************************************
ok: [node1] => {"msg": "var: username is defined"
}PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

示例2:判断受管主机是否具有相应设备。

# 看有没有sdb硬盘
[lth@controller web 22:00:06]$ cat playbook.yml 
---
- hosts: node1gather_facts: yesvars:username: yuxbtasks:- debug:msg: "sdb is exist"when: ansible_devices.sdb is defined
...# 执行
[lth@controller web 22:00:10]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [debug] ****************************************************************************************
skipping: [node1]PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0# 看有没有sr0盘
[lth@controller web 22:00:20]$ vim playbook.yml 
[lth@controller web 22:00:23]$ cat playbook.yml 
---
- hosts: node1gather_facts: yesvars:username: yuxbtasks:- debug:msg: "sr0 is exist"when: ansible_devices.sr0 is defined
...# 执行
[lth@controller web 22:00:41]$ ansible-playbook playbook.yml PLAY [node1] ****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [debug] ****************************************************************************************
ok: [node1] => {"msg": "sr0 is exist"
}PLAY RECAP ******************************************************************************************
node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
文件属性判断
  • file:如果路径是一个普通文件,返回真
  • directory:如果路径是一个目录,返回真
  • link:如果路径是一个软连接,返回真
  • mount:如果路径是一个挂载点,返回真
  • exist:如果路径是存在,返回真

示例:

---
- hosts: node1gather_facts: novars:file_name: /etc/hoststasks:- debug:msg: "{{ file_name }} is regular file"when: file_name is file
任务执行结果判断
  • succeeded,通过任务的返回信息判断,任务执行成功返回真。
  • failed,通过任务的返回信息判断,任务执行失败返回真。
  • changed,通过任务的返回信息判断,任务执行状态为changed返回真。
  • skipped,通过任务的返回信息判断,任务没有满足条件跳过执行,返回真。

示例:

---
- hosts: node1gather_facts: novars:doshell: "yes"tasks:- shell: cat /etc/hostsregister: resultignore_errors: truewhen: doshell == "yes"- name: successdebug:msg: successwhen: result is succeeded- name: faileddebug:msg: failedwhen: result is failed- name: changeddebug:msg: changedwhen: result is changed- name: skippeddebug:msg: skipwhen: result is skipped
in 和 not in 判断

用于判断某个值是否存在于一个 字符串列表字典键中

示例1:给用户添加组

[lth@controller web 22:03:18]$ cat playbook.yml 
---
- name: test hosts: node1gather_facts: novars:username: devopssupergroup: wheeltasks:- name: gather user informationshell: id {{ username }}register: result- name: Task run if user is in supergroupsuser:name: "{{ username }}"groups: "{{ supergroup }}"append: yeswhen: supergroup not in result.stdout
...[root@node1 ~ 22:03:23]# useradd devops
[root@node1 ~ 22:03:25]# id devops
uid=1091(devops) gid=1091(devops)=1091(devops)# 执行
[lth@controller web 22:04:22]$ ansible-playbook playbook.yml PLAY [test] *****************************************************************************************TASK [gather user information] **********************************************************************
changed: [node1]TASK [Task run if user is in supergroups] ***********************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1                      : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

**示例二:**根据主机所属组控制输出

[lth@controller web 22:06:18]$ cat inventory 
controller[nodes]
node1
node2
node3
node4[webs]
node1 
node2[dbs]
node3
node4[lth@controller web 22:06:24]$ cat playbook.yml 
---
- name: test hosts: node1 node3gather_facts: notasks:- name: install httpdyum:name: httpdstate: presentwhen: inventory_hostname in groups.webs- name: install mariadbyum:name: mariadb       state: present    when: inventory_hostname in groups.dbs 
...# 执行
[lth@controller web 22:06:33]$ ansible-playbook playbook.yml PLAY [test] *****************************************************************************************TASK [install httpd] ********************************************************************************
skipping: [node3]
ok: [node1]TASK [install mariadb] ******************************************************************************
skipping: [node1]
changed: [node3]PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
node3                      : ok=1    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
loop 和 when 联合
  • loop:用于对一组元素逐个执行任务

  • when:用于对每一个循环项(item)进行条件判断

当 loop 和 when 一起使用时,when 表达式会对 每个 item 单独判断

示例:当 / 文件系统可用空间大于300000000 安装 mariadb-server
**解决方法:**通过 ansible_facts 获取 / 文件系统可用空间

[lth@controller web 22:10:41]$ cat playbook.yml 
---
- name: Combining Loops and Conditional Play hosts: node1tasks:- name: install mariadb-server if enough space on rootyum:name: mariadb-serverstate: latestloop: "{{ ansible_mounts }}"when:- item.mount == "/"- item.size_available > 300000000
...# 执行
[lth@controller web 22:10:41]$ ansible-playbook playbook.yml PLAY [Combining Loops and Conditional Play] *********************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [install mariadb-server if enough space on root] ***********************************************
skipping: [node1] => (item={u'block_used': 35554, u'uuid': u'b5c926d1-b582-436b-95a9-38664b23e619', u'size_total': 1063256064, u'block_total': 259584, u'mount': u'/boot', u'block_available': 224030, u'size_available': 917626880, u'fstype': u'xfs', u'inode_total': 524288, u'options': u'rw,relatime,attr2,inode64,noquota', u'device': u'/dev/sda1', u'inode_used': 326, u'block_size': 4096, u'inode_available': 523962}) 
ok: [node1] => (item={u'block_used': 524536, u'uuid': u'db4466fc-68b8-4fd7-82b1-0b578187dafa', u'size_total': 53660876800, u'block_total': 13100800, u'mount': u'/', u'block_available': 12576264, u'size_available': 51512377344, u'fstype': u'xfs', u'inode_total': 26214400, u'options': u'rw,relatime,attr2,inode64,noquota', u'device': u'/dev/mapper/centos-root', u'inode_used': 34139, u'block_size': 4096, u'inode_available': 26180261})PLAY RECAP ******************************************************************************************
node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ansible Handlers

Ansible Handlers 功能

功能说明

  • Handlers 是为 “有变更时才执行的操作” 而设计的

  • 常用于在某些操作完成后执行额外的步骤,例如:

    • 修改配置文件后重启服务
    • 更新程序后重载守护进程
    • 改变权限后重新应用策略

工作原理

  1. 任务执行后发生变更(changed)

  2. 该任务中使用 notify 通知某个 handler

  3. Handler 在 playbook 的最后统一执行一次(除非用 meta: flush_handlers 强制提前执行)。

示例 :在远程主机 node1部署 Web 服务器

[lth@controller web 22:15:35]$ cat playbook.yml 
---
- name: deploy web serverhosts: node1tasks:- name: install packagesyum:name: httpdstate: presentnotify:- enable and restart apache- name: install httpd-manualyum:name: httpd-manualstate: presentnotify:- enable and restart apache- debug: msg: last task in taskshandlers:- name: enable and restart apacheservice:name: httpdstate: restartedenabled: yes
...# 执行
[lth@controller web 22:15:49]$ ansible-playbook playbook.yml PLAY [deploy web server] ****************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [install packages] *****************************************************************************
changed: [node1]TASK [install httpd-manual] *************************************************************************
changed: [node1]TASK [debug] ****************************************************************************************
ok: [node1] => {"msg": "last task in tasks"
}RUNNING HANDLER [enable and restart apache] *********************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1                      : ok=5    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

处理 Errors

Errors 介绍

在 Ansible 中,错误(Errors)指执行 Playbook 或任务时出现的异常情况,可能由多种原因导致,会中断正常执行流程。了解错误的类型、触发原因及处理方式,对确保自动化任务稳定稳运行至关重要。

示例:

# 环境
[root@node1 ~ 22:19:08]# cp /etc/hosts /etc/myhost
[root@node2 ~ 22:19:11]# rm -f /etc/myhost[lth@controller web 22:19:21]$ vim playbook.yml 
[lth@controller web 22:19:36]$ cat playbook.yml 
---
- name: testhosts: node1,node2tasks:- name: show /etc/myhostsshell: cat /etc/myhosts- name: echo enddebug:msg: echo end# 执行
[lth@controller web 22:19:38]$ ansible-playbook playbook.yml PLAY [test] *****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node2]
ok: [node1]TASK [show /etc/myhosts] ****************************************************************************
fatal: [node2]: FAILED! => {"changed": true, "cmd": "cat /etc/myhosts", "delta": "0:00:00.003767", "end": "2025-08-17 22:19:38.212666", "msg": "non-zero return code", "rc": 1, "start": "2025-08-17 22:19:38.208899", "stderr": "cat: /etc/myhosts: 没有那个文件或目录", "stderr_lines": ["cat: /etc/myhosts: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
fatal: [node1]: FAILED! => {"changed": true, "cmd": "cat /etc/myhosts", "delta": "0:00:00.003663", "end": "2025-08-17 22:19:45.235356", "msg": "non-zero return code", "rc": 1, "start": "2025-08-17 22:19:45.231693", "stderr": "cat: /etc/myhosts: 没有那个文件或目录", "stderr_lines": ["cat: /etc/myhosts: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
node2                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
ignore_errors

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

ignore_errors可以定义在以下位置:

  • 定义在 play 中,则play中所有任务忽略错误。
  • 定义在 task 中,则特定task忽略错误。

示例:

[lth@controller web 22:21:51]$ vim playbook.yml 
[lth@controller web 22:22:01]$ cat playbook.yml 
---
- name: testhosts: node1tasks:- name: install a not exist packageyum:name: notexitpackagestate: presentignore_errors: yesregister: result- name: debug install resultdebug:msg: notexitpackage is not exitwhen: result is failed# 执行
[lth@controller web 22:22:12]$ ansible-playbook playbook.yml PLAY [test] *****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [install a not exist package] ******************************************************************
fatal: [node1]: FAILED! => {"changed": false, "msg": "No package matching 'notexitpackage' found available, installed or updated", "rc": 126, "results": ["No package matching 'notexitpackage' found available, installed or updated"]}
...ignoringTASK [debug install result] *************************************************************************
ok: [node1] => {"msg": "notexitpackage is not exit"
}PLAY RECAP ******************************************************************************************
node1                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1 
force_handlers

play中使用关键字 force_handlers,控制当play中某个任务执行失败后是否继续执行之前定义的notify,可用值yes或no(也可以是true或false),默认no(false)。

示例:

[lth@controller web 22:23:38]$ vim playbook.yml 
[lth@controller web 22:23:43]$ cat playbook.yml 
---
- name: testhosts: node1force_handlers: yestasks:- name: a task which always notifies its handlercommand: /bin/truenotify: restart the sshd- name:  fails because the package doesn't existyum:name: notexistpkgstate: latesthandlers:- name: restart the sshdservice:name: sshdstate: restarted[lth@controller web 22:23:59]$  ansible-playbook playbook.yml PLAY [test] *****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [a task which always notifies its handler] *****************************************************
changed: [node1]TASK [fails because the package doesn't exist] ******************************************************
fatal: [node1]: FAILED! => {"changed": false, "msg": "No package matching 'notexistpkg' found available, installed or updated", "rc": 126, "results": ["No package matching 'notexistpkg' found available, installed or updated"]}RUNNING HANDLER [restart the sshd] ******************************************************************
changed: [node1]PLAY RECAP ******************************************************************************************
node1                      : ok=3    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0        
fail 模块

fail 模块,执行该任务,任务必定 failed。

示例:

[lth@controller web 22:25:19]$ vim playbook.yml 
[lth@controller web 22:25:26]$ cat playbook.yml 
---
- name: test fail modulehosts: node1gather_facts: notasks:- debug:msg: task1- fail:- debug:msg: task3[lth@controller web 22:25:37]$ ansible-playbook playbook.yml PLAY [test fail module] *****************************************************************************TASK [debug] ****************************************************************************************
ok: [node1] => {"msg": "task1"
}TASK [fail] *****************************************************************************************
fatal: [node1]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0        

提示:fail模块本身也可以配置when判断,实现说明情况下任务是失败的。

failed_when

指明什么条件下,判定任务执行失败。

示例:

[lth@controller web 22:27:07]$ vim playbook.yml 
[lth@controller web 22:27:10]$ cat playbook.yml 
- name: test failed_whenhosts: node1tasks:- shell: /root/adduserregister: command_resultfailed_when: "'failed' in command_result.stdout"

环境准备:

[root@node1 ~ 22:27:16]# cat /root/adduser 
#!/bin/bash
useradd devops &> /dev/null
if [ $? -eq 0 ];thenecho add user devops success
elseecho add user devops failed
fi
[root@node1 ~ 22:27:24]# chmod +x /root/adduser 

以上示例:

  • 当devops用户不存在时,shell模块跳过执行。
  • 当devops用户存在时,shell模块执行失败。

以上示例可改写为fail模块和when语句联合使用:

- name: test fail modulehosts: node1tasks:- shell: /root/adduserregister: command_result- fail:msg: "add user devops failed"when: "'failed' in command_result.stdout"

验证:

[lth@controller web 22:29:04]$ ansible-playbook playbook.yml PLAY [test failed_when] *****************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node1]TASK [shell] ****************************************************************************************
fatal: [node1]: FAILED! => {"changed": true, "cmd": "/root/adduser", "delta": "0:00:00.005987", "end": "2025-08-17 22:29:04.903010", "failed_when_result": true, "rc": 0, "start": "2025-08-17 22:29:07.897023", "stderr": "", "stderr_lines": [], "stdout": "add user devops failed", "stdout_lines": ["add user devops failed"]}PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
changed_when

指明什么条件下,判定任务执行结果为changed。

示例1:

[lth@controller web 22:35:15]$ vim playbook.yml 
[lth@controller web 22:35:23]$ cat playbook.yml 
---
- name: changed_whenhosts: node1tasks:- name: upgrade-databaseshell: /usr/local/bin/upgrade-databaseregister: resultchanged_when: "'Success' in result.stdout"notify:- restart_databasehandlers:- name: restart_databaseservice:name: mariadbstate: restarted

环境准备:

[root@node1 ~ 22:35:29]# yum install -y mariadb-server
[root@node1 ~ 22:35:34]# systemctl enable mariadb --now
[root@node1 ~ 22:35:48]# vim /usr/local/bin/upgrade-database
#!/bin/bash
mysql -e 'create user lth@"%" identified by "redhat";' && mysql -e 'GRANT ALL PRIVILEGES on *.* TO lth@"%";' && echo update database Success
[root@node1 ~ 22:35:55]# chmod +x /usr/local/bin/upgrade-database

对于command模块和shell模块,只要命令正常执行,结果状态通常都是changed。可以通过返回码和输出结果来判定它们是否做出更改。

关键字 changed_when: false ,让任务结果状态不为changed,只能报告为ok或failed。

示例2:

[lth@controller web 22:38:33]$ vim playbook.yml 
[lth@controller web 22:38:26]$ cat playbook.yml 
---
- name: Test Whenhosts: node1gather_facts: notasks:- name: test changed_whenshell: cat /etc/redhat-releasechanged_when: false[lth@controller web 22:38:47]$ ansible-playbook playbook.yml PLAY [Test When] ************************************************************************************TASK [test changed_when] ****************************************************************************
ok: [node1]PLAY RECAP ******************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Ansible block

多个任务作为block子条目,block作为多个任务整体。

Ansible 的 block 是一种语法结构,可以让你将多个任务组合在一起,并对这些任务统一地使用:

  • when 条件判断
  • become 权限提升
  • rescue 错误处理
  • always 最终操作(无论成功或失败都执行)

示例:

- name: block examplehosts: node1tasks:- block:- debug:msg: task1- debug:msg: task2when: ansible_distribution == "RedHat"

这里的when判断结果影响block中所有任务,关键字when与block对齐。

对齐语法二:

- name: block examplehosts: node1tasks:- block:- debug:msg: task1- debug:msg: task2when: ansible_distribution == "RedHat"

blocks 还可以与 rescue和always一起使用:

  • block:定义主要任务。
  • rescue:block中任务执行失败后,就会执行rescue中定义的任务。
  • always:无论block和rescue中任务执行是否成功和失败,都会执行的任务。

block配置when指令,同样适用rescue和always。

**示例1:**更新数据库,更新失败要回滚,最后要重启数据库。

- name: upgrade dbblock:- name: upgrade the databaseshell:cmd: /usr/local/lib/upgrade-databaserescue:# 数据库更新失败就回滚- name: revert the database upgradeshell:cmd: /usr/local/lib/revert-databasealways:# 重启数据库- name: always restart the databaseservice:name: mariadbstate: restartedwhen: ansible_distribution == "RedHat"   # 此处的when语句将作用于block、rescue和always子句

**示例2:**在所有受管节点上创建符合以下要求的逻辑卷:

  • 在research卷组中创建逻辑卷:

    • 逻辑卷名称为data
    • 逻辑卷大小为4000MiB
    • 使用ext4文件系统格式化逻辑卷
    • 将逻辑卷挂载到/data目录
    • 如果无法创建请求的逻辑卷大小,应显示错误信息:Could not create logical volume of that size 并且应改为使用大小800MiB。
  • 如果卷组research不存在,应显示错误信息:Volume does not exist

环境准备

# node1:添加一块 20G sata 硬盘
[root@node1 ~ 22:44:43]# vgcreate research /dev/sdbPhysical volume "/dev/sdb" successfully created.Volume group "research" successfully created# node2:添加一块 20G sata 硬盘
[root@node2 ~ 22:44:54]# parted /dev/sdb unit MiB mklabel msdos
信息: You may need to update /etc/fstab.[root@node2 ~ 22:45:13]# parted /dev/sdb unit MiB mkpart primary 1 1025   
信息: You may need to update /etc/fstab.[root@node2 ~ 22:45:24]# vgcreate research /dev/sdb1             Physical volume "/dev/sdb1" successfully created.Volume group "research" successfully created

playbook.yaml内容如下:

---
- name: create and use lvhosts: alltasks:- block:- name: Create a logical volume of 4000mlvol:vg: researchlv: datasize: 4000rescue:- debug:msg: Could not create logical volume of that size- name: Create a logical volume of 800mlvol:vg: researchlv: datasize: 800always:- name: Create a ext4filesystem:fstype: ext4dev: /dev/research/data- name: create directory /datafile:path: /datastate: directory- name: Mount /dev/research/datamount:path: /datasrc: /dev/research/datafstype: ext4state: mountedwhen: ansible_lvm.vgs.research is defined- name: Volume group does not existdebug:msg: Volume group does not existwhen: ansible_lvm.vgs.research is not defined

验证:

[lth@controller web 22:49:37]$ ansible-playbook playbook.yml PLAY [create logical volume] ************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [node2]
ok: [controller]
ok: [node3]
ok: [node1]
ok: [node4]TASK [Create a logical volume of 4000m] *************************************************************
skipping: [controller]
skipping: [node3]
skipping: [node4]
[WARNING]: The value 4000 (type int) in a string field was converted to u'4000' (type string). If
this does not look like what you expect, quote the entire value to ensure it does not change.
fatal: [node2]: FAILED! => {"changed": false, "err": "  Volume group \"research\" has insufficient free space (255 extents): 1000 required.\n", "msg": "Creating logical volume 'data' failed", "rc": 5}
changed: [node1]TASK [Could not create logical volume of that size] *************************************************
ok: [node2] => {"msg": "Could not create logical volume of that size"
}TASK [Create a logical volume of 800m] **************************************************************
[WARNING]: The value 800 (type int) in a string field was converted to u'800' (type string). If this
does not look like what you expect, quote the entire value to ensure it does not change.
changed: [node2]TASK [Create a ext4 filesystem] *********************************************************************
skipping: [controller]
skipping: [node3]
skipping: [node4]
changed: [node2]
changed: [node1]TASK [Create a directory] ***************************************************************************
skipping: [controller]
skipping: [node3]
skipping: [node4]
changed: [node2]
changed: [node1]TASK [Mount up device] ******************************************************************************
skipping: [controller]
skipping: [node3]
skipping: [node4]
changed: [node2]
changed: [node1]TASK [Volume does not exist] ************************************************************************
ok: [controller] => {"msg": "Volume does not exist"
}
skipping: [node1]
skipping: [node2]
ok: [node3] => {"msg": "Volume does not exist"
}
ok: [node4] => {"msg": "Volume does not exist"
}PLAY RECAP ******************************************************************************************
controller                 : ok=2    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0   
node1                      : ok=5    changed=4    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
node2                      : ok=6    changed=4    unreachable=0    failed=0    skipped=1    rescued=1    ignored=0   
node3                      : ok=2    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0   
node4                      : ok=2    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0# 验证挂载点
[root@node1 ~ 22:51:20]# df /data/
文件系统                    1K-块  已用    可用 已用% 挂载点
/dev/mapper/research-data 3966144 15992 3728968    1% /data
[root@node1 ~ 22:51:47]# df /data -h
文件系统                   容量  已用  可用 已用% 挂载点
/dev/mapper/research-data  3.8G   16M  3.6G    1% /data
http://www.lryc.cn/news/623409.html

相关文章:

  • C语言---第一个C语言程序
  • 12.web api 3
  • 网格布局 CSS Grid
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day6
  • k8s集群搭建一主多从的jenkins集群
  • 锂电池SOH预测 | Matlab基于KPCA-PLO-Transformer-LSTM的的锂电池健康状态估计(锂电池SOH预测),附锂电池最新文章汇集
  • 网络原理与编程实战:从 TCP/IP 到 HTTP/HTTPS
  • 《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
  • KingbaseES:一体化架构与多层防护,支撑业务的持续稳定运行与扩展
  • Manus AI 与多语言手写识别技术剖析
  • 整体设计 之“凝聚式中心点”原型 --整除:智能合约和DBMS的深层联合 之1
  • 第三十九天(WebPack构建打包Mode映射DevTool源码泄漏识别还原)
  • 大模型提示词(Prompt)终极指南:从原理到实战,让AI输出质量提升300%
  • 朝花夕拾(四) --------python中的os库全指南
  • 《算法导论》第 27 章 - 多线程算法
  • -nostartfiles参数官方解释,含义
  • 【远程桌面】从RustDesk服务器看UDP对比WebRTC
  • Rust:实现仅通过索引(序数)导出 DLL 函数的功能
  • Node.js导入MongoDB具体操作
  • Kafka 面试题及详细答案100道(23-35)-- 核心机制2
  • 【前端面试题】前端面试知识点(第三十一题到第六十一题)
  • 计算机毕设选题推荐-基于大数据的全面皮肤病症状数据可视化分析系统【Hadoop、spark、python】
  • day42_2025-08-16
  • ArrayList的contains问题
  • 大模拟 Major
  • 扫地机器人(2025蓝桥杯省A组 H题)
  • 【P14 3-6 】OpenCV Python——视频加载、摄像头调用、视频基本信息获取(宽、高、帧率、总帧数),视频保存在指定位置
  • LeetCode 刷题【43. 字符串相乘】
  • 安卓11 12系统修改定制化_____修改运营商版本安装特定应用时的默认规则
  • 美国服务器环境下Windows容器工作负载基于指标的自动扩缩