k8s之client-go的update和patch

1、对于update 请求,需要将整个修改后的对象提交给apiserver,并且apiserver会校验用户提交的resourceVersion是否和当前k8s中这个对象的resourceVersion一致,一致才能接受本次update,否则发生版本冲突。


2、对于patch请求,只需要将对象中某些字段的修改提交给apiserver,并且apiserver 不会考虑版本问题,而是直接将patch打到对象上,然后再更新版本号
(1)json patch: 需要指定操作类型,且修改列表时要通过元素序号来指定元素;
(2)merge patch:无法单独更新一个列表中的某个元素,会整个覆盖列表;
(3)strategic merge patch: 更新列表时,不需要指定序号,而是以name 作为 key 来计算 merge,同时strategic只适用于原生 K8s 资源以及Aggregated API方式的自定义资源,对于 CRD 定义的资源对象是无法使用的;

(4)apply patch:包括client-side apply和server-side apply,kubectl apply时默认是client-side apply patch,同时kubectl edit也是patch;

  • kubectl apply 即client-side apply patch

- 首先解析用户提交的数据(YAML/JSON)为一个对象 A;然后调用 Get 接口从 K8s 中查询这个资源对象:

- 如果查询结果不存在,kubectl 将本次用户提交的数据记录到对象 A 的 annotation 中(key 为 kubectl.kubernetes.io/last-applied-configuration),最后将对象 A提交给 K8s 创建;

- 如果查询到 K8s 中已有这个资源,假设为对象 B,kubectl 尝试从对象 B 的 annotation 中取出 kubectl.kubernetes.io/last-applied-configuration 的值(对应了上一次 apply 提交的内容);

- kubectl 根据前一次 apply 的内容和本次 apply 的内容计算出 diff 得到patch报文(默认为 strategic merge patch 格式,如果CR则采用 merge patch),并将 diff 中添加本次的 kubectl.kubernetes.io/last-applied-configuration annotation,最后用 patch 请求提交给 K8s 做更新。

  • kubectl edit 即client-side apply patch

与kubectl apply 相比,kubectl edit 逻辑上更简单一些。在用户执行kubectl edit 命令之后,kubectl 从 K8s 中查到当前的资源对象,并打开一个命令行编辑器(默认用 vi)为用户提供编辑界面。当用户修改完成、保存退出时,kubectl 并非直接把修改后的对象提交 update(避免 Conflict,如果用户修改的过程中资源对象又被更新),而是会把修改后的对象和初始拿到的对象计算 diff,最后将 diff 内容用 patch 请求提交给 K8s。

  • server-side apply patch

server-side apply是k8s v1.18时基于managedFields的新特性。把计算diff得到patch报文的逻辑放到了服务端即kube-apiserver,客户端只需要提交完整的managedFields即可默认为 strategic merge patch 格式,如果CR则采用 merge patch)当多个client控制一个资源时, 相比于client-side apply 用 last-applied annotations的方式,server-side apply 通过一种声明式 API即managedFields来明确指定哪个Field被哪个client管理和控制。例如 WorkloadController只能修改image相关的操作,而ScaleController 只能修改副本数。

当使用 server-side apply patch 尝试着去改变一个被其他人管理的字段, 会导致请求被拒绝,当然也可以设置强制执行,这里有三种策略:覆盖前值,成为唯一的管理器;不覆盖前值,放弃管理权;不覆盖前值,成为共享的管理器;

(5)常用的three-way-merge三路合并算法的好处是程序能理解你到底做了哪些改动,如果这些改动没有冲突,则它会自动帮你合并。

(6)另外,kube-apply才会记录在last-applied-anno上,kube-edit不会,kube-apply是通过当前资源的last-applied-anno和要apply的资源计算patch

3、因此,每次对象被修改,不管是 update 还是 patch 修改,版本号metadata.resourceVersion都会发生变化,但是也要注意因为patch时apiserver不会考虑版本问题,所以如果我们 patch 之前这个对象已经被其他人修改了,那么我们的patch有可能产生非预期的后果。(metadata.generation是spec变化才会变化)

4、我们在不同的场景下怎么选择 update 或 patch 来使用呢?这里的建议是:
如果要更新的字段只有我们自己会修改(比如我们有一些自定义标签,并写了 operator 来管理),则使用 patch 是最简单的方式;
如果要更新的字段可能会被其他方修改(比如我们修改的 replicas 字段,可能有一些其他组件比如 HPA 也会做修改),则建议使用 update 来更新,避免出现互相覆盖;

5、对于ctrl中的几个patch封装,源码目录为"sigs.k8s.io/controller-runtime/pkg/client"

- client.Merge:会把数据全量发送到apiserver,执行MergePatch;
- client.Apply:会把数据全量发送到apiserver,执行server-side apply patch;
- client.MergeFrom():只会发变化的部分到apiserver,要用deepcopy,执行MergePatch;

6、spec和status

create只能创建metadata和spec,不能创建status
update和patch只能更新metadata和spec,不能更新status
status.update和status.patch只能更新status

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用client-go库中的`Patch`方法来部分更新Kubernetes资源的`spec`字段。具体操作步骤如下: 1. 导入必要的包 ```go import ( "encoding/json" "fmt" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/util/retry" ) ``` 2. 创建Kubernetes客户端 ```go config, err := rest.InClusterConfig() if err != nil { panic(err.Error()) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } ``` 3. 构造需要更新的`Patch`对象 ```go type patch struct { Spec struct { Replicas int32 `json:"replicas"` } `json:"spec"` } // 将需要更新的字段构造成Patch对象 updatePatch := patch{} updatePatch.Spec.Replicas = 3 patchBytes, err := json.Marshal(updatePatch) if err != nil { panic(err.Error()) } ``` 4. 执行部分更新操作 ```go // 定义资源名称和名称空间 name := "deployment-name" namespace := "default" // 创建资源标识 patchMeta := metav1.ObjectMeta{ Name: name, Namespace: namespace, } // 使用client-go的Patch方法进行部分更新 _, err = clientset.AppsV1().Deployments(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) if err != nil { if errors.IsNotFound(err) { fmt.Printf("Deployment %s in namespace %s not found\n", name, namespace) } else { panic(err.Error()) } } ``` 这样就可以使用client-go库来部分更新Kubernetes资源的`spec`字段了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值