k8s 中的 Client-Side Apply 和 Server-Side Apply
背景
如果你经常与 kubectl 打交道,那相信你一定见过 kubectl.kubernetes.io/last-applied-configuration annotation,以及那神烦的 managedFields,像这样:
kubectl get pods hello -oyaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"run":"hello"},"name":"hello","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"hello","resources":{}}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always"},"status":{}}
creationTimestamp: "2022-05-28T07:28:51Z"
labels:
run: hello
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:labels:
.: {}
f:run: {}
....
manager: kubectl
operation: Update
time: "2022-05-28T07:28:51Z"
....
由这两个字段,引出本文的两位主角,Client-Side Apply(以下简称CSA)和 Server-Side Apply(以下简称SSA)
kubectl.kubernetes.io/last-applied-configuration: 是使用 kubectl apply 进行 Client-Side Apply 时,由 kubectl 自行填充的。
managedFields: 则是由 kubectl apply 的增强功能—— Server-Side Apply 的引入而添加。
1、kubectl apply 最初始的样子——Client-Side Apply
由此可见,last-applied-configuration 体现的是一种 ownership 的关系,表示哪些字段是由 kubectl 管理,它是 kubectl apply 时,计算 patch 报文的依据。
其实这种合并方式也称为:three way merge
2、kubectl apply 升级版——Server-Side Apply
kubectl apply --server-side=true -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: test-server-side-apply
data:
a: "a"
b: "b"
EOF
部署成功后,查看对象会发现该对象中不再存在 last-applied-configuration:
configmap/test-server-side-apply serverside-applied
kubectl get cm test-server-side-apply -oyaml
apiVersion: v1
data:
a: a
b: b
kind: ConfigMap
metadata:
creationTimestamp: "2022-12-11T01:49:55Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
f:a: {}
f:b: {}
manager: kubectl
operation: Apply
time: "2022-12-11T01:49:55Z"
name: test-server-side-apply
namespace: default
如果你没能看到 managedFields 字段,可以加上 --show-managed-fields 参数:kubectl get cm test-server-side-apply -oyaml --show-managed-fields
失去 last-applied-configuration 后,表达 ownership 的任务就落入了新引入的字段管理机制(field management)手中。根据以上输出的 yaml 的 metadata.managedFields 字段,我们不难得出它想表达的含义:该 configmap 中 data.a 和 data.b 字段都是由 kubectl 来管理的。
“ 字段管理(field management) ”机制追踪对象字段的变化。当一个字段值改变时,其所有权从当前管理器(manager)转移到施加变更的管理器。当尝试将新配置应用到一个对象时,如果字段有不同的值,且由其他管理器管理, 将会引发 冲突 。冲突引发警告信号:此操作可能抹掉其他协作者的修改。冲突可以被刻意忽略,这种情况下,值将会被改写,所有权也会发生转移
SSA 中使用了字段管理机制来追踪对象的变化,当 apply 改变一个字段时,而恰巧该字段被其他用户声明了 ownership,此时会发生冲突。这可以防止一个管理者不小心覆盖掉其他用户设置的值。举个例子:如果修改我们刚刚通过SSA 创建的 test-server-side-applyconfigmap,并且手动设置管理者为 test(通过–field-manager 字段),此时 kubectl 会拒绝我们的提交,提示冲突:
kubectl apply --server-side=true --field-manager="test" -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: test-server-side-apply
data:
a: "a"
b: "c"
EOF
error: Apply failed with 1 conflict: conflict with "kubectl": .data.b
Please review the fields above--they currently have other managers. Here
are the ways you can resolve this warning:
* If you intend to manage all of these fields, please re-run the apply
command with the `--force-conflicts` flag.
* If you do not intend to manage all of the fields, please edit your
manifest to remove references to the fields that should keep their
current managers.
* You may co-own fields by updating your manifest to match the existing
value; in this case, you'll become the manager if the other manager(s)
stop managing the field (remove it from their configuration).
See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts
从 kubectl 返回的提示,我们可以得知当冲突发生的时我们有三种选择:
- 覆盖前值,成为当前字段的唯一管理者——通过增加 --force-conflicts flag
- 不覆盖前值,放弃管理权——在本次配置中,把修改的字段删掉(本例中是 data.b)
- 不覆盖前值,成为共享管理者——把冲突值改成和服务器对象一致
此时如果我们手动设置管理者为 kubectl则可以成功:
kubectl apply --server-side=true --field-manager="kubectl" -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: test-server-side-apply
data:
a: "a"
b: "c"
EOF
configmap/test-server-side-apply serverside-applied