bigywater.tech
Kubernetes · 17

kubectl apply:最后一次应用的注解与三方合并

为什么 kubectl apply 能删掉你从 YAML 里去掉的字段,却不动别人手工加的字段?靠的是 metadata.annotations 里的 last-applied-configuration。这篇把三方合并的每一个输入都打印出来,再看它的几个坑:没有注解时的行为、server-side 的区别,以及大对象 apply 会撞上的注解上限。

{a: 10, c: 3}
三方合并的结果:a 以这次为准、b 被删除、手工加的 c 保留
Warning: … missing the … annotation
kubectl create 出来的对象第一次 apply 时的提示,这次 apply 不会删字段
262144 字节
data 约 270000 字节的 ConfigMap 用 apply 时因注解过长被拒绝

三个输入

实测 · 本机执行输出
  第一次 apply {a: 1, b: 2}:configmap/cfg created
  last-applied 注解里的 data:{'a': '1', 'b': '2'};集群里:{'a': '1', 'b': '2'}
  有人手工 patch 了 {c: 3, a: hand-edited} 之后:集群里 {'a': 'hand-edited', 'b': '2', 'c': '3'};last-applied 仍是 {'a': '1', 'b': '2'}
  再 apply {a: 10}(去掉了 b):configmap/cfg configured
  结果:集群里 {'a': '10', 'c': '3'};last-applied 现在是 {'a': '10'}
    解读:a 被改成 10(新的赢);b 在上次 apply 里有、这次没有 → 被删除;c 不在任何一次 apply 里 → 保留

client-side apply 比较三样东西:上次 apply 的配置(存在 kubectl.kubernetes.io/last-applied-configuration 注解里)、这次的 manifest、集群里现在的对象。实验里第一次 apply {a: 1, b: 2},有人手工 patch 加了 c: 3 并改了 a,再 apply {a: 10}:a 以这次的为准变成 10;b 在上次 apply 里有、这次没有,被删除;c 从没出现在任何一次 apply 里,保留。

没有注解的对象

实测 · 本机执行输出
  kubectl create 出来的 made:last-applied=None
  去掉 k2 再 apply:退出码 0;configmap/made configured | Warning: resource configmaps/made is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be 
  结果:集群里 {'k2': '2', 'x': '1'}(k2 没有被删,因为没有“上次”可比);last-applied 现在是 {'x': '1'}

kubectl create 出来的对象没有这个注解(除非加 --save-config)。第一次 apply 时 kubectl 提示 missing the kubectl.kubernetes.io/last-applied-configuration annotation,因为没有“上次”可比,去掉的字段 k2 没有被删,但这次 apply 会把注解补上。所以先 create 再 apply,想删的字段要等第二次 apply 才删得掉。

server-side 不用注解

实测 · 本机执行输出
  server-side apply:configmap/ss serverside-applied;last-applied 注解:None

--server-side 创建的对象没有 last-applied 注解,字段归属改由 managedFields 记录(上一篇的内容),也不再有“上次配置”这个概念。

注解的大小上限

实测 · 本机执行输出
  kubectl create 一个 data 约 270000 字节的 ConfigMap:退出码 0;configmap/big created
  kubectl apply 同样大小:退出码 1;The ConfigMap "big2" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes
  kubectl apply --server-side 同样大小:退出码 0;configmap/big3 serverside-applied

last-applied 把整份配置又存了一遍到注解里,而一个对象的所有注解加起来不能超过 262144 字节。data 约 270000 字节的 ConfigMap 用 kubectl create 没问题,用 kubectl apply 就报 metadata.annotations: Too long: may not be more than 262144 bytes;--server-side 没有这个问题。大 ConfigMap、嵌入大段配置的 CRD 是最常撞上的地方。

分步演示

三方合并
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0);对象是 k11 命名空间里的 ConfigMap,注解内容用 kubectl get -o json 读出再解析;“有人手工修改”用 kubectl patch --type=merge 模拟;大 ConfigMap 的 data 是 270000 个字符的字符串;每种情形各一次
    • 没有验证:嵌套 map 与列表(带 merge key 与不带)在三方合并里的删除规则;kubectl apply --prune;kubectl.kubernetes.io/last-applied-configuration 与 server-side apply 的 managedFields 之间的迁移(csa-to-ssa 升级);--save-config 的行为;apply 对 CRD 的三方合并(CRD 没有 strategic patch,走 JSON merge patch)
    京ICP备2026067502号-1京公网安备11010502063487号