kubectl patch --type 的三个取值看似差不多,改容器列表时结果却天差地别。这篇用同一个目标跑三种 patch,看列表是被合并还是被整个替换,再看删除怎么写、哪些字段会按 key 合并、自定义资源能用哪几种。
初始:containers={"a": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": ["X"]}, "b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2']
--type=merge(JSON merge patch,RFC 7386):退出码 0;deployment.apps/web patched
containers={"a": {"image": "busybox:1.35", "command": null, "env": []}} tolerations=['t1', 't2']
--type=strategic(默认,按 name 合并列表):退出码 0;deployment.apps/web patched
containers={"a": {"image": "busybox:1.35", "command": ["sleep", "3600"], "env": ["X"]}, "b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2']
--type=json(JSON patch,RFC 6902,按下标):退出码 0;deployment.apps/web patched
containers={"a": {"image": "busybox:1.35", "command": ["sleep", "3600"], "env": ["X"]}, "b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2']
merge patch 把 a 的 image 和 command 写全再试:退出码 0;deployment.apps/web patched
containers={"a": {"image": "busybox:1.35", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2'](容器 b 仍然被整个列表替换掉了)目标都是“把容器 a 的镜像改成 busybox:1.35”。merge patch(RFC 7386)把整个 containers 列表当成一个值替换:结果只剩 a,a 的 command 丢了(null),容器 b 不见了;就算把 a 的 image 和 command 写全,b 也回不来。strategic merge patch(kubectl 对内置资源的默认类型)按 name 对齐列表元素,只改 a 的 image,其余都保留。JSON patch(RFC 6902)按下标 containers/0/image 精确修改,同样只动 image。
merge patch 把 annotation drop 设为 null:deployment.apps/web patched;annotations={"deployment.kubernetes.io/revision":"1","keep":"yes"}
strategic 用 $patch: delete 删掉容器 b:deployment.apps/web patched;containers={"a": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": ["X"]}} tolerations=['t1', 't2']
strategic 删掉容器 a 的 env X:deployment.apps/web patched;containers={"a": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}, "b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2']
JSON patch remove containers/0(按下标):deployment.apps/web patched;containers={"b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t1', 't2']
JSON patch 先 test containers/0/name==b 再 remove(下标和预期不符):退出码 1;The request is invalid: the server rejected our request due to an error in our requestmerge patch 里把某个 key 设为 null 就是删除(这里删掉了注解 drop);strategic 里用 $patch: delete 按 key 删列表元素(删掉容器 b,或容器 a 里的 env X);JSON patch 用 remove + 下标,下标一错删掉的就是别的元素(containers/0 删掉的是 a)。JSON patch 可以先加一个 test 操作做前置条件,条件不符整个 patch 被拒绝(这次退出码 1)。
PodSpec.containers:{'x-kubernetes-list-type': 'map', 'x-kubernetes-patch-merge-key': 'name', 'x-kubernetes-patch-strategy': 'merge'}
PodSpec.volumes:{'x-kubernetes-list-type': 'map', 'x-kubernetes-patch-merge-key': 'name', 'x-kubernetes-patch-strategy': 'merge,retainKeys'}
PodSpec.tolerations:{'x-kubernetes-list-type': 'atomic'}
Container.env:{'x-kubernetes-list-type': 'map', 'x-kubernetes-patch-merge-key': 'name', 'x-kubernetes-patch-strategy': 'merge'}
Container.ports:{'x-kubernetes-list-type': 'map', 'x-kubernetes-patch-merge-key': 'containerPort', 'x-kubernetes-patch-strategy': 'merge'}
Container.command:{'x-kubernetes-list-type': 'atomic'}
Container.args:{'x-kubernetes-list-type': 'atomic'}
strategic 改 tolerations(没有 merge key)写一条 t3:deployment.apps/web patched;containers={"a": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": ["X"]}, "b": {"image": "busybox:1.36", "command": ["sleep", "3600"], "env": []}} tolerations=['t3']策略取决于 schema 里的扩展:containers 和 env 的 x-kubernetes-patch-merge-key 是 name,ports 是 containerPort,volumes 的策略是 merge,retainKeys;而 tolerations、command、args 只有 x-kubernetes-list-type: atomic,strategic patch 写一条 t3,tolerations 就从 [t1, t2] 变成只有 [t3]。
对自定义资源 --type=strategic:退出码 1;error: application/strategic-merge-patch+json is not supported by k10.example.com/v1, Kind=Box: the body of the request was in an unknown format - accepted media types include: application/json-patch+json, application/merge-patch+;spec.items=[{"name":"first","v":1},{"name":"second","v":2}]
对自定义资源 --type=merge:退出码 0;box.k10.example.com/b1 patched;spec.items=[{"name":"first","v":9}]
对自定义资源 --type=json:退出码 0;box.k10.example.com/b1 patched;spec.items=[{"name":"first","v":9},{"name":"second","v":2}]CRD 没有 Go 结构体上的 patchStrategy 标记,所以不支持 strategic merge patch:--type=strategic 得到 application/strategic-merge-patch+json is not supported by k10.example.com/v1, Kind=Box,并列出可接受的媒体类型(其中有 application/json-patch+json、application/merge-patch+json)。对自定义资源,merge patch 同样整个替换列表;要精确改列表元素,用 JSON patch 或 server-side apply(前提是 CRD 里声明了 x-kubernetes-list-type: map)。