描述与实现分离是什么
声明式配置里,描述文件只表达结果:这个包要装、这个服务要启动、这个文件内容应为这样。达到结果的步骤——先检测再安装、先备份再替换、失败要不要重试——交给工具的实现层决定。分离的价值是同一份描述可以反复执行而不重复动作(幂等),也能作为校验依据:执行完读回实际状态与描述比对,不一致就是漂移。代价是工具不认识的自定义动作需要落回命令式,而命令式容易破坏幂等性,必须显式声明收敛条件。
基本信息
| 适用平台 | Ansible 模块、Terraform 资源、Kubernetes spec 与控制器、Puppet/Chef/Salt 资源 |
|---|---|
| 描述载体 | 清单与 Playbook、资源定义文件、CRD、状态与类定义 |
| 实现层 | 模块、Provider、控制器与调和循环、资源提供者 |
| 关键约束 | 描述中不得夹带执行步骤;命令式补丁须声明幂等保证 |
典型使用场景
- 跨环境一致:同一份描述在生产与预发落地相同状态
- 幂等重跑:首次执行做变更,二次执行报告无变化
- 漂移检测:描述文件本身就是比对的期望值来源
- 自动纠偏:控制器按周期调和,把被手工改动的资源拉回期望状态
- 变更评审:描述文件可 diff、可版本化,评审对象就是将要生效的内容
- 替换一次性脚本:把「怎么装」的历史脚本转成「应该装什么」的长期描述
写法对照
写法对照
# 声明式:只说结果,工具负责检测与安装
- name: 确保依赖包存在
ansible.builtin.package:
name: nginx
state: present
# 命令式补丁:必须自带幂等条件,否则每次执行都算变更
- name: 仅在文件缺失时才初始化
ansible.builtin.command:
cmd: /usr/local/bin/app --init
creates: /var/lib/app/initialized
# 另一种收敛条件:用 changed_when 修正变更判定
- name: 查询状态并据结果决定后续
ansible.builtin.command: /usr/local/bin/app status
register: app_status
changed_when: false # 只读命令,不应被记成变更
# 校验:执行完读回实际状态,与描述比对
# 示例:kubectl get -o yaml 与清单 diff,或对配置文件做 checksum 比对分层职责对照
| 层面 | 写什么 | 好处 | 代价 |
|---|---|---|---|
| 描述层 | 期望状态与约束 | 可评审、可版本化、可复用 | 表达能力受工具限制 |
| 实现层 | 模块与调和逻辑 | 幂等、自带重试与检测 | 黑盒化,需理解关键行为 |
| 命令式补丁 | 具体命令与条件 | 覆盖一切特殊场景 | 幂等需自己保证 |
| 校验层 | 读回与比对 | 确认描述真的生效 | 需要额外采集成本 |
判断一条描述是否合格,看它能否连续执行两次:第一次报告变更、第二次报告无变化。若第二次仍报变更,说明描述里混入了每次都执行的命令式动作,需要补收敛条件。
声明式不等于安全:删除类语义(状态为缺席、按清单裁剪多余资源)一旦写错,会按描述忠实删掉真实数据。写这类描述前先做差异预览,并确认目标范围。