Ansible自动化(八):条件语句

一、条件语句核心价值

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执行此任务"

六、练习题(从易到难)

基础题

  1. 编写Playbook:仅在内存≥4096MB的主机上创建 /opt/big_mem.txt 文件,内容为当前主机名。
  2. 编写Playbook:CentOS主机用yum安装nginx,Ubuntu主机用apt安装nginx。

进阶题

  1. 编写Playbook:安装nginx后,若安装状态为changed则重启nginx,否则输出"nginx已安装无需重启"。
  2. 编写Playbook:尝试创建 /usr/share/nginx/html/test/index.html 文件,若目录不存在则自动创建目录,无论是否成功都输出"任务执行完成"。

综合题

  1. 编写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')
相关推荐
MrYang202018 小时前
Vcenter vsphere 登录报错
运维·vmware
suamt18 小时前
记录windows下如何运行docker程序
运维·docker·容器
China_Yanhy18 小时前
Ansible 工业级项目标准化架构指南 (V1.0)
架构·ansible
小宇的天下18 小时前
Calibre 3Dstack --每日一个命令days8【connected】(3-8)
运维·服务器·性能优化
ICT系统集成阿祥18 小时前
服务器网卡绑定(bond)7种模式详解
运维·服务器·bond·网卡绑定·服务器链路聚合
wulalalalalalalal18 小时前
Linux 内网服务器通过代理访问外网
linux·运维·服务器
oMcLin18 小时前
如何在RHEL 9上配置并优化Kubernetes 1.23高可用集群,提升大规模容器化应用的自动化部署与管理?
kubernetes·自动化·php
C_心欲无痕18 小时前
ts - 模板字面量类型与 `keyof` 的魔法组合:`keyof T & `on${string}`使用
linux·运维·开发语言·前端·ubuntu·typescript
乾元18 小时前
无线定位与链路质量预测——从“知道你在哪”,到“提前知道你会不会掉线”的网络服务化实践
运维·开发语言·人工智能·网络协议·重构·信息与通信