k8s的对象管理二(详解path与RFC7386)

概述:

根据(一)的kubectl命令可以发现,资源的更新可以是命令式的也可以是声明式的,

所谓命令式的,顾名思义就是我直接设置某个字段是什么样子的

所谓声明式的,顾名思义就是我想让某个字段是什么样子的

二者的执行对应到真正调用后端的api时是有差别的,前者是直接replace,后者则是商量着来,有可能直接replace也有可能merge(见。。。。)

所以,接下来我们一起从API的角度来探讨下replace和patch的区别

 

零:k8s资源更新(update)的两种方式

参考官网: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/

想对指定资源进行更新操作,API server给我们提供了两种形式的api,分别如下

1. Replace

   这种方式update的目标是整个object,为整体替换。这是一个read-then-write的操作,而且因为如果在read 和 write之间资源有发生变化,那么就会发生乐观锁的失败,所以这个操作是安全的。

   注: replace操作中ResourceStatus 会被系统忽略,并不会被更新,若想更新这个字段,则必须触发那些会引起该状态变化的行为才行。也就是说有,甭想通过replace命令直接更新状态,只有通过让相应的事件发生,才能间接的刷新对应的状态。

replace方式的api为:PUT /...

 

2. Patch

官网说:

Caution: A patch is an update operation that is scoped to specific fields of an object instead of the entire object. This enables updating only a specific set of fields on an object without reading the object first.

这种方式是对部分字段应用的一个"变更"操作,所谓的应用是指将"变更"合并到指定filed(包括primitive和complex )上,合并的过程中会根据filed类型的不同或是替换(replace),或是合并(merge).

patch操作不会引发乐观锁失败,只有最后一次写生效。如果你无法读取object的完整状态或者不希望乐观所失败,那么就推荐你使用Patch操作。

当你Patching的是复杂的类型如arrays, map,那么具体的patch是如何被应用到每个filed上的,则取决于每个字段的策略属性。

 

wxy:

所谓"replace"和"patch"这两种形式,首先操作的目标就是不同的的,然后他们代表的是一种结果,即当然大目标还是我们的object,细分一下也可以认为是以何种操作方式更新资源的field。最后,至于使用怎样的命令或者怎样的api参数实现这样的操作,则是另一回事。

replace:表示的是将目标当做一个整体,然后整体"替换",所以apiserver提供的API,其http method都是put;对应到我们的client-go帮我们实现的一些方法,就是“Update”接口; 

patch:则是对目标的指定fields进行操作,同时指定部分还可以更加细分的进行指定的操作。至于如何进行“特定的操作,则是根据固定的策略。我们接下里就详细针对Patch操作做一个分析。其http method都是patch。

 

一:详解k8s的Patch操作

这个操作的结果可能是替换当前值,也可能是与当前值进行合并,具体要根据对应的策略(称为patch strategy)而定, 包括被操作字段的配置以及patch操作的配置. 
 
0. 怎样才算是做一次patch操作
    客户端,使用的http method为 PATCH, 同时可以在请求header中通过参数???????
    服务端,根据客户端的请求参数,以及自己维护的filed的“属性”,然后按照rfc7386的规范做操作
   
    在k8s的服务端,即apiServer,他通过读取源码定义时为filed添加的tag来知晓field的“属性”,具体示例如下:   
type Container struct {
  ...
  Ports []ContainerPort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"containerPort" ...`
}
   
tag解析:  
   patchStrategy: 其vlaue可以取值merge或replace,缺省为replace,即如果源码中没有这个类型的tag,则表示这个filed在patch的时候是替换操作
patchMergeKey: 用于复合结构体,其vlaue可以取值
 

kubectl apply如何实现patch calculation:

官方:https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#merge-patch-calculation

原理:
首先,kubectl命令会使用本地apply的配置文件,和当前live object configuration中的last-applied-configuration这个annotation中的内容做一个计算.
然后,将计算结果通过向API Server发送patch 请求, The patch defines updates scoped to specific fields of the live object configuration
最后,更新掉object的live configuration。
 
wxy: apiserver收到这个请求后,还需要做什么么?还是说就直接替换?
 
具体的步骤:
一 :merge calculations:
  • Calculate the fields to delete

          对于本地配置文件中explicitly set to null的那些fields, 无论last-applied-configuration中是否存在, 那么都要Clear from the live configuration.

  • Calculate the fields to set

         对于本地配置文件和live configuration中都存在的字段,且值还不相同的,则以本地为准, 即Set the value  in the live configuration.

 
二: Set the last-applied-configuration
         Set the last-applied-configuration annotation to match the value of the configuration file.
          wxy: 什么意思,具体时如何设置,设置的是raw local configuration还是计算之后的???????
 
三: patch request to the API server.
  Merge the results from 1, 2, 3 into a single patch request to the API server.
 
 
 
 
1. patch API缺省情况下执行的是是"strategic merge patch",即是一种按照field在源码中定义的策略属性进行的patch操作,具体是什么策略呢?

1.p
获取这个field的策略,则通过api(???什么api)来得知

二:除了缺省的"strategic merge patch",patch API还支持在调用API的时候指定type参数来决定怎么patch,具体如下
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#patch-operations

1.JSON merge patch
用法(k8s):
  命令行:  --type merge
  API: "Content-Type:merge-patch+json"
  特点:如果想要更新一个list,你的patch request中必须囊括了整个list,因为他会将行list完全替换掉旧的list
原理(RFC 7386):rfc7386 (nju.edu.cn)

2.JSON patch
用法(k8s):
  命令行:  --type json
  API: "Content-Type:json-patch+json"
  特点:如果想要更新一个list,你的patch request中必须囊括了整个list,因为他会将行list完全替换掉旧的list
原理(RFC 7386):

另外,对于缺省"strategic merge patch"方式,相当于
命令行:  --type strategic
API: "Content-Type:strategic-merge-patch+json"
 
三:JSON merge patch和JSON patch的用法
1.JSON Patch
这种类型的patch操作要求Patch请求以["op","path","value"]三元组的形式来定义
"op": operation,代表要执行怎样的操作,可以是"add", "remove", "replace", "move" 和 "copy"
"path":被操作对象的位置, 表示处于json文件的哪个位置的fragment,通俗的说就是json格式下的逻辑位置
"value": 用于操作的值
具体工作原理用如下例子阐述:
例1:
1)一个object的json形式描述文件如下
{
  "users" : [
    { "name" : "Alice" , "email" : "alice@example.org" },
    { "name" : "Bob" , "email" : "bob@example.org" }
  ]
}

2)patch请求的json形式描述如下:

[
    {
        "op" : "replace" ,                 ---我要做"替换"操作  
        "path" : "/users/0/email" ,        ---替换的对象是users数组的第1个元素的email字段
        "value" : "alice@wonderland.org"   ---要替换成的值是"alic...."
    },
    {
        "op" : "add" ,                     ---我要做"添加"操作 
        "path" : "/users/-" ,              ---增加的对象是users数组的下一级,即称为user的一个元素
        "value" : {                        ---增加的值是一个{...}
            "name" : "Christine", 
            "email" : "christine@example.org"
        }
    }
]

 

3)patch后得到的结果如下:
{
    "users" : [
        { "name" : "Alice" , "email" : "alice@wonderland.org" },      --被替换
        { "name" : "Bob" , "email" : "bob@example.org" },            --没变  
        { "name" : "Christine" , "email" : "christine@example.org" }  --新增的
    ]
}

 

1.JSON Merge Patch
这种类型的patch操作的Patch请求描述的是"变化信息"即"changed version of a JSON document"
具体工作原理用如下例子阐述:
例2:
1)一个object的json形式描述文件如下
{
    "a": "b",
    "c": {
        "d": "e",
        "f": "g"
    }
}

 

2)patch请求的json形式描述如下:
{
    "a":"z",   ---我想让a变成z
    "c": {     ---我想让c的f变成null(代表删除)
        "f": null
    }
}

 

3)patch后得到的结果如下:
{
    "a": "z",
    "c": {
        "d": "e",
    }
}

 

特点:
1.无法将值设置成null,因为在这里设置成null意味着删除
2.操作array比较麻烦,因为你必须全部写下,否则就会将旧array完全替换成新的,并不会发生"合并"
3.执行patch操作后,永远不会报错
wxy:
另外,kubectl patch命令是一个直接的调用patch api的操作,即直接将patch的内容作为patch request调用api
     而kubectl apply则隐含了先计算patch request的内容,然后再调用patch api

 

posted @ 2021-05-17 09:54  水鬼子  阅读(482)  评论(0编辑  收藏  举报