(Alpha)Server-Side Apply
--ssa 标志会脚手架生成带 Server-Side Apply 支持的 API,使多个参与者修改同一资源时字段管理更安全。
这将添加:
- 用于生成 ApplyConfiguration 的 API 标记(
+genclient、+kubebuilder:ac:generate=true) - Makefile 集成,在生成 CRD 与 RBAC 清单的同时生成类型安全的 ApplyConfiguration 类型
何时使用它
在以下情况使用 Server-Side Apply:
- 多个控制器或用户管理同一资源
- 用户用标签、注解或你的控制器不应覆盖的 spec 字段自定义 CR
- 你需要声明式字段所有权跟踪
- 其他 Operator 将管理你的 CR 实例(它们可导入你生成的 ApplyConfiguration 类型)
它是如何工作的
传统的 Update() 会覆盖整个对象。使用客户端 Apply 方法的 Server-Side Apply 只管理你指定的字段。
运行 make manifests 后,ApplyConfiguration 类型创建于:
api/<version>/applyconfiguration/api/<version>/ (single group)
api/<group>/<version>/applyconfiguration/<group>/<version>/ (multi-group)
导入方式如下(多 group 项目使用 example.com/myproject/api/<group>/v1/applyconfiguration/<group>/v1):
appsv1apply "example.com/myproject/api/v1/applyconfiguration/api/v1"
混合使用 SSA 与非 SSA API
groupversion_info.go 中的 +kubebuilder:ac:generate=true marker 是包级的:它为该 group/version 启用 ApplyConfiguration 生成。每个 kind 再用自己的 +kubebuilder:ac:generate marker 选择加入或排除:用 --ssa 脚手架的 kind 为 =true,未使用的为 =false。当你用 --ssa 创建 API 时,该 group/version 中此前脚手架且没有此 marker 的 kind 会自动获得它。
要更改生成哪些 kind:
- 在 kind 上方设置
+kubebuilder:ac:generate=true以包含它 - 在 kind 上方设置
+kubebuilder:ac:generate=false以排除它
Usage
使用 --ssa 标志创建 API:
kubebuilder create api --group apps --version v1 --kind Application --ssa
在控制器中实现 Server-Side Apply:
import (
"context"
metav1apply "k8s.io/client-go/applyconfigurations/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1apply "example.com/myproject/api/v1/applyconfiguration/api/v1"
)
func (r *ApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// Build desired state - specify only fields you manage
app := appsv1apply.Application(req.Name, req.Namespace).
WithStatus(appsv1apply.ApplicationStatus().
WithConditions(metav1apply.Condition().
WithType("Available").
WithStatus("True").
WithReason("Reconciled")))
// Apply status, forcing ownership of the fields this controller manages
if err := r.SubResource("status").Apply(ctx, app,
client.FieldOwner("application-controller"), client.ForceOwnership); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
然后运行:
make generate manifests
最佳实践
- 始终指定
client.FieldOwner("controller-name")以标识你的控制器 - Controller 应对所管理的字段传入
client.ForceOwnership;Kubernetes 建议 controller 始终强制解决冲突 - 使用 builder 模式仅指定你的控制器所管理的字段