一、条件语句核心价值
Ansible 条件语句(when)是实现任务按需执行的核心,让 Playbook 具备"判断能力",解决不同主机、不同场景下的差异化执行问题,是 Ansible 从"简单批量执行"到"智能自动化"的关键。
二、核心应用场景(附场景说明)
| 场景分类 | 具体说明 | 典型案例 |
|---|---|---|
| 硬件/环境判断 | 仅满足硬件条件的主机执行任务 | 内存≥8G 的主机才部署数据库 |
| 任务依赖判断 | 前序任务执行结果决定后续任务 | 软件安装成功后才启动服务 |
| 跨系统适配 | 不同操作系统执行不同命令 | CentOS 用 yum、Ubuntu 用 apt 安装软件 |
| 配置变更校验 | 配置修改成功后才重启服务 | 配置文件修改生效后重启 Nginx,失败则回滚 |
三、基础语法与规范
1. 核心格式
yaml
- name: 任务名称
模块名:
模块参数
when: 条件表达式 # when 与任务名/模块名同级
2. 关键规范
- 条件中的变量直接写名称 ,无需
{``{ }}(如when: ansible_hostname == "node01"); - 字符串需用双引号包裹(如
"node01"); - 条件表达式支持数值、逻辑、路径等多种判断规则。
3. 基础示例(按主机名创建文件)
yaml
- hosts: node01,node02
gather_facts: yes # 必须收集facts才能获取主机名变量
tasks:
- name: 为node01创建文件
copy:
content: "hello node01\n"
dest: /opt/node01.example.com.txt
when: ansible_hostname == "node01" # 仅node01执行
- name: 为node02创建文件
copy:
content: "hello node02\n"
dest: /opt/node02.example.com.txt
when: ansible_hostname == "node02" # 仅node02执行
四、常用条件测试类型(附注释+示例+场景)
1. 数值比较(判断大小/相等)
| 运算符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
== |
相等为真 | when: ansible_memtotal_mb == 8192 |
判断内存是否为8G |
!= |
不等为真 | when: ansible_processor_vcpus != 4 |
CPU核心数不是4的主机执行任务 |
> |
大于为真 | when: ansible_memtotal_mb > 4096 |
内存大于4G的主机部署应用 |
< |
小于为真 | when: ansible_distribution_version < "8" |
CentOS 7及以下执行兼容操作 |
>= |
大于等于为真 | when: ansible_memtotal_mb >= 8192 |
内存≥8G才部署数据库 |
<= |
小于等于为真 | when: ansible_processor_vcpus <= 8 |
CPU≤8核的主机限制并发 |
2. 逻辑运算符(多条件组合)
| 运算符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
and |
逻辑与(都满足) | when: ansible_distribution == "CentOS" and ansible_distribution_version == "8" |
仅CentOS 8主机执行 |
or |
逻辑或(满足其一) | when: ansible_distribution == "CentOS" or ansible_distribution == "Ubuntu" |
CentOS或Ubuntu都执行 |
not |
逻辑非(取反) | when: not ansible_hostname == "node01" |
排除node01执行 |
() |
组合表达式 | when: (ansible_distribution == "CentOS" and ansible_distribution_version == "8") or (ansible_distribution == "Ubuntu" and ansible_distribution_version == "20.04") |
多系统多版本适配 |
3. 路径测试(判断文件/目录状态)
注意:路径是被控端的路径,非主控端!
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is file |
是文件为真 | when: "/etc/nginx/nginx.conf" is file |
确认配置文件存在后再修改 |
is directory |
是目录为真 | when: "/data" is directory |
确认数据目录存在后再挂载 |
is link |
是软链接为真 | when: "/etc/nginx.conf" is link |
检查配置文件是否为软链接 |
is mount |
是挂载点为真 | when: "/data" is mount |
确认挂载成功后再写入数据 |
exists |
路径存在为真 | when: "/var/log/nginx" exists |
确认日志目录存在后配置日志 |
示例:
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 仅当/opt/test.txt是软链接时输出提示
debug:
msg: "路径是软链接"
when: "/opt/test.txt" is link
4. 变量测试(判断变量状态)
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is defined |
变量已定义(无论值是否为空) | when: app_version is defined |
确认版本变量存在后再部署 |
is undefined |
变量未定义 | when: app_port is undefined |
变量未定义时使用默认端口 |
is none |
变量存在且值为空 | when: app_config is none |
配置变量为空时加载默认配置 |
示例:
yaml
- hosts: node01
gather_facts: no
vars:
testvar: "hello" # 已定义且有值
testvar1: # 已定义但为空
tasks:
- name: 变量已定义
debug:
msg: "testvar存在"
when: testvar is defined # 执行
- name: 变量未定义
debug:
msg: "testvar2不存在"
when: testvar2 is undefined # 执行
- name: 变量为空
debug:
msg: "testvar1为空"
when: testvar1 is none # 执行
5. 任务执行结果测试(判断前序任务状态)
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is success |
任务执行成功 | when: result is success |
命令执行成功后备份数据 |
is failure |
任务执行失败 | when: result is failure |
命令失败时发送告警 |
is changed |
任务导致状态变更 | when: install_result is changed |
软件安装成功(状态变更)后启动服务 |
is skipped |
任务被跳过 | when: result is skipped |
任务被跳过时记录日志 |
示例(安装软件后根据结果启动服务):
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 安装vsftpd
yum:
name: vsftpd
state: present
register: install_result # 注册执行结果到变量
- name: 仅安装成功且状态变更时启动服务
systemd:
name: vsftpd
state: started
enabled: yes
when: install_result is changed # 仅安装(新增/更新)后执行
6. 字符串测试(判断大小写)
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is lower |
全小写为真 | when: app_name is lower |
检查配置项是否为小写规范 |
is upper |
全大写为真 | when: app_env is upper |
检查环境变量是否为大写规范 |
示例:
yaml
- hosts: node01
gather_facts: no
vars:
test1: "ansible" # 全小写
test2: "ANSIBLE123" # 字母全大写,数字不影响
tasks:
- name: 全小写提示
debug:
msg: "test1是全小写"
when: test1 is lower # 执行
- name: 全大写提示
debug:
msg: "test2是全大写"
when: test2 is upper # 执行
7. 数值特性测试(判断奇偶/整除)
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is even |
偶数为真 | when: num is even |
偶数主机ID执行备份任务 |
is odd |
奇数为真 | when: num is odd |
奇数主机ID执行同步任务 |
divisibleby(num) |
整除num为真 | when: num is divisibleby(3) |
每3台主机执行一次清理任务 |
示例:
yaml
- hosts: node01
gather_facts: no
vars:
num1: 6 # 偶数
num2: 7 # 奇数
num3: 15 # 可被3整除
tasks:
- debug: msg="num1是偶数"
when: num1 is even
- debug: msg="num2是奇数"
when: num2 is odd
- debug: msg="num3可被3整除"
when: num3 is divisibleby(3)
8. 版本比较(判断软件版本)
核心:
版本字符串 is version('目标版本', '比较符')支持的比较符:
> (gt)、>= (ge)、< (lt)、<= (le)、== (eq)、!= (ne)
示例(检查Nginx版本是否达标):
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 获取Nginx版本
shell: nginx -v 2>&1 | awk -F '/' '{print $2}' # 提取纯版本号(如1.20.1)
register: nginx_version
- name: 仅版本大于1.13.0时提示
debug:
msg: "Nginx版本达标"
when: nginx_version.stdout is version('1.13.0', 'gt')
适用场景:部署应用前检查依赖软件版本是否满足要求。
9. 集合/列表测试
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is subset |
子集为真 | when: os_list is subset(sys_list) |
检查目标系统是否在支持列表内 |
is superset |
父集为真 | when: sys_list is superset(os_list) |
检查支持列表是否包含目标系统 |
in |
字符串在列表中为真 | when: "CentOS" in sys_list |
检查系统是否在支持列表内 |
示例:
yaml
- hosts: node01
gather_facts: no
vars:
sys_list: ["CentOS", "Ubuntu", "RHEL"] # 支持的系统列表
os_list: ["CentOS"] # 目标系统列表
current_os: "CentOS"
tasks:
- debug: msg="os_list是sys_list的子集"
when: os_list is subset(sys_list)
- debug: msg="sys_list包含CentOS"
when: current_os in sys_list # 变量形式
- debug: msg="字符串匹配CentOS"
when: "CentOS" in sys_list # 字符串形式
10. 类型测试(判断变量类型)
| 测试符 | 说明 | 示例 | 适用场景 |
|---|---|---|---|
is string |
变量是字符串 | when: app_name is string |
检查配置项是否为字符串类型 |
is number |
变量是数字 | when: app_port is number |
检查端口号是否为数字类型 |
示例:
yaml
- hosts: node01
gather_facts: no
vars:
app_name: "nginx" # 字符串
app_port: 8080 # 数字
tasks:
- debug: msg="app_name是字符串"
when: app_name is string
- debug: msg="app_port是数字"
when: app_port is number
五、高级用法
1. 多任务共享条件(block)
当多个任务需要同一个条件时,用 block 包裹,避免重复写 when。
示例(node01部署Nginx,node02部署MariaDB):
yaml
- hosts: node01,node02
gather_facts: yes
tasks:
# node01执行的任务块
- name: node01部署Nginx
block:
- name: 安装Nginx
yum: name=nginx state=present
- name: 创建首页文件
copy: content="hello ansible\n" dest=/usr/share/nginx/html/index.html
- name: 启动Nginx
service: name=nginx state=started enabled=yes
when: ansible_hostname == "node01" # 整个block仅node01执行
# node02执行的任务块
- name: node02部署MariaDB
block:
- name: 安装MariaDB
yum: name=mariadb-server state=present
- name: 启动MariaDB
systemd: name=mariadb state=started enabled=yes
when: ansible_hostname == "node02" # 整个block仅node02执行
2. block 错误处理(rescue/always)
| 关键字 | 说明 | 适用场景 |
|---|---|---|
rescue |
block内任务失败时执行 | 配置文件目录不存在时自动创建 |
always |
无论block是否失败都执行 | 无论部署是否成功,都记录日志 |
综合示例(创建分区+格式化+挂载):
yaml
- hosts: node01
tasks:
- name: 磁盘分区与挂载
block:
# 尝试创建100G分区(失败则走rescue)
- name: 创建100G分区
parted:
device: /dev/sda
number: 1
state: present
part_end: 100GiB
rescue:
# 分区失败时创建20G分区
- name: 创建20G分区
parted:
device: /dev/sda
number: 1
state: present
part_end: 20GiB
always:
# 无论分区大小,都格式化并挂载
- name: 格式化为xfs
filesystem: fstype=xfs dev=/dev/sda1
- name: 创建/data目录
file: path=/data state=directory
- name: 挂载到/data
mount: path=/data src=/dev/sda1 fstype=xfs state=mounted
when: ansible_devices.sda is defined # 仅存在sda硬盘时执行
3. 剧本条件退出(终止执行)
方式1:fail模块(直接终止并提示)
yaml
- hosts: node01
gather_facts: yes
tasks:
- name: 非Ubuntu系统直接退出
fail:
msg: "仅支持Ubuntu系统,剧本终止执行"
when: ansible_distribution != "Ubuntu"
- name: 安装vsftpd(仅Ubuntu执行)
apt: name=vsftpd state=present
方式2:failed_when(任务执行后终止)
yaml
- hosts: node01
gather_facts: yes
tasks:
- name: 执行命令(非Ubuntu则标记为失败)
shell: touch /opt/test.txt
failed_when: ansible_distribution != "Ubuntu" # 条件满足则任务失败,剧本终止
- name: 后续任务(非Ubuntu则不会执行)
debug: msg="仅Ubuntu执行此任务"
六、练习题(从易到难)
基础题
- 编写Playbook:仅在内存≥4096MB的主机上创建
/opt/big_mem.txt文件,内容为当前主机名。 - 编写Playbook:CentOS主机用
yum安装nginx,Ubuntu主机用apt安装nginx。
进阶题
- 编写Playbook:安装nginx后,若安装状态为
changed则重启nginx,否则输出"nginx已安装无需重启"。 - 编写Playbook:尝试创建
/usr/share/nginx/html/test/index.html文件,若目录不存在则自动创建目录,无论是否成功都输出"任务执行完成"。
综合题
- 编写Playbook实现:
- 仅在CentOS 7/8或Ubuntu 20.04主机上执行;
- 检查
/data是否为挂载点,不是则挂载/dev/sdb1到/data; - 安装mysql,若安装成功则启动服务,失败则输出"mysql安装失败";
- 最后判断mysql版本是否≥5.7,达标则输出"版本符合要求",否则终止剧本。
练习题参考答案(核心片段)
基础题1
yaml
- hosts: all
gather_facts: yes
tasks:
- name: 创建文件(仅内存≥4G)
copy:
content: "{{ ansible_hostname }}\n"
dest: /opt/big_mem.txt
when: ansible_memtotal_mb >= 4096
基础题2
yaml
- hosts: all
gather_facts: yes
tasks:
- name: CentOS安装nginx
yum: name=nginx state=present
when: ansible_distribution == "CentOS"
- name: Ubuntu安装nginx
apt: name=nginx state=present update_cache=yes
when: ansible_distribution == "Ubuntu"
进阶题3
yaml
- hosts: all
gather_facts: no
tasks:
- name: 安装nginx
yum: name=nginx state=present
register: install_result
- name: 重启nginx(仅安装变更时)
service: name=nginx state=restarted
when: install_result is changed
- name: 无需重启提示
debug: msg="nginx已安装无需重启"
when: not install_result is changed
进阶题4
yaml
- hosts: all
gather_facts: no
tasks:
- name: 创建文件
block:
- copy:
content: "test\n"
dest: /usr/share/nginx/html/test/index.html
rescue:
- name: 创建目录
file: path=/usr/share/nginx/html/test state=directory
# 重新执行创建文件
- copy:
content: "test\n"
dest: /usr/share/nginx/html/test/index.html
always:
- debug: msg="任务执行完成"
综合题5(核心片段)
yaml
- hosts: all
gather_facts: yes
tasks:
# 系统版本判断
- name: 系统版本校验
fail: msg="仅支持CentOS 7/8或Ubuntu 20.04"
when: >
not (
(ansible_distribution == "CentOS" and ansible_distribution_major_version in ["7","8"]) or
(ansible_distribution == "Ubuntu" and ansible_distribution_version == "20.04")
)
# 挂载/data
- name: 检查/data挂载
mount:
path: /data
src: /dev/sdb1
fstype: xfs
state: mounted
when: "/data" is not mount
# 安装mysql
- name: 安装mysql
yum: name=mysql-community-server state=present
register: mysql_install
ignore_errors: true
- name: 启动mysql
service: name=mysqld state=started enabled=yes
when: mysql_install is success
- name: 安装失败提示
debug: msg="mysql安装失败"
when: mysql_install is failure
# 版本校验
- name: 获取mysql版本
shell: mysql -V | awk '{print $5}' | cut -d '.' -f1,2
register: mysql_version
when: mysql_install is success
- name: 版本达标提示
debug: msg="版本符合要求"
when: mysql_version.stdout is version('5.7', 'ge')
- name: 版本不达标退出
fail: msg="mysql版本低于5.7,剧本终止"
when: mysql_version.stdout is version('5.7', 'lt')