diff --git a/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.6-li-jie-k8s-zhong-de-clientside-apply-he-serverside-apply.md b/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.6-li-jie-k8s-zhong-de-clientside-apply-he-serverside-apply.md index 4a0dd7f..a84b27d 100644 --- a/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.6-li-jie-k8s-zhong-de-clientside-apply-he-serverside-apply.md +++ b/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.6-li-jie-k8s-zhong-de-clientside-apply-he-serverside-apply.md @@ -4,8 +4,33 @@ 如果你经常与`kubectl`打交道,那相信你一定见过 `kubectl.kubernetes.io/last-applied-configuration` annotation,以及那神烦的`managedFields`,像这样: -```bash -$ 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" .... +```yaml +$ 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**) @@ -19,6 +44,8 @@ $ kubectl get pods hello -oyaml apiVersion: v1 kind: Pod metadata: annotations: * Client-Side Apply 和 Server-Side Apply的基本工作方式。 * Server-Side Apply的优点。 +## `kubectl apply` 最初始的样子——Client-Side Apply + 在开始之前,有必要澄清一下`kubectl apply`的预期工作方式。`kubectl apply`是一种声明示的K8S对象管理方式,是我们最常用的应用部署,升级方式之一。 需要特别指出的是,`kubectl apply`声明的仅仅是它关心的字段的状态,而不是整个对象的真实状态。apply表达的意思是:“我”管理的字段应该和我apply的配置文件一致(但我不关心其他字段)。 @@ -26,7 +53,30 @@ $ kubectl get pods hello -oyaml apiVersion: v1 kind: Pod metadata: annotations: 什么是“我”管理的字段,什么又是其他的字段呢?举个例子,当我们希望使用HPA管理应用副本数时,[Kubernetes推荐的做法](https://link.juejin.cn/?target=https%3A%2F%2Fkubernetes.io%2Fdocs%2Ftasks%2Frun-application%2Fhorizontal-pod-autoscale%2F%23migrating-deployments-and-statefulsets-to-horizontal-autoscaling)是在apply的配置文中不指定具体`replicas`副本数。首次部署时,K8S会将`replicas`值设置为默认1,随后由HPA控制器扩容到合适的副本数。 ```yaml -apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: nginx name: nginx spec: # replicas: 1 不要设置replicas selector: matchLabels: app: nginx strategy: {} template: metadata: creationTimestamp: null labels: app: nginx spec: containers: - image: nginx:latest name: nginx resources: {} +apiVersion: apps/v1 +kind: Deployment +metadata: +  creationTimestamp: null +  labels: +    app: nginx +  name: nginx +spec: +  # replicas: 1 不要设置replicas +  selector: +    matchLabels: +      app: nginx +  strategy: {} +  template: +    metadata: +      creationTimestamp: null +      labels: +        app: nginx +    spec: +      containers: +      - image: nginx:latest +        name: nginx +        resources: {} + ``` 当升级应用时(修改镜像版本),修改配置文件中的`image`字段,再次执行`kubectl apply`。此时`kubectl apply`只会影响镜像版本(因为他是“我”管理的字段),而不会影响HPA控制器设置的副本数。在这个例子中,`replicas`字段不是`kubectl apply`管理的字段,因此更新镜像时不会被删除,避免了每次应用升级时,副本数都会被重置。 @@ -49,7 +99,7 @@ apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: a 由此可见,`last-applied-configuration`体现的是一种ownership的关系,表示哪些字段是由`kubectl`管理,它是`kubectl apply`时,计算patch报文的依据。 -### `kubectl apply`升级版——Server-Side Apply +## `kubectl apply`升级版——Server-Side Apply **SSA**是另一种声明式的对象管理方式,和**CSA**的作用是基本一致的。**SSA**始于从1.14开始发布alpha版本,到1.16beta,到1.18beta2,终于在1.22升级为GA。 @@ -60,13 +110,44 @@ apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: a 顾名思义,**SSA**将对象合并的逻辑转移到了服务端(APIServer),客户端只需提交完整的配置文件,剩下的工作交给服务端处理。 在`kubectl`中使用**SSA**,只需在`kubectl apply`时加上`--server-side`参数即可,例如这样: ```bash -$ kubectl apply --server-side=true -f - < TIPS: 如果你没能看到`managedFields`字段,可以加上 --show-managed-fields 参数: kubectl get cm test-server-side-apply -oyaml --show-managed-fields @@ -82,7 +163,28 @@ $ kubectl get cm test-server-side-apply -oyaml apiVersion: v1 data: a: a b: b ki **SSA**中使用了字段管理机制来追踪对象的变化,当apply改变一个字段时,而恰巧该字段被其他用户声明了ownership,此时会发生冲突。 这可以防止一个管理者不小心覆盖掉其他用户设置的值。 举个例子: 如果修改我们刚刚通过**SSA**创建的`test-server-side-apply`configmap,并且手动设置管理者为`test`(通过--field-manager字段),此时`kubectl`会拒绝我们的提交,提示冲突: ```bash -$ kubectl apply --server-side=true --field-manager="test" -f - <