一:概述:
根据(一)的kubectl命令可以发现,资源的更新可以是命令式的也可以是声明式的,
所谓命令式的,顾名思义就是我直接设置某个字段是什么样子的
所谓声明式的,顾名思义就是我想让某个字段是什么样子的
二者的执行对应到真正调用后端的api时是有差别的,前者是直接replace,后者则是商量着来,有可能直接replace也有可能merge(见。。。。)
所以,接下来我们一起从API的角度来探讨下replace和patch的区别
二:资源更新(update)的两种方式
参考官网;https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#podspec-v1-core
想对指定资源进行更新操作,API server给我们提供了两种形式的api,分别如下
1. Replace
这种方式是将当前资源的spec替换为指定的。这是一个read-then-write的操作,而且因为如果在read 和 write之间资源有发生变化,那么就会发生乐观锁的失败,所以这个操作是安全的。
注;replace操作中ResourceStatus 会被系统忽略,并不会被更新,若想更新这个字段,则必须触发那些会引起该状态变化的行为才行。也就是说有,甭想通过replace命令直接更新状态,只有通过让相应的事件发生,才能间接的刷新对应的状态。
常用的replace API有:
2.Patch
这种方式是对指定的spec字段应用一个"变更"操作,所谓的应用是指将"变更"合并到每一个filed上,合并的过程中会根据filed类型的不同或是替换(replace),或是合并(merge).
patch操作不会引发乐观锁失败,只有最后一次写生效。如果你无法读取object的完整状态或者不希望乐观所失败,那么就推荐你使用Patch操作。
当你Patching的是复杂的类型如arrays, map,那么具体的patch是如何被应用到每个filed上的,则取决于每个字段的
三:详解Patch操作
这个策略被定义在 Kubernetes的源码中,以filed的tag的形式存在,具体为key等于patchStrategy那个tag
type PodSpec struct {
...
Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
}
解析:
1.patchStrategy的vlaue可以取值merge或replace,缺省为replace,即如果源码中没有这个类型的tag,则表示这个filed在patch的时候是替换操作
获取这个field的策略,则通过api(???什么api)来得知
二:除了缺省的"strategic merge patch",patch API还支持在调用API的时候指定type参数来决定怎么patch,具体如下
1.JSON merge patch
用法(k8s):
命令行: --type merge
API: "Content-Type:merge-patch+json"
特点:如果想要更新一个list,你的patch request中必须囊括了整个list,因为他会将行list完全替换掉旧的list
原理(RFC 7386):
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"
1.JSON Patch
这种类型的patch操作要求Patch请求以["op","path","value"]三元组的形式来定义
"op": operation,代表要执行怎样的操作,可以是"add", "remove", "replace", "move" 和 "copy"
"path":被操作对象的位置, 表示处于json文件的哪个位置的fragment,通俗的说就是json格式下的逻辑位置
"value": 用于操作的值
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" } } ]
{ "users" : [ { "name" : "Alice" , "email" : "alice@wonderland.org" }, --被替换 { "name" : "Bob" , "email" : "bob@example.org" }, --没变 { "name" : "Christine" , "email" : "christine@example.org" } --新增的 ] }
具体工作原理用如下例子阐述:
1)一个object的json形式描述文件如下
{ "a": "b", "c": { "d": "e", "f": "g" } }
{ "a":"z", ---我想让a变成z "c": { ---我想让c的f变成null(代表删除) "f": null } }
{ "a": "z", "c": { "d": "e", } }
1.无法将值设置成null,因为在这里设置成null意味着删除
2.操作array比较麻烦,因为你必须全部写下,否则就会将旧array完全替换成新的,并不会发生"合并"
3.执行patch操作后,永远不会报错
另外,kubectl patch命令是一个直接的调用patch api的操作,即直接将patch的内容作为patch request调用api
而kubectl apply则隐含了先计算patch request的内容,然后再调用patch api