应用的值老是被还原——找出字段的主人
目标
在 kwok 集群中用服务端应用创建两个管理者,并亲自读取 managedFields。亲手触发冲突与强制、释放字段、scale 子资源的冲突,最后再与 Argo CD 的忽略规则衔接起来。
为什么重要
即使启用了 GitOps,向集群对象写入的主体也不止一个。Argo CD 会写,自动扩缩器会写 replicas,Webhook 会插入容器,人在紧急时会用 kubectl 写。服务端应用是由 API 服务器用记录来保存“谁是这个字段的所有者”的装置。 只有会读这份记录,才能回答三个问题——为什么我的应用被拒绝,为什么强行推入之后下一次被拒绝的是对方,为什么释放了值却没有回到旧值。而且这份记录还会衔接到 Argo CD 这一侧。因为忽略规则中的 managedFieldsManagers 不是按字段路径,而是按这份所有权记录来选择比较对象。
步骤
- 在 kwok 集群中创建命名空间
ga-ssa,并在/root/ga-ssa/deploy.yaml中写下 Deploymentweb——spec.replicas为 2,选择器和标签为app: web,一个容器(web,镜像nginx:1.25)。用kubectl apply --server-side --field-manager=gitops应用。 - 用
kubectl -n ga-ssa get deploy web -o json --show-managed-fields读取所有权记录,每个管理者一行,以<관리자> | <연산>(占位符依次为管理者、操作类型)的格式保存到/root/ga-ssa/managers.txt。其中gitops必须以Apply操作出现。 - 在
/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 变成多少。 - 在
/root/ga-ssa/web-csa.yaml中写下 Deploymentweb-csa(同一命名空间,标签和容器名称为web-csa,镜像nginx:1.25),不带--server-side,用普通的kubectl apply部署。把两个 Deployment(web和web-csa)的metadata.annotations键列表保存到/root/ga-ssa/csa.txt。 - 在
/root/ga-ssa/scale-deploy.yaml中写下 Deploymentweb-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。 - 把集群中的
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。
参考
kubectl get ... -o json会隐藏 managedFields——请加上--show-managed-fields。- 用
--field-manager指定管理者名称。默认值因命令而异。 - 冲突消息会告知哪些字段属于谁。请先读那份列表。
- 常见错误:习惯性地加上
--force-conflicts——所有权会转移,下次被拦住的就是对方。 - 常见错误:以为从清单中去掉字段就会回到旧值。实际上会变成 API 默认值。
- 参考:https://kubernetes.io/docs/reference/using-api/server-side-apply/
亮出名称并应用
在 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 中表达“自动扩缩器写入的字段不参与比较”的方法就是这两种——按路径排除,或者按管理者名称排除。不过预览命令只渲染路径规则,所以管理者名称规则实际会过滤掉什么,必须由人去读所有权记录来判断。