5、ansible的流程控制
5.1 循环
在 ansible 的 task 中,如果要重复执行相同的模块,则可以使用循环的方式来实现
5.1.1 loop迭代
对于迭代项的引用,要使用内置变量 item 来引用,这是固定写法。
迭代元素使用 with_items 来锚定列表,列表中可以是单项元素,也可以是字典。从 ansible2.5 以后的版本中,使用 loop 来代替 with_items。
bash
[devops@master chap03]$ cat loop.yml
---
- name: test loop
hosts: master
gather_facts: no
tasks:
- name: test loop1
debug:
msg: "{{ item }}"
loop:
- tom
- jerry
- xiaoming
- name: test loop2
debug:
msg: "{{ item.name }} --- {{ item.type }}"
loop:
- name: tom
type: cat
- name: jerry
type: mouse
- name: xiaoming
type: person
[devops@master chap03]$ ansible-playbook loop.yml
PLAY [test loop] ***************************************************************
TASK [test loop1] **************************************************************
ok: [master] => (item=tom) => {
"msg": "tom"
}
ok: [master] => (item=jerry) => {
"msg": "jerry"
}
ok: [master] => (item=xiaoming) => {
"msg": "xiaoming"
}
TASK [test loop2] **************************************************************
ok: [master] => (item={'name': 'tom', 'type': 'cat'}) => {
"msg": "tom --- cat"
}
ok: [master] => (item={'name': 'jerry', 'type': 'mouse'}) => {
"msg": "jerry --- mouse"
}
ok: [master] => (item={'name': 'xiaoming', 'type': 'person'}) => {
"msg": "xiaoming --- person"
}
PLAY RECAP *********************************************************************
master : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.1.2 until循环
使用 until 也可以控制一个 task 重复执行,until 后面的值或表达式为 true 的时候,才退出重试,即在task 没有获得预期值的情况下,会一直重复执行,直到得到预期结果。
until 默认重试三次,每次重试时间间隔 5S,可自定义修改。
bash
[devops@master chap03]$ cat until.yml
---
- name: test until
hosts: master
gather_facts: no
tasks:
- shell: cat /tmp/ansible-until
register: rs
until: rs.stdout == "123"
[devops@master chap03]$ ansible-playbook until.yml
PLAY [test until] **************************************************************
TASK [shell] *******************************************************************
FAILED - RETRYING: [master]: shell (3 retries left).
FAILED - RETRYING: [master]: shell (2 retries left).
FAILED - RETRYING: [master]: shell (1 retries left).
fatal: [master]: FAILED! => {"attempts": 3, "changed": true, "cmd": "cat /tmp/ansible-until", "delta": "0:00:00.004718", "end": "2025-10-24 13:58:35.306311", "msg": "non-zero return code", "rc": 1, "start": "2025-10-24 13:58:35.301593", "stderr": "cat: /tmp/ansible-until: 没有那个文件或目录", "stderr_lines": ["cat: /tmp/ansible-until: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
PLAY RECAP *********************************************************************
master : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[devops@master chap03]$ cat until.yml
---
- name: test until
hosts: master
gather_facts: no
tasks:
- shell: cat /tmp/ansible-until
register: rs
until: rs.stdout == "123"
retries: 5 #重试次数5次,默认3次
delay: 2 #重试间隔时间,默认5秒
5.1.3 with_lines逐行处理
with_lines 可以将一条命令的执行结果逐行调用同一个 task 进行处理。
bash
[devops@master chap03]$ cat with_lines.yml
---
- name: test with_lines
hosts: master
gather_facts: no
tasks:
- name: test with_lines
debug:
msg: "{{ item }}"
with_lines: cat /etc/fstab | grep -v '^#'
[devops@master chap03]$ ansible-playbook with_lines.yml
PLAY [test with_lines] *********************************************************
TASK [test with_lines] *********************************************************
ok: [master] => (item=) => {
"msg": ""
}
ok: [master] => (item=/dev/mapper/rhel-root / xfs defaults 0 0) => {
"msg": "/dev/mapper/rhel-root / xfs defaults 0 0"
}
ok: [master] => (item=UUID=fbda9444-cdb9-4ef5-9f55-070ce83a3f00 /boot xfs defaults 0 0) => {
"msg": "UUID=fbda9444-cdb9-4ef5-9f55-070ce83a3f00 /boot xfs defaults 0 0"
}
ok: [master] => (item=/dev/mapper/rhel-swap none swap defaults 0 0) => {
"msg": "/dev/mapper/rhel-swap none swap defaults 0 0"
}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.2 条件判断
when 语句可以实现选择执行,即根据条件判断的结果决定是否执行 task,条件判断的数据来源可以是变量,前面的 task 的执行结果等。
示例1:判断变量是否被定义
bash
#when条件为真则执行任务
[devops@master chap03]$ cat when1.yml
---
- name: test when
hosts: master
gather_facts: no
tasks:
- debug:
msg: undefined
when: username is undefined #如果变量未被定义则为真
- debug:
msg: undefined
when: username is defined
[devops@master chap03]$ ansible-playbook when1.yml
PLAY [test when] ***************************************************************
TASK [debug] *******************************************************************
ok: [master] => {
"msg": "undefined"
}
TASK [debug] *******************************************************************
skipping: [master]
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
示例2:循环判断
bash
[devops@master chap03]$ cat when2.yml
---
- name: test when
gather_facts: no
hosts: master
tasks:
- debug:
msg: "{{ item }}"
when: item > 3
loop: [1,2,3,4,5]
[devops@master chap03]$ ansible-playbook when2.yml
PLAY [test when] ***************************************************************
TASK [debug] *******************************************************************
skipping: [master] => (item=1)
skipping: [master] => (item=2)
skipping: [master] => (item=3)
ok: [master] => (item=4) => {
"msg": 4
}
ok: [master] => (item=5) => {
"msg": 5
}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例3: 根据不同的系统安装不同的软件
bash
[devops@master chap03]$ cat when3.yml
---
- name: test when
hosts: master
tasks:
- name: redhat yum
yum:
name: httpd
state: present
when: (ansible_facts.distribution == "RedHat" and ansible_facts.distribution_version == "9.3")
- name: ubuntu apt
apt:
name: apache2
state: absent
when:
- ansible_facts.distribution == "Ubuntu"
- ansible_facts.distribution_version == "20.04"
[devops@master chap03]$ ansible-playbook when3.yml
PLAY [test when] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [master]
TASK [redhat yum] **************************************************************
changed: [master]
TASK [ubuntu apt] **************************************************************
skipping: [master]
PLAY RECAP *********************************************************************
master : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
failed_when:使用 when 是保证让条件成立时执行 task,使用 failed_when 表示为真时 task 是执行失败状态。
bash
[devops@master chap03]$ cat failed_when.yml
---
- name: test failed when
hosts: master
tasks:
- shell: touch /tmp/when-{{ item }}
loop: [1,2,3,4,5]
failed_when: item > 3
#可以看到循环中有两个任务显示failed,但是只是显示failed,并不是没有执行
[devops@master chap03]$ ansible-playbook failed_when.yml
PLAY [test failed when] ********************************************************
TASK [Gathering Facts] *********************************************************
ok: [master]
TASK [shell] *******************************************************************
changed: [master] => (item=1)
changed: [master] => (item=2)
changed: [master] => (item=3)
failed: [master] (item=4) => {"ansible_loop_var": "item", "changed": true, "cmd": "touch /tmp/when-4", "delta": "0:00:00.004887", "end": "2025-10-24 14:30:57.956584", "failed_when_result": true, "item": 4, "msg": "", "rc": 0, "start": "2025-10-24 14:30:57.951697", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
failed: [master] (item=5) => {"ansible_loop_var": "item", "changed": true, "cmd": "touch /tmp/when-5", "delta": "0:00:00.004684", "end": "2025-10-24 14:30:58.405185", "failed_when_result": true, "item": 5, "msg": "", "rc": 0, "start": "2025-10-24 14:30:58.400501", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[devops@master chap03]$ ll /tmp/when-*
-rw-r--r-- 1 root root 0 10月 24 14:30 /tmp/when-1
-rw-r--r-- 1 root root 0 10月 24 14:30 /tmp/when-2
-rw-r--r-- 1 root root 0 10月 24 14:30 /tmp/when-3
-rw-r--r-- 1 root root 0 10月 24 14:30 /tmp/when-4
-rw-r--r-- 1 root root 0 10月 24 14:30 /tmp/when-5
changed_when:只有在 task 的执行结果返回状态为 changed 的时候,我们才认为该 task 是真实执行了,在远程主机上产生了数据变化,但是在 ansible 中,不是所有模块都具有幂等性,对于某些不会产生数据变化的 task ,ansible 也会给出 changed 输出,我们可以使用 changed_when 来避免这一情况。
bash
#由于shell模块不具有幂等性,每次执行命令时都会显示changed,可以使用 changed_when让其不显示changed
[devops@master chap03]$ cat changed_when.yml
---
- name: test changed when
hosts: master
gather_facts: no
tasks:
- name: test task
shell: id
changed_when: false
[devops@master chap03]$ ansible-playbook changed_when.yml
PLAY [test changed when] *******************************************************
TASK [test task] ***************************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.3 block、rescue、always分组
使用 block 可以对 task 任务进行分组,将多个 task 任务放到一个 block 下,可以在写一个 when 判断的情况下调用多个 task 任务。
当block的任务失败时,会执行rescue中的任务。
always中的任务始终会执行。
示例1:
bash
[devops@master chap03]$ cat block.yml
---
- name: test block
hosts: master
tasks:
- name: block test
block:
- debug:
msg: "block test1"
- debug:
msg: "block test2"
rescue:
- debug:
msg: "rescue test"
always:
- debug:
msg: "always test"
[devops@master chap03]$ ansible-playbook block.yml
PLAY [test block] **************************************************************
TASK [Gathering Facts] *********************************************************
ok: [master]
TASK [debug] *******************************************************************
ok: [master] => {
"msg": "block test1"
}
TASK [debug] *******************************************************************
ok: [master] => {
"msg": "block test2"
}
TASK [debug] *******************************************************************
ok: [master] => {
"msg": "always test"
}
PLAY RECAP *********************************************************************
master : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例2:给master添加一块sata类型的硬盘大小为2G,给node01添加一块sata类型的硬盘大小为1G。写一个playbook:如果主机上没有/dev/sda磁盘则提示"/dev/sda does not exists",如果有该磁盘则创建一个1800M大小的分区并格式化为xfs的文件系统类型,如果无法创建该大小磁盘则提示"can not create 1800M partition"并创建一个800M大小的磁盘并格式为xfs的文件系统类型。
bash
[devops@master chap03]$ ansible all -a 'lsblk /dev/sda'
node01 | CHANGED | rc=0 >>
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 1G 0 disk
master | CHANGED | rc=0 >>
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 2G 0 disk
node02 | FAILED | rc=32 >>
lsblk: /dev/sda:不是一个块设备non-zero return code
#定义playbook
[devops@master chap03]$ cat disk.yml
---
- name: test block rescue always
hosts: all
tasks:
- name: check disk
debug:
msg: "/dev/sda does not exists"
when: " 'sda' not in ansible_facts.devices "
- name: create partition
when: " 'sda' in ansible_facts.devices " #此处的when会被应用到block,rescue和always
block:
- name: create 1800M
parted:
device: /dev/sda
number: 1
state: present
part_end: 1.8GiB
rescue:
- name: print message
debug:
msg: "can not create 1800M partition"
- name: create 800M
parted:
device: /dev/sda
number: 1
state: present
part_end: 800MiB
always:
- name: format xfs
filesystem:
fstype: xfs
dev: /dev/sda1
[devops@master chap03]$ ansible-playbook disk.yml
[WARNING]: Collection community.general does not support Ansible version 2.14.9
PLAY [test block rescue always] ************************************************
TASK [Gathering Facts] *********************************************************
ok: [master]
ok: [node01]
ok: [node02]
TASK [check disk] **************************************************************
ok: [node02] => {
"msg": "/dev/sda does not exists"
}
skipping: [master]
skipping: [node01]
TASK [create 1800M] ************************************************************
skipping: [node02]
fatal: [node01]: FAILED! => {"changed": false, "err": "Error: The location 1.8GiB is outside of the device /dev/sda.\n", "msg": "Error while running parted script: /sbin/parted -s -f -m -a optimal /dev/sda -- unit KiB mkpart primary 0% 1.8GiB", "out": "", "rc": 1}
changed: [master]
TASK [print message] ***********************************************************
ok: [node01] => {
"msg": "can not create 1800M partition"
}
TASK [create 800M] *************************************************************
changed: [node01]
TASK [format xfs] **************************************************************
skipping: [node02]
ok: [node01]
ok: [master]
PLAY RECAP *********************************************************************
master : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
node01 : ok=4 changed=1 unreachable=0 failed=0 skipped=1 rescued=1 ignored=0
node02 : ok=2 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
#执行后查看效果
[devops@master chap03]$ ansible all -a 'lsblk /dev/sda'
node01 | CHANGED | rc=0 >>
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 1G 0 disk
└─sda1 8:1 0 799M 0 part
master | CHANGED | rc=0 >>
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 2G 0 disk
└─sda1 8:1 0 1.8G 0 part
node02 | FAILED | rc=32 >>
lsblk: /dev/sda:不是一个块设备non-zero return code
5.4 并发和滚动执行
默认情况下, ansible 从上到下执行,如果一个 playbook 中有多个 task,在有多台远程主机的情况下,需要在所有远程主机上执行完当前的 task 之后才执行下一个 task,如果主机过多,或者需要执行的task 比较消耗时间,则会导致所有主机都处于一个执行中状态。
forks选项是Ansible原生支持的一种支持并发执行的方式,指定同时执行任务的受控节点的数量,可以通过ansible.cfg配置文件指定默认值。
(1)在 ansible.cfg中全局设置
bash
[defaults]
forks = 20 # 设置默认并发数为 20
(2)在 Playbook 运行时设置
bash
[devops@master chap03]$ cat forks.yml
- name: Example Playbook
gather_facts: no
hosts: all
tasks:
- debug:
msg: "Running with forks={{ ansible_forks }}"
[devops@master chap03]$ ansible-playbook forks.yml
PLAY [Example Playbook] ********************************************************
TASK [debug] *******************************************************************
ok: [node02] => {
"msg": "Running with forks=5"
}
ok: [master] => {
"msg": "Running with forks=5"
}
ok: [node01] => {
"msg": "Running with forks=5"
}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[devops@master chap03]$ ansible-playbook --forks 2 forks.yml
PLAY [Example Playbook] ********************************************************
TASK [debug] *******************************************************************
ok: [node02] => {
"msg": "Running with forks=2"
}
ok: [master] => {
"msg": "Running with forks=2"
}
ok: [node01] => {
"msg": "Running with forks=2"
}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serial:将一个 Play 中的主机分成多个批次,逐批执行play中的任务。
bash
[devops@master chap03]$ cat serial.yml
---
- name: test serial
hosts: all
gather_facts: no
serial: 1
tasks:
- name: test1
debug:
msg: "test1"
- name: test2
debug:
msg: "test2"
#会在第一个主机上执行完所有任务后再在第二个主机上执行所有任务
[devops@master chap03]$ ansible-playbook serial.yml
PLAY [test serial] *************************************************************
TASK [test1] *******************************************************************
ok: [node02] => {
"msg": "test1"
}
TASK [test2] *******************************************************************
ok: [node02] => {
"msg": "test2"
}
PLAY [test serial] *************************************************************
TASK [test1] *******************************************************************
ok: [master] => {
"msg": "test1"
}
TASK [test2] *******************************************************************
ok: [master] => {
"msg": "test2"
}
PLAY [test serial] *************************************************************
TASK [test1] *******************************************************************
ok: [node01] => {
"msg": "test1"
}
TASK [test2] *******************************************************************
ok: [node01] => {
"msg": "test2"
}
PLAY RECAP *********************************************************************
master : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node01 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node02 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.5 yaml文件的互相调用
5.5.1 import_playbook静态导入
将一个或多个playbook文件导入到主playbook中,在 Playbook 解析阶段(运行前)加载并合并内容。
bash
[devops@master chap03]$ cat play1.yml
---
- name: master
gather_facts: no
hosts: master
vars:
var1: play1
tasks:
- debug:
msg: "{{ var1 }}"
[devops@master chap03]$ cat play2.yml
---
- name: master
gather_facts: no
hosts: node01
vars:
var1: play2
tasks:
- debug:
msg: "{{ var1 }}"
[devops@master chap03]$ cat play-main.yml
---
- name: import playbook1
import_playbook: play1.yml
- name: import playbook2
import_playbook: play2.yml
[devops@master chap03]$ ansible-playbook play-main.yml
PLAY [master] ******************************************************************
TASK [debug] *******************************************************************
ok: [master] => {
"msg": "play1"
}
PLAY [master] ******************************************************************
TASK [debug] *******************************************************************
ok: [node01] => {
"msg": "play2"
}
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.5.2 import_tasks静态导入
在 Playbook 解析阶段(运行前)加载并合并内容,不支持loop循环。
bash
[devops@master chap03]$ cat install-pkg.yml
- name: install pkg
yum:
name: "{{ package }}"
state: present
- name: install curl
yum:
name: curl
state: present
[devops@master chap03]$ cat start-svc.yml
- name: start and enable svc
service:
name: "{{ service_name }}"
state: started
[devops@master chap03]$ cat task-main.yml
---
- name: test import task
hosts: master
gather_facts: no
vars:
package: httpd
service_name: httpd
tasks:
- import_tasks: install-pkg.yml
- import_tasks: start-svc.yml
[devops@master chap03]$ ansible-playbook task-main.yml
PLAY [test import task] ********************************************************
TASK [install pkg] *************************************************************
ok: [master]
TASK [install curl] ************************************************************
ok: [master]
TASK [start and enable svc] ****************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
bash
#添加when判断:当对 import_tasks 使用 when 进行条件判断时,when 对应的条件会被应用于文件中的每一个任务,意思是每要执行一个任务时,都要先进行条件判断,满足条件就执行,不满足就跳过,需要多次判断。
[devops@master chap03]$ cat task-main.yml
---
- name: test import task
hosts: master
gather_facts: no
vars:
package: httpd
service_name: httpd
tasks:
- import_tasks: install-pkg.yml
when: var1 is defined
- import_tasks: start-svc.yml
[devops@master chap03]$ ansible-playbook task-main.yml
PLAY [test import task] ********************************************************
TASK [install pkg] *************************************************************
skipping: [master]
TASK [install curl] ************************************************************
skipping: [master]
TASK [start and enable svc] ****************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
5.5.3 include_tasks动态导入
只有在任务执行到该步骤时才会加载文件,支持条件判断和循环。
bash
[devops@master chap03]$ cat install-pkg.yml
- name: install pkg
yum:
name: "{{ package }}"
state: present
- name: install curl
yum:
name: curl
state: present
[devops@master chap03]$ cat start-svc.yml
- name: start and enable svc
service:
name: "{{ service_name }}"
state: started
[devops@master chap03]$ cat task-main.yml
---
- name: test import task
hosts: master
gather_facts: no
vars:
package: httpd
service_name: httpd
tasks:
- include_tasks: install-pkg.yml
- include_tasks: start-svc.yml
[devops@master chap03]$ ansible-playbook task-main.yml
PLAY [test import task] ********************************************************
TASK [include_tasks] ***********************************************************
included: /home/devops/chap03/install-pkg.yml for master
TASK [install pkg] *************************************************************
ok: [master]
TASK [install curl] ************************************************************
ok: [master]
TASK [include_tasks] ***********************************************************
included: /home/devops/chap03/start-svc.yml for master
TASK [start and enable svc] ****************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
#设置when条件:当对 include_tasks 使用 when 进行条件判断时,when 对应的条件只应用一次,表示when条件成立时导入该任务,之后执行这个include文件中所有的任务的时候不再进行其他判断;
[devops@master chap03]$ cat task-main.yml
---
- name: test import task
hosts: master
gather_facts: no
vars:
package: httpd
service_name: httpd
tasks:
- include_tasks: install-pkg.yml
when: var1 is defined
- include_tasks: start-svc.yml
[devops@master chap03]$ ansible-playbook task-main.yml
PLAY [test import task] ********************************************************
TASK [include_tasks] ***********************************************************
skipping: [master]
TASK [include_tasks] ***********************************************************
included: /home/devops/chap03/start-svc.yml for master
TASK [start and enable svc] ****************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
#使用loop循环
[devops@master chap03]$ cat install-package.yml
---
- name: Install {{ package_name }}
yum:
name: "{{ package_name }}" # 使用循环变量 package_name
state: present
[devops@master chap03]$ cat package-main.yml
---
- name: test
hosts: master
vars:
packages: [ "httpd", "net-tools" ]
tasks:
- name: test include
include_tasks: install-package.yml
loop: "{{ packages }}"
loop_control:
loop_var: package_name # 指定循环变量名
[devops@master chap03]$ ansible-playbook package-main.yml
PLAY [test] ********************************************************************
TASK [test include] ************************************************************
included: /home/devops/chap03/install-package.yml for master => (item=httpd)
included: /home/devops/chap03/install-package.yml for master => (item=net-tools)
TASK [Install httpd] ***********************************************************
ok: [master]
TASK [Install net-tools] *******************************************************
ok: [master]
PLAY RECAP *********************************************************************
master : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0