重写同一设置的两个控制器
一句话总结
直接修改 ApplicationSet 创建的 Application 后,parent 可能会再次将其恢复。要停止相关操作,必须先确认谁拥有该字段。
为什么需要它
为了调查故障,关闭了自动同步。在 UI 中确认已经关闭,短暂离开后却发现它再次开启。询问同事后,所有人都说没有操作。重启 controller 或反复点击同一按钮,都无法解释问题,因为修改的 target 可能并不是 top-level source。
GitOps 中并非只有一个 reconciliation loop。Application controller 会让实际 resource 匹配 Application 指定的 Git target;ApplicationSet controller 则会让实际 Application object 匹配由 generator 与 template 生成的 Application target。operator 修改 child Application 的 automation field 后,第二个 loop 可能把它视为 drift 并恢复。修改从 Git 读取的 ConfigMap,与修改 Application 自身,属于不同层次的变更。
工作原理
本实验只在 list generator 中放置 alpha 一个项目,目的不是增加 service 数量,而是缩小范围观测 ownership relationship。ApplicationSet template 创建 alpha 的 Application,而该 Application 读取内部 Git 的 apps/alpha directory,创建一个小 ConfigMap。另有独立 Application 和单独 ConfigMap 作为 control group。如果独立目标也一起变化,就说明 scope 选错了。
| reconciler | 读取 target 的位置 | 修改对象 | 维护时要问的问题 |
|---|---|---|---|
| ApplicationSet controller | generator 与 template | Application 配置 | 谁会重新写入这个 automation value? |
| Application controller | Application 指定的 Git | ConfigMap 等 actual resource | 真正应用的是哪个 Git commit? |
ownerReferences 是判断第一种关系的重要线索。从 child Application 的 ownerReferences 确认 kind、name、uid、controller,就能知道它连接到哪个 ApplicationSet。只比较 name 可能漏掉用相同名称重建的 parent,因此也要对照 UID。ConfigMap 则通过 Argo CD tracking annotation 区分由哪个 Application 管理。不能把这两类 ownership information 视为同一机制。
明确关闭自动同步的值是 spec.syncPolicy.automated.enabled=false。不能把 null 或 field 缺失理解成 false。即使 prune 与 selfHeal 为 true,只要 enabled=false,自动同步就处于关闭状态。本环境始终显式把 enabled 写为 boolean,以免猜测 default。
向 child 写入 enabled=false 时,API 接受该变更,与该状态能够维持,也是两件不同的事。实验会先保存 patch response 中的 false 与 resourceVersion,再在后续观测中确认:同一 Application UID 的 enabled 已恢复为 true。这样可以避免把一开始就未成功写入 false 的请求误称为 restore experiment。resourceVersion 是区分观测版本的 opaque string,不能作为时间或 global sequence number 计算。
在 parent template 声明 enabled=false 后,生成 child 的 target 本身就会变化。child 的 false 不再与 parent 存在差异,因此可以保持。template 可能共同应用于多个 child,所以真实团队环境在变更前,应先确认 generator result 与受影响 Application list。实验通过单个 child 限定 scope,但不能据此泛化为大型生产环境也只会改变一个对象。
现场会遇到的情况
设想一个组织要求 development environment 自动部署,而 validation environment 只有人工确认后才部署。如果同一个 template 应用于所有 environment,却只在 validation environment 通过按钮关闭,source 与 actual state 就会冲突。团队要保证的运维方式,必须通过 template 或显式 exception policy 表达。依赖某个人记住每次重新关闭,不是 desired state declaration,而是不断重复的手工步骤。
调查时,应像下面这样读取不同层次的 document。这些命令都只在个人实验 VM 内执行。
kubectl -n argocd get applicationset cgoa-appset-ownership -o json
kubectl -n argocd get application cgoa-appset-ownership-alpha -o json
kubectl -n cgoa-appset-ownership get configmap alpha-config -o json
第一份 document 查看 template 与 generator,第二份查看 ownerReferences 与 automation policy,第三份查看 actual data。仅凭“自动化已关闭”一句话,无法判断停止了哪个 reconciliation loop。
下一项实验要做什么
首先比较 API 接受 child 变更的记录,以及 parent 恢复该变更的记录。随后修改 parent template,再分别查看 child 设置与 Git change 是否应用。ApplicationSet 自身由 helper 应用实验 bootstrap declaration;由更上层 Application 从 Git 协调它的第三个 loop,不在本次范围内。只有明确这条边界,才能避免混淆实验中直接 apply 的声明与生产环境完整 GitOps wiring。
官方文档
- ApplicationSet 变更控制:区分 child 变更与 exception rule。
- 自动同步:确认 enabled 的 true、false、null 含义。