TT Lab
开始
学习 学习路径 课程

GitOps 与 Argo CD

应用的值老是被还原——找出字段的主人

在 TT Lab 中继续学习

目标

在 kwok 集群中用服务端应用创建两个管理者,并亲自读取 managedFields。亲手触发冲突与强制、释放字段、scale 子资源的冲突,最后再与 Argo CD 的忽略规则衔接起来。

为什么重要

即使启用了 GitOps,向集群对象写入的主体也不止一个。Argo CD 会写,自动扩缩器会写 replicas,Webhook 会插入容器,人在紧急时会用 kubectl 写。服务端应用是由 API 服务器用记录来保存“谁是这个字段的所有者”的装置。 只有会读这份记录,才能回答三个问题——为什么我的应用被拒绝,为什么强行推入之后下一次被拒绝的是对方,为什么释放了值却没有回到旧值。而且这份记录还会衔接到 Argo CD 这一侧。因为忽略规则中的 managedFieldsManagers 不是按字段路径,而是按这份所有权记录来选择比较对象。

步骤

  1. 在 kwok 集群中创建命名空间 ga-ssa,并在 /root/ga-ssa/deploy.yaml 中写下 Deployment web——spec.replicas 为 2,选择器和标签为 app: web,一个容器(web,镜像 nginx:1.25)。用 kubectl apply --server-side --field-manager=gitops 应用。
  2. 用 kubectl -n ga-ssa get deploy web -o json --show-managed-fields 读取所有权记录,每个管理者一行,以 <관리자> | <연산>(占位符依次为管理者、操作类型)的格式保存到 /root/ga-ssa/managers.txt。其中 gitops 必须以 Apply 操作出现。
  3. 在 /root/ga-ssa/hotfix.yaml 中写同一个 Deployment,但 spec.replicas 为 5,镜像为 nginx:1.27。用 --field-manager=hotfix 尝试服务端应用,把失败的输出连同标准错误一起保存到 /root/ga-ssa/conflict.txt。暂时不要使用 --force-conflicts。
  4. 用 --force-conflicts 再做一次同样的应用,把成功的输出保存到 /root/ga-ssa/force.txt。现在集群中的镜像是 nginx:1.27,该字段的所有者是 hotfix。
  5. 创建 /root/ga-ssa/hotfix-slim.yaml——与 hotfix.yaml 相同,但没有 spec.replicas 这一行。 用 --field-manager=hotfix 做服务端应用之后,把拥有 spec.replicas 的管理者列表保存到 /root/ga-ssa/release.txt(如果没有任何人,就是空文件)。确认集群中的 replicas 变成多少。
  6. 在 /root/ga-ssa/web-csa.yaml 中写下 Deployment web-csa(同一命名空间,标签和容器名称为 web-csa,镜像 nginx:1.25),不带 --server-side,用普通的 kubectl apply 部署。把两个 Deployment(web 和 web-csa)的 metadata.annotations 键列表保存到 /root/ga-ssa/csa.txt。
  7. 在 /root/ga-ssa/scale-deploy.yaml 中写下 Deployment web-scale(副本 2,标签和容器名称 web-scale,镜像 nginx:1.25),并用 --field-manager=gitops 做服务端应用。然后用 kubectl -n ga-ssa scale deploy web-scale --replicas=4 把副本数增加,再用同一个管理者名称重新应用同一个文件。把失败的输出保存到 /root/ga-ssa/scale-conflict.txt。
  8. 把集群中的 web 连同所有权记录一起以 YAML 保存到 /root/ga-ssa/live.yaml(-o yaml --show-managed-fields)。在 /root/ga-ssa/argocd-cm.yaml 中放置键 resource.customizations.ignoreDifferences.apps_Deployment,在 managedFieldsManagers 中写 hotfix,在 jsonPointers 中写 /spec/replicas。把 argocd admin settings resource-overrides ignore-differences /root/ga-ssa/live.yaml --argocd-cm-path /root/ga-ssa/argocd-cm.yaml 的输出保存到 /root/ga-ssa/ignore.txt。

参考

亮出名称并应用

在 kwok 集群中创建命名空间 ga-ssa,并在 /root/ga-ssa/deploy.yaml 中写下 Deployment web——spec.replicas 为 2,选择器和标签为 app: web,一个容器(web,镜像 nginx:1.25)。用 kubectl apply --server-side --field-manager=gitops 应用。

--field-manager 是告知 API 服务器“这次变更是谁做的”的名称。服务端应用会按这个名称记录它拥有哪些字段——这是客户端应用所没有的概念。

读取所有权记录

用 kubectl -n ga-ssa get deploy web -o json --show-managed-fields 读取所有权记录,每个管理者一行,以 <관리자> | <연산>(占位符依次为管理者、操作类型)的格式保存到 /root/ga-ssa/managers.txt。其中 gitops 必须以 Apply 操作出现。

如果去掉这个选项,.metadata.managedFields 会显示为 null——因为 kubectl 默认把它隐藏了。用 jq 遍历数组,取出 manager 和 operation。

第二个管理者试图写同一个字段时

在 /root/ga-ssa/hotfix.yaml 中写同一个 Deployment,但 spec.replicas 为 5,镜像为 nginx:1.27。用 --field-manager=hotfix 尝试服务端应用,把失败的输出连同标准错误一起保存到 /root/ga-ssa/conflict.txt。暂时不要使用 --force-conflicts。

服务器会以“这个字段被别人持有”为由拒绝,并以列表形式告知是哪些字段。如果没有这个拒绝,两个自动化工具就会悄悄覆盖对方的值。在参考答案脚本里要小心,别让它因失败而中断。

抢夺所有权

用 --force-conflicts 再做一次同样的应用,把成功的输出保存到 /root/ga-ssa/force.txt。现在集群中的镜像是 nginx:1.27,该字段的所有者是 hotfix。

强制并不是消除冲突,而是转移所有权。原来的所有者会从该字段中退出,所以下次原来的所有者再应用自己的值时,这次遇到冲突的就是它——如果两个自动化工具轮流使用强制,就会变成一场没有尽头的争斗。

释放字段后,值不会回到所有者手里

创建 /root/ga-ssa/hotfix-slim.yaml——与 hotfix.yaml 相同,但没有 spec.replicas 这一行。 用 --field-manager=hotfix 做服务端应用之后,把拥有 spec.replicas 的管理者列表保存到 /root/ga-ssa/release.txt(如果没有任何人,就是空文件)。确认集群中的 replicas 变成多少。

从清单中去掉字段,该管理者就会从所有权列表中放弃那个字段。但值不会回到原来所有者的值——没有人拥有的字段会变成 API 默认值。所有者列表,用 jq 选出 .fieldsV1."f:spec"."f:replicas" 不是 null 的条目即可。

客户端应用会留下什么

在 /root/ga-ssa/web-csa.yaml 中写下 Deployment web-csa(同一命名空间,标签和容器名称为 web-csa,镜像 nginx:1.25),不带 --server-side,用普通的 kubectl apply 部署。把两个 Deployment(web 和 web-csa)的 metadata.annotations 键列表保存到 /root/ga-ssa/csa.txt。

客户端应用会把“我上次发送的清单”整个放进一个注解里,并与它比较来决定要删除什么。所以没有所有者的概念,两个工具处理同一个对象时会互相删除对方的字段。以服务端方式部署的那一个没有这个注解。

kubectl scale 从另一扇门进来

在 /root/ga-ssa/scale-deploy.yaml 中写下 Deployment web-scale(副本 2,标签和容器名称 web-scale,镜像 nginx:1.25),并用 --field-manager=gitops 做服务端应用。然后用 kubectl -n ga-ssa scale deploy web-scale --replicas=4 把副本数增加,再用同一个管理者名称重新应用同一个文件。把失败的输出保存到 /root/ga-ssa/scale-conflict.txt。

kubectl scale 写入的不是整个对象,而是名为 scale 的子资源。所以所有权记录中会分别留下管理者 kubectl 和子资源 scale,冲突消息也会告知这一点。自动扩缩器造成的冲突,正是这种样子。

按所有者名称从比较中排除

把集群中的 web 连同所有权记录一起以 YAML 保存到 /root/ga-ssa/live.yaml(-o yaml --show-managed-fields)。在 /root/ga-ssa/argocd-cm.yaml 中放置键 resource.customizations.ignoreDifferences.apps_Deployment,在 managedFieldsManagers 中写 hotfix,在 jsonPointers 中写 /spec/replicas。把 argocd admin settings resource-overrides ignore-differences /root/ga-ssa/live.yaml --argocd-cm-path /root/ga-ssa/argocd-cm.yaml 的输出保存到 /root/ga-ssa/ignore.txt。

在 Argo CD 中表达“自动扩缩器写入的字段不参与比较”的方法就是这两种——按路径排除,或者按管理者名称排除。不过预览命令只渲染路径规则,所以管理者名称规则实际会过滤掉什么,必须由人去读所有权记录来判断。