本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
Kubernetes 1.22:服务器端应用进入 GA 阶段
服务端应用(Server-side Apply,SSA)已在 Kubernetes v1.22 版本中晋升为 GA(正式发布)。GA 里程碑意味着您可以依赖该功能及其 API,而无需担心未来不兼容的更改。GA 功能受 Kubernetes 弃用策略的保护。
什么是服务端应用?
服务端应用帮助用户和控制器通过声明式配置管理其资源。服务端应用将“kubectl apply”实现的客户端应用功能替换为服务端实现,允许除 kubectl 之外的工具/客户端使用。服务端应用是一种新的合并算法,以及在 Kubernetes api-server 上运行的字段所有权跟踪。服务端应用启用了冲突检测等新功能,因此系统知道两个参与者何时尝试编辑同一字段。有关更多信息,请参阅服务端应用文档和Beta 2 发布公告。
自 Beta 版以来有什么新功能?
自Beta 2 发布以来,已添加了对子资源的支持,并且 client-go 和 Kubebuilder 都添加了对服务端应用的全面支持。这完善了控制器开发所需的服务端应用功能。
支持子资源
服务端应用现在完全支持子资源,如status
和scale
。这对于通常负责写入子资源的控制器来说尤为重要。
client-go 中的服务端应用支持
以前,服务端应用只能通过 client-go 类型客户端使用Patch
函数调用,并将PatchType
设置为ApplyPatchType
。现在,客户端中包含了Apply
函数,允许以更直接、类型安全的方式调用服务端应用。每个Apply
函数都接受一个“应用配置”类型作为参数,这是一个应用请求的结构化表示。例如:
import (
...
v1ac "k8s.io/client-go/applyconfigurations/autoscaling/v1"
)
hpaApplyConfig := v1ac.HorizontalPodAutoscaler(autoscalerName, ns).
WithSpec(v1ac.HorizontalPodAutoscalerSpec().
WithMinReplicas(0)
)
return hpav1client.Apply(ctx, hpaApplyConfig, metav1.ApplyOptions{FieldManager: "mycontroller", Force: true})
请注意,在此示例中,HorizontalPodAutoscaler
是从“applyconfigurations”包导入的。每个“应用配置”类型都代表与相应 go struct 相同的 Kubernetes 对象类型,但所有字段都是指针以使其可选,从而允许准确表示应用请求。例如,当上述示例中的应用配置被整理为 YAML 时,它会生成
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: myHPA
namespace: myNamespace
spec:
minReplicas: 0
要理解为什么需要这样做,上面的 YAML 无法由 v1.HorizontalPodAutoscaler go struct 生成。例如:
hpa := v1.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{
APIVersion: "autoscaling/v1",
Kind: "HorizontalPodAutoscaler",
},
ObjectMeta: ObjectMeta{
Namespace: ns,
Name: autoscalerName,
},
Spec: v1.HorizontalPodAutoscalerSpec{
MinReplicas: pointer.Int32Ptr(0),
},
}
上面的代码尝试声明与前面示例中所示的相同应用配置,但当整理为 YAML 时,会生成
kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v1
metadata
name: myHPA
namespace: myNamespace
creationTimestamp: null
spec:
scaleTargetRef:
kind: ""
name: ""
minReplicas: 0
maxReplicas: 0
其中,除其他外,包含spec.maxReplicas
设置为0
。这几乎肯定不是调用者所期望的(预期的应用配置没有提及maxReplicas
字段),并且可能对生产系统造成严重后果:它指示自动伸缩器将 Pod 缩减为零。这里的问题源于 go struct 包含必需字段,如果未明确设置,这些字段的值为零。go struct 对于创建和更新操作按预期工作,但与应用从根本上不兼容,这就是我们引入生成的“应用配置”类型的原因。
“应用配置”还具有方便的With<FieldName>
函数,使构建应用请求变得更容易。这允许开发人员设置字段,而无需处理“应用配置”类型中所有字段都是指针且不方便使用 go 设置的事实。例如,MinReplicas: &0
不是合法的 go 代码,因此如果没有With
函数,开发人员将通过使用库来解决此问题,例如MinReplicas: pointer.Int32Ptr(0)
,但像corev1.Protocol
这样的字符串枚举仍然是一个问题,因为通用库无法支持它们。除了便利性之外,With
函数还将开发人员与底层表示隔离开来,这使得更改底层表示以在将来支持其他功能更加安全。
在控制器中使用服务端应用
无论您如何实现控制器,都可以使用新的服务端应用支持。但是,新的 client-go 支持使得在控制器中使用服务端应用变得更容易。
在编写使用服务端应用的新控制器时,一个好的方法是让控制器每次协调对象时重新创建该对象的应用配置。这确保控制器完全协调其负责的所有字段。控制器通常应通过在ApplyOptions
中设置Force: true
来无条件设置其拥有的所有字段。控制器还必须提供一个FieldManager
名称,该名称对于调用应用的协调循环来说是唯一的。
在将现有控制器升级到使用服务端应用时,同样的方法通常也适用——将控制器迁移为每次协调任何对象时重新创建应用配置。不幸的是,控制器可能具有多个代码路径,根据各种条件更新对象的不同部分。将这样的控制器迁移到服务端应用可能会有风险,因为如果控制器忘记在应用请求中包含之前包含的任何字段,则该字段可能会被意外删除。为了简化此类迁移,client-go 应用支持提供了一种方法,可以用“提取/就地修改/应用”工作流替换执行“读取/就地修改/更新”(或修补)工作流的任何控制器协调代码。以下是新工作流的示例:
fieldMgr := "my-field-manager"
deploymentClient := clientset.AppsV1().Deployments("default")
// read, could also be read from a shared informer
deployment, err := deploymentClient.Get(ctx, "example-deployment", metav1.GetOptions{})
if err != nil {
// handle error
}
// extract
deploymentApplyConfig, err := appsv1ac.ExtractDeployment(deployment, fieldMgr)
if err != nil {
// handle error
}
// modify-in-place
deploymentApplyConfig.Spec.Template.Spec.WithContainers(corev1ac.Container().
WithName("modify-slice").
WithImage("nginx:1.14.2"),
)
// apply
applied, err := deploymentClient.Apply(ctx, deploymentApplyConfig, metav1.ApplyOptions{FieldManager: fieldMgr})
对于使用自定义资源定义 (CRD) 的开发人员,Kubebuilder 应用支持将提供相同的功能。可用时,文档将包含在 Kubebuilder 书籍中。
服务端应用和 CustomResourceDefinitions
强烈建议所有自定义资源定义 (CRD) 都应有模式。没有模式的 CRD 被服务端应用视为非结构化数据。键被视为结构体中的字段,列表被假定为原子性的。
指定模式的 CRD 可以在模式中指定额外的注解。请参阅文档以获取可用注解的完整列表。
自 Beta 版以来的新注解
默认值:未明确表达兴趣的字段的值应默认为默认值。这可以防止应用者无意中拥有可能与其他应用者冲突的默认字段。如果未指定,默认值为 nil 或相应类型的 nil 等效值。
- 用法:有关更多详细信息,请参阅CRD 默认值文档。
- Golang:
+default=<value>
- OpenAPI 扩展:
default: <value>
映射和结构体的原子性
映射:默认情况下,映射是细粒度的。不同的管理器能够管理每个映射条目。它们也可以配置为原子性,这样单个管理器就拥有整个映射。
- 用法:有关更详细的概述,请参阅合并策略
- Golang:
+mapType=granular/atomic
- OpenAPI 扩展:
x-kubernetes-map-type: granular/atomic
结构体:默认情况下,结构体是细粒度的,每个字段可以由单独的应用者拥有。对于某些类型的结构体,可能需要原子性。这最常见于小型坐标式结构体,例如字段/对象/命名空间选择器、对象引用、RGB 值、端点(协议/端口对)等。
- 用法:有关更详细的概述,请参阅合并策略
- Golang:
+structType=granular/atomic
- OpenAPI 扩展:
x-kubernetes-map-type:atomic/granular
接下来呢?
在服务端应用之后,API 表达式工作组的下一个重点是提高已发布的 Kubernetes API 模式的表达能力和大小。要查看我们正在处理的完整项目列表,请加入我们的工作组并参阅工作项目文档。
如何参与?
应用工作组是 wg-api-expression。它在 Slack #wg-api-expression 上可用,通过邮件列表,我们还每隔一个星期二在太平洋时间上午 9:30 在 Zoom 上开会。
我们还要借此机会感谢所有贡献者的辛勤工作,使这次晋升为 GA 成为可能
- Andrea Nodari
- Antoine Pelisse
- Daniel Smith
- Jeffrey Ying
- Jenny Buckley
- Joe Betz
- Julian Modesto
- Kevin Delgado
- Kevin Wiesmüller
- Maria Ntalla