Kubernetes 的部分更新策略 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GopherDaily
V2EX    Kubernetes

Kubernetes 的部分更新策略

  •  
  •   GopherDaily 2023-05-29 15:48:10 +08:00 1585 次点击
    这是一个创建于 932 天前的主题,其中的信息可能已经有所发展或是发生改变。

    事情的起因是使用 kubectl apply 时遇到的一个偶发的错误提示.

    Warning: resource deployments/busybox-demo is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically. 

    为了快速规避可能的不良影响, 我转到了 kubectl patch. 在事后翻阅文档时, 可以明显的感受到 k8s 的 patch 基本考虑并处理了所有可能的场景, 极具借鉴意义.

    部分更新, partial modification, 是一个常见而复杂的问题, HTTP PATCH 就是典型的部分更新语义, 一些实现会将 HTTP PUT 也实现成部分更新. 虽然业界对部分更新有充分而详细的讨论, 但很多实现者依然会忽略这些现成的结论而自行设计, 导致重复的问题重复出现.

    部分更新的典型问题

    我们假设已经存在一个资源 busybox-demo, 来讨论部分更新的典型问题.

    apiVersion: apps/v1 kind: Deployment metadata: name: busybox-demo namespace: default spec: replicas: 1 selector: matchLabels: app: busybox template: metadata: labels: app: busybox spec: containers: - name: busybox image: busybox:1.28 args: - sleep - "1000000" 

    部分更新的语义下, 我们只需要提供资源的标识和想要更新的字段. 所以如果我们希望把 replicas 更新为 2, 只需要提供如下信息.

    apiVersion: apps/v1 kind: Deployment metadata: name: busybox-demo namespace: default spec: replicas: 2 

    而困难的点, 一方面在于如何把 replicas 更新为 0. 是仅需要把 replicas 设置为 0, 还是需要把 replicas 设置 null. server 会不会错误的把 key 不存在理解为更新为 0? 很多实现没有明确而清晰的考虑零值的问题, 导致实现使用中出现歧义.

    另一方面在于是否允许对数组进行部分更新? 怎么标识同一元素? 怎么标识某一元素的删除? 在 JSON Merge PATCH 中是无法对数组做部分更新, 这需要一些额外的工作.

    strategic

    kubectl patch 提供了三种部分更新的策略, strategic 是默认选项.

    你可以通过 patch 将 deploy 的 replicas 从 1 更新到 2.

     k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-92x58 1/1 Running 0 3m15s k patch -n default deploy busybox-demo -p '{"spec": {"replicas": 2}}' deployment.apps/busybox-demo patched k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-92x58 1/1 Running 0 3m25s busybox-demo-7b8b5c46db-j7grc 1/1 Running 0 4s 

    你可以通过指定 replicas 为 0, 将 pod 的数量减少到 0.

     k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-96q2d 1/1 Running 0 60s k patch -n default deploy busybox-demo -p '{"spec": {"replicas": 0}}' deployment.apps/busybox-demo patched k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-96q2d 1/1 Terminating 0 79s 

    当你将 replicas 指定为 null 时, 有趣的事情发生了, replicas 变为了 1, 这是 replicas 的默认值. 原因是 strategic 会将 null 视为要删除对应的 key, 进而导致其取默认值.

     k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-kfhbw 1/1 Running 0 4s busybox-demo-7b8b5c46db-r7znn 1/1 Running 0 4s k patch -n default deploy busybox-demo -p '{"spec": {"replicas": null}}' deployment.apps/busybox-demo patched k get -n default pods NAME READY STATUS RESTARTS AGE busybox-demo-7b8b5c46db-kfhbw 1/1 Running 0 21s busybox-demo-7b8b5c46db-r7znn 1/1 Terminating 0 21s k get -n default deploy busybox-demo -o json | jq '.spec.replicas' 1 

    k8s 提供 strategic 的主要目的是为了实现数组的部分更新. k8s 在文档中定义了两个注解: patchStrategy 和 patchMergeKey, 以 PodSpec.Containers 为例. patchStrategy 为 merge, 代表 k8s 会尝试将已有的数组和请求中的数组进行合并. 而 patchMergeKey 则指定了数组合并时用来判断数组元素是否相同的字段, 在此为 name.

    所以, 我们可以通过如下的方式去修改和新增容器.

     k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[]' { "args": [ "sleep", "1000000" ], "image": "busybox:1.28", "imagePullPolicy": "IfNotPresent", "name": "busybox", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File" } cat patch-containers.yaml spec: template: spec: containers: - name: busybox resources: requests: cpu: 500m - name: busybox-add image: busybox:1.28 args: - sleep - "3600" k patch -n default deploy busybox-demo --patch-file patch-containers.yaml deployment.apps/busybox-demo patched k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[]' { "args": [ "sleep", "1000000" ], "image": "busybox:1.28", "imagePullPolicy": "IfNotPresent", "name": "busybox", "resources": { "requests": { "cpu": "500m" } }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File" } { "args": [ "sleep", "3600" ], "image": "busybox:1.28", "imagePullPolicy": "IfNotPresent", "name": "busybox-add", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File" } 

    我们也可以通过 patch 删除某个容器, 虽然好像 k8s 的文档里没有说这种做法.

     k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[].name' "busybox" "busybox-add" k patch -n default deploy busybox-demo -p '{"spec": {"template": {"spec": {"containers": [{"name": "busybox-add", "$patch": "delete"}]}}}}' deployment.apps/busybox-demo patched k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[].name' "busybox" 

    JSON Merge Patch 和 JSON Patch

    k8s 也支持 JSON Merge PatchJSON Patch.

    相较于 strategic, JSON Merge Patch 的主要区别在于其并不支持数组层面的部分更新. JSON Merge Patch 会直接使用请求中的数组替换现有的数组.

     k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[]' { "args": [ "sleep", "1000000" ], "image": "busybox:1.28", "imagePullPolicy": "IfNotPresent", "name": "busybox", "resources": { "requests": { "cpu": "500m" } }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File" } cat patch-json.yaml spec: template: spec: containers: - name: busybox-add image: busybox:1.28 args: - sleep - "3600" k patch -n default deploy busybox-demo --patch-file patch-json.yaml --type merge deployment.apps/busybox-demo patched k get -n default deploy busybox-demo -o json | jq '.spec.template.spec.containers[]' { "args": [ "sleep", "3600" ], "image": "busybox:1.28", "imagePullPolicy": "IfNotPresent", "name": "busybox-add", "resources": {}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File" } 

    JSON Patch 则是完全的另外一个思路, 并不是完全的增量更新. 它允许对资源的任意字段单独进行复杂的操作, 更灵活强大的同时, 也更复杂.

    [ { "op": "test", "path": "/a/b/c", "value": "foo" }, { "op": "remove", "path": "/a/b/c" }, { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }, { "op": "replace", "path": "/a/b/c", "value": 42 }, { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }, { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" } ] 

    apply

    当使用 kubectl 去操作资源时, 我们更推荐使用 apply 而不是 patch. apply 做了一些额外工作, 极大的降低了使用成本.

    当你使用 apply 去创建或者修改资源时, k8s 会通过特定的注解来记录这次请求.

     k get -n default deploy busybox-demo -o json | jq '.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]' -r | jq { "apiVersion": "apps/v1", "kind": "Deploymen", "metadata": { "annotations": {}, "name": "busybox-demo", "namespace": "default" }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "busybox" } }, "template": { "metadata": { "labels": { "app": "busybox" } }, "spec": { "containers": [ { "args": [ "sleep", "1000000" ], "image": "busybox:1.28", "name": "busybox" } ] } } } } 

    当你随后再通过 apply 去更新资源时, kubectl 会根据上次的请求, 当前的资源现状和这次的请求计算出具体应该如何修改. 而体而言:

    • 如果某个字段在上次请求中存在, 但是在这次请求中不存在, 则被删除.
    • 如果某个字段在当前请求中存在, 但是在现状中不存在或者值不同, 则被添加或者修改.

    此时如果面临上文中需要删除某个容器的场景时, 直接在配置文件中删除对应的配置即可, 不再需要使用 $patch 这样的字段了. 需要注意, patch 等命令都不会更新这个注解, 所以如果最好不要把 apply 和其他命令混用在一个字段上.

    Reference

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5123 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 05:56 PVG 13:56 LAX 21:56 JFK 00:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86