从Git删除与从集群消失
一句话总结
从 Git 删除表示删除意图,而是否真正删除,取决于自动 prune 策略、object-level protection rule 与批准。
为什么需要它
清理 deployment repository 中不再使用的 config file 时,review 可能只显示删除一个文件。但如果该文件定义了 Namespace 或 persistent data 的连接,这个小 diff 在生产环境中就会变成大规模删除。GitOps 的持续 reconciliation 不仅用于创建和修改,删除同样是匹配 desired state 的动作。反过来,如果 Git 中消失的所有 object 都永久保留,unused resource 会不断累积,也难以判断责任人。需要的不是完全关闭删除,而是制定标准:哪些对象自动删除,哪些对象必须 review 后再删除。
本实验不写入真实数据,而用包含短字符串 disposable-exercise 的 ConfigMap 复现 lifecycle。这是为了学习与生产相同的判断结构,同时避免造成数据丢失。不能把它解释为已经测试真实 PVC retention 与 recovery。能够重新 apply 同名 YAML,与能够恢复数据,是两件不同的事。
工作原理
首先分离三个对象:Git directory 保存 desired state;Application 指定把哪个 directory 应用到哪个 cluster;ConfigMap 是由此创建的 actual object。从 Git path 移除 object 所触发的 prune,与删除 Application 本身时的资源清理,是两类不同事件。
开启自动同步,并不等于同时开启自动删除。Application 的 automated.prune 决定 delete automation。本实验将其设为 true,再比较 object-level option。下表的关键在于,不把不同 status column 压缩成一个“成功”。
| 从 Git 删除的 object | actual object | sync.status | operationState.phase |
|---|---|---|---|
| 无额外保护的 normal | 已删除 | Synced | Succeeded |
| Prune=false 的 retained | 保留 | OutOfSync | 可能为 Succeeded |
| Prune=confirm 的 confirmed,批准前 | 保留 | OutOfSync | Running |
| confirmed,批准后 | 已删除 | Synced | Succeeded |
Prune=false 会跳过 deletion operation。Argo CD 原本要执行的删除仍处于待处理状态,因此 desired state 与 actual state 不同,但这并不表示 reconciler 故障。此时 syncResult 中相应 object 会记录 PruneSkipped。确认 protected object 保留的原因后,恢复 Git definition 可以让同一 UID 再次变为 Synced。为了消除 OutOfSync 而手动删除 actual object,会首先破坏 protection rule 的目的。该恢复只是让已经保留的 actual object 再次匹配 Git,可能不需要创建新的 sync operation。因此,应确认 current sync.revision、Synced 与相同 UID,不要把产生新 operation 本身当作成功条件。
Prune=confirm 会暂停需要 review 的删除。此时 Running 不一定表示无限故障,也可能只是等待批准。可以从 operationState.message 与 pending deletion resource list 查明等待原因。批准也可以通过在 Application 的 argocd.argoproj.io/deletion-approved annotation 中记录时间完成。重点是:approval marker 添加在 Application,而不是某一个 ConfigMap。如果除了已 review 的对象之外,又出现其他 deletion candidate,就不能沿用之前的判断。
Delete=false 名称相似,但它控制的是删除 Application 时的清理,并不是从 Git 移除 resource 所触发 prune 的别名。编写运维文档时,应为“删除”明确主语:先区分从 Git 移除了什么、是否删除 Application、还是直接删除 actual object,才能选择正确 option。
现场会遇到的情况
团队把 config ownership 迁移到另一 repository 时,如果先移除旧 Application 中的 Git definition,自动 prune 可能先删除 object。仅复制一项 protection option 并不能完成迁移设计,还必须确定新旧 owner 的 scope、两个 reconciler 同时管理同一 object 的时间,以及失败时回到哪个 source。本单元的两个 Application 管理不同 ConfigMap,因此不会制造这种 dual ownership。
读取证据时,应同时查看 name 与 UID。在 Kubernetes 中可以创建同名新 object,因此不能仅因名称相同就判断对象得到保留。反过来,Git commit 变化也不表示 object 必然重新创建。把 actual state 与 desired state 的坐标并排保存,会很有帮助。
git -C /srv/cgoa-prune-safety rev-parse HEAD
kubectl -n argocd get application cgoa-prune-safety -o json
kubectl -n cgoa-prune-safety get configmaps -o json
这里需要读取的是 commit SHA、source.path、destination.namespace、sync.revision、pending deletion list 与 UID。如果无法说明每项含义,只把完整输出贴入报告,不能代替验证。
下一项实验要做什么
亲自创建并确认普通删除、保留删除对象、Git 恢复与等待批准。在批准阶段,把前一次观测中的 Application UID 与目标 ConfigMap UID 写入答案。helper 会在当前 target 不一致时停止。此限制用于防止学习过程中误操作错误 scope,并不是能够阻止 administrator root 一切操作的安全保证。
官方文档
- Argo CD sync option:比较 Prune 与 Delete 分别适用的事件。
- Kubernetes object name 与 UID:区分名称复用与 object identity。