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')
相关推荐
七夜zippoe13 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
Fcy64815 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满15 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠15 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
Harvey90315 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技16 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀17 小时前
Linux环境变量
linux·运维·服务器
zzzsde17 小时前
【Linux】进程(4):进程优先级&&调度队列
linux·运维·服务器
聆风吟º18 小时前
CANN开源项目实战指南:使用oam-tools构建自动化故障诊断与运维可观测性体系
运维·开源·自动化·cann
NPE~18 小时前
自动化工具Drissonpage 保姆级教程(含xpath语法)
运维·后端·爬虫·自动化·网络爬虫·xpath·浏览器自动化