管理变量和事实
管理 FACTS
FACTS 介绍
FACTS 是 "Flexible AC Transmission Systems(柔性交流输电系统)" 的缩写,是指一类用于增强电力系统传输能力、稳定性与控制性的电力电子装置或技术。
它通过快速调节系统中的电压、电流、阻抗或相位角,提高交流电网的传输效率与安全性。
查看 FACTS 内容
示例1:查看所有变量
[yuxb@controller web 09:49:50]$ cat playbook.yml
---
# - name: Installs a package and prints the result
# hosts: node1
# tasks:
# - name: Install the package
# yum:
# name: httpd
# state: installed
# register: install_result
# - debug:
# var: install_result
- name: Dump facts
hosts: node1
tasks:
- name: Print all facts
debug:
var: ansible_facts
...
示例2:查看单个变量
目的:
通过 setup
模块获取 facts,并在 playbook 中打印指定变量的值。
---
- hosts: node1
tasks:
- name: Print Ansible facts
debug:
msg: >
The default IPv4 address of {{ ansible_fqdn }}
is {{ ansible_default_ipv4.address }}
使用 setup
模块收集目标主机 facts:
[yuxb@controller web 09:57:03]$ ansible node1 -m setup > node1.facts
^[[A[yuxb@controller web 09:57:1sz node1.factsacts
说明:
-
-m setup
:使用 setup 模块收集系统信息(Facts) -
> node1.facts
:将输出保存到本地文件node1.facts
中,便于查看和搜索
setup 和 gather_facts 模块
Facts 是 Ansible 自动收集的远程主机信息,比如:
-
主机名、IP、操作系统、内存、CPU
-
网络接口、磁盘挂载、Python 版本
-
默认网关、路由、FQDN 等
setup模块
-
setup
是一个 Ansible 模块 ,你可以手动调用它来 收集并查看某个主机的所有 facts。 -
适合临时查看、调试或提取特定信息。
# 收集并打印出 node1 的所有系统信息。
ansible node1 -m setup
# 只查看默认 IPv4 的 facts 信息
ansible node1 -m setup -a 'filter=ansible_default_ipv4'
gather_facts 参数
-
gather_facts
是 Playbook 中的一个参数 ,用于指定 是否自动在执行任务前收集 facts。 -
默认是
true
。
示例:
---
- hosts: all
gather_facts: true # 默认就是 true
tasks:
- name: Print OS type
debug:
msg: "This system is {{ ansible_os_family }}"
设置为 false(不收集):
---
- hosts: all
gather_facts: false
tasks:
- name: This will not have facts
debug:
msg: "No facts will be available"
总结对比表:
特性 | setup 模块 |
gather_facts 参数 |
---|---|---|
类型 | 模块(可命令行或 task 中使用) | Playbook 级参数 |
作用时机 | 手动执行时收集 facts | 在每次 Playbook 运行前自动执行 |
默认行为 | 不自动运行 | 默认是自动运行(true) |
过滤功能 | 支持 filter 查询特定 facts | 不支持 filter,只能全量收集 |
使用场景 | 临时查询、过滤 facts、调试等 | 用于 Playbook 中任务执行前的数据准备 |
实施任务控制
在 Ansible 中,"实施任务控制" 通常指的是对任务执行流程的控制,比如:
-
条件执行(when)
-
循环执行(with_items / loop)
-
错误处理(block / rescue / ignore_errors)
-
任务依赖(tags / handlers)
-
异步执行(async / poll)
-
任务注册与判断(register)
编写循环任务
复杂循环
[yuxb@controller web 10:36:36]$ 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 Laoma WebSite!\n"
dest: /var/www/html/index.html
# 第三个任务
- name: firewalld enabled and running
# service模块,用于启用并启动firewalld服务
service:
name: "{{item}}"
enabled: true
state: started
loop:
- 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: httpd
enabled: true
state: started
# Playbook中第二个play,-开头表示列表
- name: Test intranet web server
hosts: localhost
become: no
tasks:
- name: connect to intranet web server
# uri模块,用于测试网站是否可以访问
uri:
url: http://node1
return_content: yes
status_code: 200
# yaml格式结束行,一般省略
...
# 验证
[yuxb@controller web 10:36:41]$ 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
简单循环
示例一
[yuxb@controller web 10:40:57]$ vim playbook.yml
---
- name: add several users
hosts: node1
gather_facts: no
tasks:
- name: add user jane
user:
name: "jane"
groups: "wheel"
state: present
- name: add user joe
user:
name: "joe"
state: present
groups: "wheel"
...
# 使用loop循环改写
[yuxb@controller web 10:49:39]$ cat playbook.yml
---
- name: add several users
hosts: node1
gather_facts: no
tasks:
- name: add user jane
user:
name: "{{ item }}"
groups: "wheel"
state: present
loop:
- jane
- joe
...
# 验证
[yuxb@controller web 10:36:55]$ ansible-playbook playbook.yml
PLAY [add several users] ****************************************************************************
TASK [add user jane] ********************************************************************************
changed: [node1] => (item=jane)
changed: [node1] => (item=joe)
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例二
[yuxb@controller web 10:58:12]$ cat playbook.yml
---
- name: add several users
hosts: node1
gather_facts: no
tasks:
- name: add user jane
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- name: jane
groups: wheel
- name: joe
groups: root
...
# 验证
[yuxb@controller web 10:49:53]$ ansible-playbook playbook.yml
PLAY [add several users] ****************************************************************************
TASK [add user jane] ********************************************************************************
ok: [node1] => (item={u'name': u'jane', u'groups': u'wheel'})
changed: [node1] => (item={u'name': u'joe', u'groups': u'root'})
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Do-Until Loops
Do-Until 循环 是一种 后测试循环结构,意思是:
-
先执行一次循环体
-
然后再判断条件
-
直到条件为真时,循环才会终止
示例:测试 node2,什么时候可以ping通。
[yuxb@controller web 10:58:14]$ vim playbook.yml
[yuxb@controller web 11:20:47]$ cat playbook.yml
---
- name: test loop
hosts: node1
gather_facts: no
tasks:
- shell: ping -c1 -w 2 node2
register: result
until: result.rc == 0
retries: 20
delay: 1
...
# 关闭node2测试
[yuxb@controller web 11:21:03]$ 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后
[yuxb@controller web 11:21:03]$ 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 中,使用 when
子句实现条件判断,使任务根据特定条件是否满足来执行。
常见判断
在某些场景中,变量可能并不是每次都存在,如果直接使用未定义的变量,会导致任务报错。因此,判断变量是否已定义是一个最佳实践。
变量是否定义判断
示例一
变量是否定义判断
[yuxb@controller web 11:45:48]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
vars:
username: yuxb
tasks:
- debug:
msg: "var: username is defined"
when: username is defined
...
# 执行
[yuxb@controller web 11:46:19]$ 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硬盘
[yuxb@controller web 11:49:01]$ cat playbook.yml
---
- hosts: node1
gather_facts: yes
vars:
username: yuxb
tasks:
- debug:
msg: "sdb is exist"
when: ansible_devices.sdb is defined
...
# 执行
[yuxb@controller web 11:46:21]$ 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盘
[yuxb@controller web 11:49:05]$ vim playbook.yml
[yuxb@controller web 11:50:15]$ cat playbook.yml
---
- hosts: node1
gather_facts: yes
vars:
username: yuxb
tasks:
- debug:
msg: "sr0 is exist"
when: ansible_devices.sr0 is defined
...
# 执行
[yuxb@controller web 11:49:13]$ 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
in 和 not in 判断
用于判断某个值是否存在于一个 字符串 、列表 或 字典键中。
示例1:给用户添加组
[yuxb@controller web 13:50:47]$ cat playbook.yml
---
- name: test
hosts: node1
gather_facts: no
vars:
username: devops
supergroup: wheel
tasks:
- name: gather user information
shell: id {{ username }}
register: result
- name: Task run if user is in supergroups
user:
name: "{{ username }}"
groups: "{{ supergroup }}"
append: yes
when: supergroup not in result.stdout
...
[root@node1 ~ 13:47:57]# useradd devops
[root@node1 ~ 13:48:12]# id devops
uid=1091(devops) gid=1091(devops) 组=1091(devops)
# 执行
[yuxb@controller web 13:50:10]$ 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
示例二:根据主机所属组控制输出
[yuxb@controller web 13:59:02]$ cat inventory
controller
[nodes]
node1
node2
node3
node4
[webs]
node1
node2
[dbs]
node3
node4
[yuxb@controller web 13:58:56]$ cat playbook.yml
---
- name: test
hosts: node1 node3
gather_facts: no
tasks:
- name: install httpd
yum:
name: httpd
state: present
when: inventory_hostname in groups.webs
- name: install mariadb
yum:
name: mariadb
state: present
when: inventory_hostname in groups.dbs
...
# 执行
[yuxb@controller web 14:02:01]$ 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 获取 / 文件系统可用空间
[yuxb@controller web 14:27:02]$ cat playbook.yml
---
- name: Combining Loops and Conditional Play
hosts: node1
tasks:
- name: install mariadb-server if enough space on root
yum:
name: mariadb-server
state: latest
loop: "{{ ansible_mounts }}"
when:
- item.mount == "/"
- item.size_available > 300000000
...
# 执行
[yuxb@controller web 14:25:52]$ 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
Handler(处理器) 是 Ansible 中的一种特殊任务,用于在任务发生**变更(changed)**时才触发执行的动作。
Ansible Handlers 功能
功能说明
-
Handlers 是为 "有变更时才执行的操作" 而设计的
-
常用于在某些操作完成后执行额外的步骤,例如:
-
修改配置文件后重启服务
-
更新程序后重载守护进程
-
改变权限后重新应用策略
-
工作原理
-
任务执行后发生变更(changed)
-
该任务中使用
notify
通知某个 handler -
Handler 在 playbook 的最后统一执行一次 (除非用
meta: flush_handlers
强制提前执行)
示例 在远程主机 node1
上部署 Web 服务器
[yuxb@controller web 14:40:17]$ cat playbook.yml
---
- name: deploy web server
hosts: node1
tasks:
- name: install packages
yum:
name: httpd
state: present
notify:
- enable and restart apache
- name: install httpd-manual
yum:
name: httpd-manual
state: present
notify:
- enable and restart apache
- debug:
msg: last task in tasks
handlers:
- name: enable and restart apache
service:
name: httpd
state: restarted
enabled: yes
...
# 执行
[yuxb@controller web 14:27:08]$ 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
Ansible评估各任务的返回代码,从而确定任务是成功还是失败。通常而言, 当某个主机执行任务失败时,Ansible将立即终止该主机继续执行play,其他主机可以继续执行play。
示例:
# 环境
[root@node1 ~ 14:37:44]# cp /etc/hosts /etc/myhost
[root@node2 ~ 15:02:29]# rm -f /etc/myhost
[yuxb@controller web 14:41:21]$ vim playbook.yml
[yuxb@controller web 15:03:54]$ cat playbook.yml
---
- name: test
hosts: node1,node2
tasks:
- name: show /etc/myhosts
shell: cat /etc/myhosts
- name: echo end
debug:
msg: echo end
# 执行
[yuxb@controller web 15:01:27]$ 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-14 15:02:49.212666", "msg": "non-zero return code", "rc": 1, "start": "2025-08-14 15:02:49.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-14 15:02:49.235356", "msg": "non-zero return code", "rc": 1, "start": "2025-08-14 15:02:49.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忽略错误。
示例:
[yuxb@controller web 15:03:57]$ vim playbook.yml
[yuxb@controller web 15:05:33]$ cat playbook.yml
---
- name: test
hosts: node1
tasks:
- name: install a not exist package
yum:
name: notexitpackage
state: present
ignore_errors: yes
register: result
- name: debug install result
debug:
msg: notexitpackage is not exit
when: result is failed
# 执行
[yuxb@controller web 15:05:16]$ 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"]}
...ignoring
TASK [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)。
示例:
[yuxb@controller web 15:05:39]$ vim playbook.yml
[yuxb@controller web 15:07:11]$ cat playbook.yml
---
- name: test
hosts: node1
force_handlers: yes
tasks:
- name: a task which always notifies its handler
command: /bin/true
notify: restart the sshd
- name: fails because the package doesn't exist
yum:
name: notexistpkg
state: latest
handlers:
- name: restart the sshd
service:
name: sshd
state: restarted
[yuxb@controller web 15:05:45]$ 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。
示例:
[yuxb@controller web 15:07:14]$ vim playbook.yml
[yuxb@controller web 15:09:33]$ cat playbook.yml
---
- name: test fail module
hosts: node1
gather_facts: no
tasks:
- debug:
msg: task1
- fail:
- debug:
msg: task3
[yuxb@controller web 15:07:33]$ 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
failed_when
指明什么条件下,判定任务执行失败。
示例:
[yuxb@controller web 15:09:36]$ vim playbook.yml
[yuxb@controller web 15:11:21]$ cat playbook.yml
---
- name: test failed_when
hosts: node1
tasks:
- shell: /root/adduser
register: command_result
failed_when: "'failed' in command_result.stdout"
环境准备:
[root@node1 ~ 15:15:37]# vim /root/adduser
[root@node1 ~ 15:16:06]# chmod +x /root/adduser
[root@node1 ~ 15:16:11]# cat /root/adduser
#!/bin/bash
useradd devops &> /dev/null
if [ $? -eq 0 ];then
echo add user devops success
else
echo add user devops failed
fi
-
当devops用户不存在时,shell模块跳过执行。
-
当devops用户存在时,shell模块执行失败。
[yuxb@controller web 15:09:49]$ 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-14 15:16:36.903010", "failed_when_result": true, "rc": 0, "start": "2025-08-14 15:16:36.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:
[yuxb@controller web 15:11:24]$ vim playbook.yml
[yuxb@controller web 15:17:58]$ cat playbook.yml
---
- name: changed_when
hosts: node1
tasks:
- name: upgrade-database
shell: /usr/local/bin/upgrade-database
register: result
changed_when: "'Success' in result.stdout"
notify:
- restart_database
handlers:
- name: restart_database
service:
name: mariadb
state: restarted
环境准备:
[root@node1 ~ 15:18:25]# yum install -y mariadb-server
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
软件包 1:mariadb-server-5.5.68-1.el7.x86_64 已安装并且是最新版本
无须任何处理
[root@node1 ~ 15:18:27]# systemctl enable mariadb --now
[root@node1 ~ 15:18:27]# vim /usr/local/bin/upgrade-database
[root@node1 ~ 15:18:49]# cat /usr/local/bin/upgrade-database
#!/bin/bash
mysql -e 'create user yuxb@"%" identified by "123";' && mysql -e 'GRANT ALL PRIVILEGES on *.* TO yuxb@"%";' && echo update database Success
[root@node1 ~ 15:19:08]# chmod +x /usr/local/bin/upgrade-database
[yuxb@controller web 15:20:07]$ ansible-playbook playbook.yml
PLAY [changed_when] *********************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [node1]
TASK [upgrade-database] *****************************************************************************
fatal: [node1]: FAILED! => {"changed": false, "cmd": "/usr/local/bin/upgrade-database", "delta": "0:00:00.037942", "end": "2025-08-14 15:20:26.929733", "msg": "non-zero return code", "rc": 1, "start": "2025-08-14 15:20:26.891791", "stderr": "ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)", "stderr_lines": ["ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)"], "stdout": "", "stdout_lines": []}
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
对于command模块和shell模块,只要命令正常执行,结果状态通常都是changed。可以通过返回码和输出结果来判定它们是否做出更改。
关键字 changed_when: false ,让任务结果状态不为changed,只能报告为ok或failed。
示例2:
[yuxb@controller web 15:18:01]$ vim playbook.yml
[yuxb@controller web 15:22:09]$ cat playbook.yml
---
- name: Test When
hosts: node1
gather_facts: no
tasks:
- name: test changed_when
shell: cat /etc/redhat-release
changed_when: false
[yuxb@controller web 15:20:27]$ 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
[yuxb@controller web 15:22:20]$ vim playbook.yml
[yuxb@controller web 15:24:49]$ cat playbook.yml
---
- name: test
hosts: node1,node2
tasks:
- name: show /etc/myhosts
shell: cat /etc/hosts
changed_when: false
- name: echo end
debug:
msg: echo end
[yuxb@controller web 15:22:13]$ ansible-playbook playbook.yml
PLAY [test] *****************************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [node2]
ok: [node1]
TASK [show /etc/myhosts] ****************************************************************************
ok: [node2]
ok: [node1]
TASK [echo end] *************************************************************************************
ok: [node1] => {
"msg": "echo end"
}
ok: [node2] => {
"msg": "echo end"
}
PLAY RECAP ******************************************************************************************
node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Ansible block
多个任务作为block子条目,block作为多个任务整体。
Ansible 的 block
是一种语法结构,可以让你将多个任务组合在一起,并对这些任务统一地使用:
-
when
条件判断 -
become
权限提升 -
rescue
错误处理 -
always
最终操作(无论成功或失败都执行)
**示例1:**在所有受管节点上创建符合以下要求的逻辑卷:
-
在research卷组中创建逻辑卷:
-
逻辑卷名称为data
-
逻辑卷大小为4000MiB
-
使用ext4文件系统格式化逻辑卷
-
将逻辑卷挂载到/data目录
-
如果无法创建请求的逻辑卷大小,应显示错误信息:Could not create logical volume of that size 并且应改为使用大小800MiB。
-
-
如果卷组research不存在,应显示错误信息:Volume does not exist
环境准备:
# node1:添加一块 20G sata 硬盘
[root@node1 ~ 15:36:13]# vgcreate research /dev/sdb
Physical volume "/dev/sdb" successfully created.
Volume group "research" successfully created
# node2:添加一块 20G sata 硬盘
[root@node2 ~ 15:37:04]# parted /dev/sdb unit MiB mklabel msdos
信息: You may need to update /etc/fstab.
[root@node2 ~ 15:37:04]# parted /dev/sdb unit MiB mkpart primary 1 1025
信息: You may need to update /etc/fstab.
[root@node2 ~ 15:37:11]# vgcreate research /dev/sdb1
Physical volume "/dev/sdb1" successfully created.
Volume group "research" successfully created
playbook.yaml内容如下:
[yuxb@controller web 16:31:17]$ cat playbook.yml
---
- name: create logical volume
hosts: all
tasks:
- name: create lv
block:
- name: Create a logical volume of 4000m
lvol:
vg: research
lv: data
size: 4000
rescue:
- name: Could not create logical volume of that size
debug:
msg: Could not create logical volume of that size
- name: Create a logical volume of 800m
lvol:
vg: research
lv: data
size: 800
always:
- name: Create a ext4 filesystem
filesystem:
fstype: ext4
dev: /dev/research/data
- name: Create a directory
file:
path: /data
state: directory
- name: Mount up device
mount:
path: /data
src: /dev/research/data
fstype: ext4
state: mounted
when: ansible_lvm.vgs.research is defined
- name: Volume does not exist
debug:
msg: Volume does not exist
when: ansible_lvm.vgs.research is not defined
[yuxb@controller web 16:30:01]$ 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 ~ 16:33:32]# df /data/
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/mapper/research-data 3966144 15992 3728968 1% /data
[root@node1 ~ 16:33:39]# df /data -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/research-data 3.8G 16M 3.6G 1% /data
部署文件到受管主机
修改文件并将其复制到主机
file 模块
**示例1:**创建文件或修改文件属性
[yuxb@controller web 16:31:19]$ vim playbook.yml
[yuxb@controller web 17:08:29]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: Touch a file and set permissions
file:
path: /tmp/testfile
owner: yuxb
group: wheel
mode: 0640
state: touch
[yuxb@controller web 16:31:07]$ ansible-playbook playbook.yml
PLAY [node1] ****************************************************************************************
TASK [Touch a file and set permissions] *************************************************************
changed: [node1]
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@node1 ~ 17:13:27]# ls -l /tmp/testfile
-rw-r----- 1 yuxb wheel 0 8月 14 17:13 /tmp/testfile
**示例2:**删除文件
state: absent
[yuxb@controller web 17:23:51]$ ansible-playbook playbook.yml
[root@node1 ~ 17:13:48]# ls -l /tmp/testfile
ls: 无法访问/tmp/testfile: 没有那个文件或目录
lineinfile 模块
示例1:确保文件中存在特定行
[yuxb@controller web 17:08:32]$ vim playbook.yml
[yuxb@controller web 18:44:27]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: add line
lineinfile:
path: /tmp/testfile
line: 'Add this line to file'
state: present
create: yes
[yuxb@controller web 17:24:19]$ ansible-playbook playbook.yml
PLAY [node1] ****************************************************************************************
TASK [add line] *************************************************************************************
changed: [node1]
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@node1 ~ 17:24:26]# ls -l /tmp/testfile
-rw-r--r-- 1 root root 22 8月 14 18:44 /tmp/testfile
[root@node1 ~ 18:45:06]# cat /tmp/testfile
Add this line to file
还可以在特定位置插入:
[yuxb@controller web 18:47:41]$ vim playbook.yml
[yuxb@controller web 18:48:08]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: add line
lineinfile:
path: /tmp/testfile
line: my
insertbefore: file
state: present
create: yes
[yuxb@controller web 18:44:52]$ ansible-playbook playbook.yml
[root@node1 ~ 18:45:13]# cat /tmp/testfile
my
Add this line to file
替换文本行:
[yuxb@controller web 18:49:13]$ vim playbook.yml
[yuxb@controller web 18:49:42]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: add line
lineinfile:
path: /tmp/testfile
line: my
regexp: file
state: present
create: yes
[yuxb@controller web 18:47:45]$ ansible-playbook playbook.yml
[root@node1 ~ 18:47:47]# cat /tmp/testfile
my
my
replace 模块
[yuxb@controller web 18:53:54]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: add line
replace:
path: /tmp/testfile
replace: hello world
regexp: my
[yuxb@controller web 18:53:22]$ ansible-playbook playbook.yml
[root@node1 ~ 18:49:49]# cat /tmp/testfile
hello world
hello world
blockinfile 模块
[yuxb@controller web 18:53:55]$ vim playbook.yml
[yuxb@controller web 18:59:17]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: add line
blockinfile:
path: /tmp/testfile
block: |
line 1 in file
line 2 in file
state: present
[yuxb@controller web 18:53:58]$ ansible-playbook playbook.yml
[root@node1 ~ 18:54:14]# cat /tmp/testfile
hello world
hello world
# BEGIN ANSIBLE MANAGED BLOCK
line 1 in file
line 2 in file
# END ANSIBLE MANAGED BLOCK
copy 模块
示例1:将控制节点上文件拷贝到受管理节点,类似于Linux中scp命令。
[yuxb@controller web 18:59:18]$ vim playbook.yml
[yuxb@controller web 19:01:19]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: copy /tmp/testfile to remote node
copy:
src: /tmp/testfile
dest: /tmp
[yuxb@controller web 19:03:34]$ echo "hello from controller" > /tmp/testfile
[yuxb@controller web 19:04:04]$ ansible-playbook playbook.yml
PLAY [node1] ****************************************************************************************
TASK [copy /tmp/testfile to remote node] ************************************************************
changed: [node1]
PLAY RECAP ******************************************************************************************
node1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@node1 ~ 19:02:47]# cat /tmp/testfile
hello from controller
stat 模块
**stat 模块检索文件的信息,类似于Linux stat命令。**参数提供检索文件属性、确定文件校验和等功能。
stat 模块返回一个包含文件状态数据的值的散列字典,允许您使用单独的变量引用各条信息。
示例:
[yuxb@controller web 19:01:20]$ vim playbook.yml
[yuxb@controller web 19:04:59]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- stat:
path: /tmp/testfile
checksum_algorithm: md5
register: result
- debug:
msg: "/tmp/testfile md5 is {{ result.stat.checksum }}"
- debug:
var: result
[yuxb@controller web 19:05:05]$ ansible-playbook playbook.yml
PLAY [node1] ****************************************************************************************
TASK [stat] *****************************************************************************************
ok: [node1]
TASK [debug] ****************************************************************************************
ok: [node1] => {
"msg": "/tmp/testfile md5 is 610c7f16071ca402c0cba45e4b2af437"
}
TASK [debug] ****************************************************************************************
ok: [node1] => {
"result": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"failed": false,
"stat": {
"atime": 1755169454.854485,
"attr_flags": "",
"attributes": [],
"block_size": 4096,
"blocks": 8,
"charset": "us-ascii",
"checksum": "610c7f16071ca402c0cba45e4b2af437",
"ctime": 1755169447.1454144,
"dev": 64768,
"device_type": 0,
"executable": false,
"exists": true,
"gid": 0,
"gr_name": "root",
"inode": 67588143,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mimetype": "text/plain",
"mode": "0644",
"mtime": 1755169446.892412,
"nlink": 1,
"path": "/tmp/testfile",
"pw_name": "root",
"readable": true,
"rgrp": true,
"roth": true,
"rusr": true,
"size": 22,
"uid": 0,
"version": "1806842840",
"wgrp": false,
"woth": false,
"writeable": true,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
}
}
PLAY RECAP ******************************************************************************************
node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
fetch 模块
从受管节点检索文件,例如将被管理节点文件先取到控制节点,然后用于分发到其他节点。诸如SSH公钥之类的文件。
示例:
[yuxb@controller web 19:05:02]$ vim playbook.yml
[yuxb@controller web 19:07:28]$ cat playbook.yml
---
- hosts: node1
gather_facts: no
tasks:
- name: fetch file from remote node
fetch:
src: /tmp/testfile
dest: /tmp
[yuxb@controller web 19:05:06]$ ansible-playbook playbook.yml
[yuxb@controller web 19:08:08]$ tree /tmp/node1/
/tmp/node1/
└── tmp
└── testfile
1 directory, 1 file