幂等是什么表现
幂等的判定方法很直接:同一份 playbook 对同一台机器连跑两次,第二次应该几乎没有 changed,只有 ok 与 skipping。做到了就意味着重复执行安全:中途失败可以整段重跑、定时收敛可以无脑执行、也才谈得上检查模式预演的可信度。声明式模块自带这个性质,命令类模块默认没有。
基本信息
| 判定方法 | 连跑两次同一 playbook,对比第二次的 changed 数量 |
|---|---|
| 模块属性 | 模块文档中会说明是否支持检查模式与幂等语义 |
| 常见手段 | 用声明式模块替代命令、给命令加 creates/removes、明确 changed_when |
| 配套验证 | 用检查模式看“将会变更”是否为 0 |
| 平台差异 | 包管理与服务管理模块在 Linux、Windows 上名称不同但语义一致 |
| 例外情况 | 某些动作天然有副作用(时间戳、日志追加),需明确取舍 |
典型使用场景
- 流水线反复执行:部署 playbook 每次运行都要求“无变更则不动手”
- 收敛类巡检:定时跑同一份配置,只有真漂移时才产生变更
- 失败后重跑:整段重跑不会把已经完成的部分再改一遍
- 变更评审:按第二次是否 changed 判断一个 playbook 是否可安全重复执行
- 迁移替代:把 shell 写的部署脚本逐步改写成声明式任务,换取幂等
- 回归测试:在测试环境连续执行两遍,作为上线前的固定检查项
从非幂等到幂等
从非幂等到幂等
- name: 不幂等:每次都 changed
ansible.builtin.shell: /usr/local/bin/init-db.sh
- name: 幂等:产物存在就跳过
ansible.builtin.command: /usr/local/bin/init-db.sh
args:
creates: /var/lib/app/db.initialized # 该文件存在则不执行
- name: 用声明式模块替代命令
ansible.builtin.file:
path: /var/lib/app
state: directory
owner: appsvc
group: appsvc
mode: "0750"
- name: 只读动作显式声明不产生变更
ansible.builtin.command: /usr/local/bin/app --version
register: app_ver_check
changed_when: false # 只查询,不代表配置发生变更常见写法的幂等性
| 写法 | 是否幂等 | 说明 |
|---|---|---|
| package/template/service 等声明式模块 | 是 | 按目标状态收敛,已是目标状态则 ok |
| shell 直接执行脚本 | 否 | 每次都执行,一律报告 changed |
| command 带 creates/removes | 是(有条件) | 按条件文件存在与否决定是否执行 |
| file 的 state: touch | 否(默认) | 时间戳每次都更新,除非显式保留时间 |
| lineinfile 用正则定位 | 是(需写对) | 正则不匹配现有内容时会反复追加 |
| command 加 changed_when: false | 假装是 | 不改变实际行为,只是不再报告变更 |
changed_when: false 是给“只读查询”用的标签,不是让非幂等任务变得安全的手段:它只影响报告,不影响是否真的执行。用错了会掩盖掉真实的重复变更,排查会更难。
把非幂等任务混进幂等的 playbook 里,会让第二次运行产生意外副作用:例如重复追加配置行、重复执行数据库初始化。发现此类任务应先加条件或改用声明式模块,再纳入定时执行。
怎么判断一个模块
查模块文档,看它是否声明支持检查模式、参数里有没有“目标状态”语义(present/absent/started/stopped 这类),以及是否有 creates、removes 之类的条件参数。三条都具备的模块基本可以直接依赖;只有一条或都没有的,写任务时必须额外补条件。