首页/系统与基础设施/自动化运维速查/事实驱动的条件分支设计
Ansible 核心70 条

事实驱动的条件分支设计

用事实做条件分支时,先把判断归纳成少量派生变量,让规则只有一处;版本比较用专用测试而非字符串比较,易变条件放在使用点附近判断,未覆盖的分支必须显式失败,并用检查模式验证变量生成顺序。

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​‌‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌‌‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​‌‌​​‌​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​‌‌​​​​‌​‌‌​‌‌​​​​‌​‌‌​‌​‌‌​​​‌​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌​​‌‌‌​‌‌​‌‌‌​​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​

条件分支要有单一判断点

用事实做条件判断的常见退化形态是:同一个判断(例如“是不是这个发行版家族”)在几十个任务的 when 里各写一遍。改一次判断规则要翻遍全剧,还容易漏。合理的设计是先把事实归纳成少量派生变量,在剧本开头算一次,后面所有分支都引用这些派生变量,判断逻辑只有一处。

基本信息

数据来源采集到的事实、注册的变量、清单与分组变量
判断写法任务的 when、块级 when、条件表达式中的比较与测试
派生变量用 set_fact 把复杂判断收敛成一两个布尔或类别变量
版本比较版本号不要按字符串比较,用专门的版本比较测试
兜底处理未覆盖的分支要显式失败,不要安静跳过
验证方式检查模式预演,并对各分支分别构造一次真实运行

典型使用场景

  • 跨发行版适配:按家族选择包名、服务名与配置路径
  • 虚拟化差异:物理机与虚拟机在磁盘、网卡命名上处理不同
  • 容量判断:按内存或 CPU 数量决定工作进程数
  • 安全机制差异:是否启用强制访问控制、防火墙管理方式不同
  • 新老版本兼容:某参数仅在较新版本可用,需要按版本分支
  • 现实条件判断:某挂载点是否存在,决定是否走额外的配置步骤

先归纳再分支

先归纳再分支
- name: 归纳派生变量,判断只写一次
  ansible.builtin.set_fact:
    is_debian_family: "{{ ansible_os_family == 'Debian' }}"
    supports_systemd_unit: "{{ ansible_service_mgr is defined and ansible_service_mgr == 'systemd' }}"
    worker_processes: >-
      {{ [2, (ansible_processor_vcpus | default(2) * 2)] | min }}

- name: 按派生变量分支,条件保持简单
  ansible.builtin.template:
    src: app.conf.j2
    dest: "{{ conf_dir }}/app.conf"
  vars:
    conf_dir: "{{ is_debian_family | ternary('/etc/apache2', '/etc/httpd') }}"

- name: 版本判断用版本比较,不用字符串比较
  ansible.builtin.debug:
    msg: "该版本支持新参数"
  when: ansible_distribution_version is version('8.0', '>=')

- name: 未覆盖的情况主动失败
  ansible.builtin.fail:
    msg: "未支持的发行版家族 {{ ansible_os_family }}"
  when: not (is_debian_family or ansible_os_family == 'RedHat')

分支设计对照

做法优点风险
每个任务各写 when改动局部可见规则分散,改一处漏多处
开头算派生变量判断单一来源,任务可读需确保派生变量先于使用它的任务生成
块级 when整段共享同一条件块内任务无法各自调整条件,需要拆块
按家族引入不同任务文件差异集中在一个文件里文件数量增加,需明确引入条件
用映射表取代条件任务里没有分支,最易读表项需覆盖全部目标取值,否则会出现空值
派生变量必须在被使用之前生成,且不能依赖尚未采集的事实。检查模式预演能暴露“变量还没定义就被引用”这类顺序问题,这也是它最实用的用途之一。
不要基于易变的事实做长链路决策:可用内存、挂载点在执行过程中可能变化,用它们在剧本开头决定后续所有动作,会在中途环境改变时产生不一致的结果。易变条件应放在使用它的那一步附近判断。

分支数量要控制

每增加一个分支,验证成本近似翻倍:两个条件就是四种组合,实际很难全部覆盖。设计时优先把差异变成数据(映射表),其次是把分支收敛成一个派生变量,最后才考虑在任务里写条件。分支越多,就越需要把兜底失败写清楚。

易错点

  • 同一判断散落在几十个任务的 when 里,规则变更时漏改,行为逐渐不一致
  • 版本号按字符串比较,出现版本越高判断越小的错误结论
  • 引用未采集或未定义的字段做条件,任务在条件求值阶段就失败
  • 用易变事实(可用内存、挂载状态)决定整条流程走向,中途变化导致结果不一致
  • 条件分支没有兜底,未覆盖的系统安静跳过,配置实际未下发却显示成功

数据来源与参考

  • Ansible 官方文档 Conditionals 与 when 语句
  • Ansible 官方文档 ansible.builtin.set_fact 模块
  • Ansible 官方文档 Tests(version 等比较测试)

整理校对:其其网ICT文档中心 · 本页地址:docs.qiqicto.com/automation/fact-conditional-branch-design.html

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​‌‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌‌‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​‌‌​​‌​​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​​‌‌​​​​‌​‌‌​‌‌​​​​‌​‌‌​‌​‌‌​​​‌​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌​​‌‌‌​‌‌​‌‌‌​​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​