
Karmada 迁移回滚保护机制详解从设计提案到源码实现【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada导读本文围绕 Karmada 多集群编排系统中的迁移回滚保护Migration Rollback Protection能力展开。该能力为联邦资源提供了一种删除策略当用户在 Karmada 控制面删除工作负载时可以选择是否同步删除成员集群中的对应资源从而在业务迁移如将已有集群工作负载迁入 Karmada期间遭遇意外时可以快速回滚而不影响成员集群中仍在运行的工作负载。读完本文你将掌握该特性的 API 设计PreserveResourcesOnDeletion字段如何贯穿 Policy → ResourceBinding → Work 三级对象、各控制器的改造逻辑、完整的使用配置示例以及设计提案中备选方案Annotation 方案与独立 CRD 方案的权衡取舍。本文主体依据仓库中的设计文档 docs/proposals/migration-rollback-protection/README.md并结合源码实现pkg/apis、pkg/detector、pkg/controllers等进行印证与扩充。背景为什么需要迁移回滚保护当前系统的默认行为Karmada 作为多集群编排平台其核心工作方式是用户在 Karmada 控制面创建资源模板如 Deployment通过 PropagationPolicy/ClusterPropagationPolicy 声明分发规则detector 控制器据此生成 ResourceBinding/ClusterResourceBindingbinding 控制器再生成 Work 对象下发到各成员集群最后由 execution 控制器execution-controller将 Work 中的 manifests 应用到成员集群。在提案成文时的默认行为是当用户从 Karmada 控制面删除资源模板时成员集群中的对应资源也会被同步删除。这一级联删除行为在多云多集群的常规运维中是符合预期的但在迁移场景下却可能带来风险。迁移场景中的回滚诉求提案给出了一个典型用户故事Story 1作为管理员在将工作负载迁移到 Karmada 的过程中如果出现意外情况例如云平台无法发布应用或 Pod 遇到意外问题需要利用 Karmada 提供的回滚机制立即恢复到迁移前的状态以快速止损。具体来说当用户把业务从某个已有集群迁入 Karmada 统一管控时Karmada 会在成员集群上以接管的方式打上karmada.io/managed等标签与注解。如果迁移中途发现问题需要回滚最直接的做法是删除控制面的资源模板让 Karmada放手——但默认的级联删除会把成员集群上仍在运行的工作负载一并删掉导致业务中断。迁移回滚保护正是为了解决这一痛点而设计。目标与非目标Goals目标提供在删除控制面资源时保留成员集群资源的能力在保留的同时清理 Karmada 系统附加在成员集群资源上的标签labels、注解annotations等信息让资源与 Karmada 彻底脱钩从而可以独立存活。Non-Goals非目标即本设计明确不涵盖的范围为不同的成员集群定义不同的资源删除策略该策略对所有成员集群统一生效为cronfederatedhpa、federatedhpa、federatedresourcequota等 Karmada 联邦资源提供保留能力其他删除策略例如在 Karmada 控制面保留 Work 对象对成员集群中资源的级联删除控制。约束与注意事项对于未通过 PropagationPolicy 分发的资源例如 namespace无法为其指定删除策略除非关闭自动资源传播的控制器并要求用户通过 PP/CPPPropagationPolicy/ClusterPropagationPolicy来传播资源在一个策略对应多个资源one policy vs multi resource的场景下无法仅针对单个资源执行删除策略——删除策略是绑定在策略层面、对所有匹配资源统一生效的。核心设计扩展 PropagationPolicy/ClusterPropagationPolicy APIAPI 变更新增PreserveResourcesOnDeletion布尔字段设计采用扩展 API 字段的路线在PropagationPolicy/ClusterPropagationPolicy的PropagationSpec中引入一个新的布尔字段PreserveResourcesOnDeletion。该字段会被透明地transparently传递到ResourceBinding/ClusterResourceBinding再到 Work 对象最终由 execution 控制器根据 Work 上的字段值决定删除策略。对应源码位置pkg/apis/policy/v1alpha1/propagation_types.gotype PropagationSpec struct { ... // PreserveResourcesOnDeletion controls whether resources should be preserved on the // member clusters when the resource template is deleted. // If set to true, resources will be preserved on the member clusters. // Default is false, which means resources will be deleted along with the resource template. // // This setting is particularly useful during workload migration scenarios to ensure // that rollback can occur quickly without affecting the workloads running on the // member clusters. // // Additionally, this setting applies uniformly across all member clusters and will not // selectively control preservation on only some clusters. // // Note: This setting does not apply to the deletion of the policy itself. // When the policy is deleted, the resource templates and their corresponding // propagated resources in member clusters will remain unchanged unless explicitly deleted. // // optional PreserveResourcesOnDeletion *bool json:preserveResourcesOnDeletion,omitempty }字段语义要点默认值false即保持原有行为——资源模板被删除时成员集群中的资源一并删除true 的含义资源模板被删除时成员集群中的资源被保留但会清理 Karmada 附加的标签/注解对所有成员集群统一生效不能选择性地只保留部分集群的资源不适用于策略自身的删除当策略Policy被删除时资源模板及其在成员集群中传播的资源保持不变除非被显式删除——即该字段只影响资源模板被删时的行为不影响策略被删时的行为字段类型为*bool指针并带omitempty用于区分未设置与显式设为 false。同样地ResourceBinding/ClusterResourceBinding的 Spec 也扩展了该字段见 pkg/apis/work/v1alpha2/binding_types.gotype ResourceBindingSpec struct { ... // PreserveResourcesOnDeletion controls whether resources should be preserved on the // member clusters when the binding object is deleted. // If set to true, resources will be preserved on the member clusters. // Default is false, which means resources will be deleted along with the binding object. // This setting applies to all Work objects created under this binding object. // optional PreserveResourcesOnDeletion *bool json:preserveResourcesOnDeletion,omitempty }Work的WorkSpec同样扩展见 pkg/apis/work/v1alpha1/work_types.go// WorkSpec defines the desired state of Work. type WorkSpec struct { ... // PreserveResourcesOnDeletion controls whether resources should be preserved on the // member cluster when the Work object is deleted. // If set to true, resources will be preserved on the member cluster. // Default is false, which means resources will be deleted along with the Work object. // optional PreserveResourcesOnDeletion *bool json:preserveResourcesOnDeletion,omitempty }从源码结构看该字段以三级传递的方式贯穿整个资源分发链路Policy → Binding → Work每一级都保留了完全相同的字段名与布尔语义保证删除策略在链路末端execution 控制器可被准确读取。控制器逻辑变更设计文档明确了四个控制器/组件的职责划分下面结合仓库源码逐一印证。1. detectorPolicy → ResourceBinding 的传递detector 控制器负责将PreserveResourcesOnDeletion从 PropagationPolicy/ClusterPropagationPolicy 传递到 ResourceBinding/ClusterResourceBinding。在 pkg/detector/detector.go 与 pkg/detector/detector.go 中可以看到构建 binding 对象时直接引用了policySpec.PreserveResourcesOnDeletionPreserveResourcesOnDeletion: policySpec.PreserveResourcesOnDeletion,同时 detector 在同步 binding 时如 pkg/detector/detector.go 等处也会将既有 binding 上的字段拷贝给新对象确保策略更新后字段保持一致bindingCopy.Spec.PreserveResourcesOnDeletion binding.Spec.PreserveResourcesOnDeletion2. binding-controller / cluster-resource-binding-controllerBinding → Work 的传递binding 控制器负责将PreserveResourcesOnDeletion从 ResourceBinding 传递到 Work。相关实现位于 pkg/controllers/binding/common.go它通过ctrlutil.WithPreserveResourcesOnDeletion这个 WorkOption 来设置 Work 的 Specctrlutil.WithPreserveResourcesOnDeletion(ptr.Deref(bindingSpec.PreserveResourcesOnDeletion, false)),对应的 WorkOption 定义在 pkg/controllers/ctrlutil/workoption.go// WithPreserveResourcesOnDeletion sets the PreserveResourcesOnDeletion field of the Work Spec. func WithPreserveResourcesOnDeletion(preserveResourcesOnDeletion bool) WorkOption { return func(work *workv1alpha1.Work) { work.Spec.PreserveResourcesOnDeletion preserveResourcesOnDeletion } }注意这里使用了ptr.Deref(..., false)即 binding 上未设置该字段时按false默认级联删除处理与 API 注释中的默认语义保持一致。3. execution 控制器依据 Work 字段执行删除策略execution 控制器是最终决策点。在 pkg/controllers/execution/execution_controller.go 的handleWorkDelete中删除 Work 时会先判断该字段func (c *Controller) handleWorkDelete(ctx context.Context, work *workv1alpha1.Work, cluster *clusterv1alpha1.Cluster) error { if ptr.Deref(work.Spec.PreserveResourcesOnDeletion, false) { if err : c.cleanupPolicyClaimMetadata(ctx, work, cluster); err ! nil { klog.ErrorS(err, Failed to remove annotations and labels, cluster, cluster.Name) return err } klog.V(4).InfoS(Preserving resource on deletion from work on cluster, namespace, work.Namespace, name, work.Name, cluster, cluster.Name) return nil } // Abort deleting workload if cluster is unready when unjoining cluster, otherwise the unjoin process will be failed. if util.IsClusterReady(cluster.Status) { err : c.tryDeleteWorkload(ctx, cluster.Name, work) if err ! nil { klog.ErrorS(err, Failed to delete work, name, work.Name, namespace, work.Namespace) return err } } else if cluster.DeletionTimestamp.IsZero() { // cluster is unready, but not terminating return fmt.Errorf(cluster(%s) not ready, cluster.Name) } return nil }其逻辑可归纳为PreserveResourcesOnDeletion true调用cleanupPolicyClaimMetadata清理成员集群资源上由 Karmada 附加的标签与注解然后直接返回不删除资源。日志中会输出Preserving resource on deletion from work on cluster并在工作负载删除流程中跳过tryDeleteWorkloadPreserveResourcesOnDeletion false或未设置走原有删除路径tryDeleteWorkload将 Work 中的 manifests 逐一从成员集群删除。此外若成员集群处于 not ready 状态且未在终止中terminating会返回错误中止删除避免 unjoin 流程失败。cleanupPolicyClaimMetadatapkg/controllers/execution/execution_controller.go的实现细节是遍历work.Spec.Workload.Manifests将每个 manifest 反序列化为unstructured.Unstructured根据资源是否有 namespace 选择调用detector.CleanupCPPClaimMetadata集群级资源或detector.CleanupPPClaimMetadata命名空间级资源随后通过util.RemoveLabels/util.RemoveAnnotations移除util.ManagedResourceLabels与util.ManagedResourceAnnotations中的全部托管标记最后用ObjectWatcher.Update将清理后的对象更新回成员集群。这一过程正是提案 Goals 中同时清理标签/注解等 Karmada 附加信息的落地实现。用户使用示例设置级联删除策略为 orphan设计文档给出了完整的 PropagationPolicy 示例将级联删除策略设置为孤儿orphanapiVersion: policy.karmada.io/v1alpha1 kind: PropagationPolicy metadata: name: nginx-propagation spec: resourceSelectors: - apiVersion: apps/v1 kind: Deployment name: nginx preserveResourcesOnDeletion: true使用时只需在策略的spec中增加一行preserveResourcesOnDeletion: true。此后当删除名为nginx的 Deployment 资源模板时Karmada 不再级联删除成员集群上的对应 Deployment而是清理其托管标签/注解后保留。相同的字段同样适用于ClusterPropagationPolicy集群级策略。设计问答QA设计文档还记录了社区评审时讨论的两个关键问题Q1依赖资源与主资源的删除策略是否强制绑定不强制绑定。因为依赖资源dependent resources即通过依赖分发机制自动传播的关联资源可能被多个资源模板共享此时难以决定依赖资源应采用哪种删除策略。将其留给用户自行决策可以获得更大的灵活性与扩展性。Q2成员集群的工作负载是否只需清理karmada.io/managed标签即可逻辑上一旦karmada.io/managed标签被清理该资源与 Karmada 的托管关系即告解除。不过从实现上看execution 控制器实际清理的是util.ManagedResourceLabels与util.ManagedResourceAnnotations所定义的整套托管标记不限于单一标签确保资源彻底脱离 Karmada 的接管。备选方案一通过 Annotation 扩展cascadedeletion 注解方案概述第一个备选方案不修改 API 字段而是允许用户在 Karmada 控制面的资源模板上添加一个新注解resourcetemplate.karmada.io/cascadedeletion。为了可扩展性该注解的取值采用字符串枚举类型当前支持orphan保留成员集群中的资源并清理 Karmada 系统附加在成员集群资源上的标签/注解等信息。当用户未指定该注解时系统保持当前行为——同步删除成员集群中的资源。设计文档中给出了该方案的处理流程图图中展示了成员集群工作负载删除的执行逻辑用户在控制面发起delete workload操作后进入 execution controller控制器尝试在成员集群中删除工作负载随后通过 orphan 判断节点分支若为孤儿yes则改为更新成员集群工作负载清理托管标记后保留否则no直接删除成员集群工作负载。控制器逻辑变更用户添加到资源模板上的resourcetemplate.karmada.io/cascadedeletion注解会被传播到work.spec.workload.manifests。当资源模板被删除时execution 控制器执行删除 Work 对象的逻辑可解析该注解并执行如下判断若目标注解不存在同步删除成员集群中的资源若目标注解值为orphan保留成员集群中的资源并清理 Karmada 附加的标签/注解。用户使用示例apiVersion: apps/v1 kind: Deployment metadata: annotations: propagationpolicy.karmada.io/name: foo propagationpolicy.karmada.io/namespace: default resourcetemplate.karmada.io/cascadedeletion: orphan ...用户只需在资源模板的metadata.annotations中加入resourcetemplate.karmada.io/cascadedeletion: orphan即可为该资源单独指定孤儿删除策略。相比策略字段方案注解方式更贴近资源本身但注解作为 API 略显非正式。分支想法在 Work API 中增加 CascadeDeletion 字段在该方案下还有一个分支思路直接在 Work API 中增加一个CascadeDeletion字段这样 execution 控制器就无需再解析work.spec.workload.manifests// WorkSpec defines the desired state of Work. type WorkSpec struct { ... // CascadeDeletion Declare the cascade deletion strategy. The default value is null, which is equivalent to background. // optional CascadeDeletion *CascadeDeletionPolicy json:cascadeDeletion,omitempty }此时控制器职责为binding-controller根据资源注解设置 Work 对象中的CascadeDeletion字段cluster-resource-binding-controller同样根据资源注解设置 Work 对象中的CascadeDeletion字段execution-controller基于 Work 中的CascadeDeletion字段执行资源删除。该方案的补充说明提案原文 Note对于 namespace 资源Karmada 系统中的namespace-sync-controller会自动将用户创建的每个新 namespace 传播到成员集群其实现方式是直接生成 Work 对象。因此在 Work 中新增 API 字段的方案下namespace-sync-controller需要负责处理该字段例如根据资源注解为其生成的 Work 设置 CascadeDeletion。优劣势小结劣势将注解作为 API 使用略显非正式informal优势相对于独立 CRD 方案用户学习成本低、控制面无需新增资源类型注解可直接附着在资源模板上且天然支持按资源粒度配置。备选方案二通过新增独立 CRD 扩展CascadeDeletionPolicy方案概述第二个备选方案是新增一个 CRD 资源CascadeDeletionPolicy用户通过创建该 CRD 的 CRCustom Resource实例来描述目标资源的删除策略。API 变更type CascadeDeletionPolicy struct { metav1.TypeMeta json:,inline metav1.ObjectMeta json:metadata,omitempty // Spec represents the desired cascadeDeletion Behavior. Spec CascadeDeletionSpec json:spec // Status represents the status of cascadeDeletion. // optional Status CascadeDeletionStatus json:status,omitempty } type CascadeDeletionSpec struct { // CascadeDeletion Declare the cascade deletion strategy. The default value is null, which is equivalent to background. // optional CascadeDeletion *CascadeDeletionPolicy json:cascadeDeletion,omitempty // ResourceSelectors used to select resources. // Nil or empty selector is not allowed and doesnt mean match all kinds // of resources for security concerns that sensitive resources(like Secret) // might be accidentally propagated. // required // kubebuilder:validation:MinItems1 ResourceSelectors []ResourceSelector json:resourceSelectors } // ResourceSelector the resources will be selected. type ResourceSelector struct { // APIVersion represents the API version of the target resources. // required APIVersion string json:apiVersion // Kind represents the Kind of the target resources. // required Kind string json:kind // Namespace of the target resource. // Default is empty, which means inherit from the parent object scope. // optional Namespace string json:namespace,omitempty // Name of the target resource. // Default is empty, which means selecting all resources. // optional Name string json:name,omitempty // A label query over a set of resources. // If name is not empty, labelSelector will be ignored. // optional LabelSelector *metav1.LabelSelector json:labelSelector,omitempty } type CascadeDeletionStatus struct { ... }关键设计点ResourceSelectors为必填且最少 1 项kubebuilder:validation:MinItems1。空或 nil 的选择器不允许出现且不等于匹配所有资源——这是出于安全考虑防止 Secret 等敏感资源被意外纳入ResourceSelector支持按 apiVersion kind 定位资源类型并可选通过 namespace、name 精确定位或通过 labelSelector 批量选择当 name 非空时 labelSelector 会被忽略CascadeDeletionStatus用于表达策略的状态设计文档中留作扩展未展开。同时 Work 的WorkSpec也增加CascadeDeletion字段*CascadeDeletionPolicy类型作为删除策略传递到成员集群侧的载体。控制器逻辑变更binding-controller/cluster-resource-binding-controller在创建或更新 Work 对象时检查是否存在与目标资源关联的CascadeDeletionPolicy若存在则将删除策略同步进 Work 对象execution-controller基于 Work 对象中的CascadeDeletion字段执行资源删除。用户使用示例apiVersion: policy.karmada.io/v1alpha1 kind: CascadeDeletionPolicy metadata: name: foo spec: cascadeDeletion: orphan resourceSelectors: - apiVersion: apps/v1 kind: Deployment name: foo namespace: default优劣势小结劣势增加了用户的学习成本并且使 Karmada 控制面中的资源种类/数量增多每个需要定制删除策略的资源都要对应一个 CR 实例优势策略与资源解耦可以通过 ResourceSelector 批量圈定资源范围语义更清晰便于集中管理删除策略。方案对比与最终选型三种方案的核心差异可以归结为删除策略的载体放在哪里维度方案一策略字段最终方案方案二资源模板注解方案三独立 CRD配置载体PropagationPolicy/CPP 的preserveResourcesOnDeletion布尔字段资源模板上的resourcetemplate.karmada.io/cascadedeletion注解独立CascadeDeletionPolicyCRD 对象传递链路Policy → Binding → Work三级同名字段注解随 manifest 进入 Workexecution 解析注解binding 控制器查询关联策略后写入 Work 的CascadeDeletion字段用户学习成本低策略字段与既有分发配置同处一处低注解直接写在资源上较高需理解新 CRD 及 ResourceSelector 语义控制面资源开销无新增无新增每个策略一个 CR 实例API 规范性高类型化字段、带默认值与文档化语义中注解作为 API 略显非正式高独立类型化 CRD批量管理能力按策略统一生效一策略多资源场景下不可按资源细分按资源逐个注解支持 ResourceSelector 批量圈定最终采用方案一扩展 PropagationPolicy/ClusterPropagationPolicy 字段从源码落地上看该方案已经在当前仓库中完整实现字段定义于三个 API 类型中propagation_types.go、binding_types.go、work_types.go并分别由 detectorpkg/detector/detector.go、binding 控制器pkg/controllers/binding/common.go与 execution 控制器pkg/controllers/execution/execution_controller.go完成传递与最终决策构成一条完整、可追踪的实现链路。使用建议与边界提醒迁移回滚的标准动作迁移开始前在对应 PropagationPolicy或 ClusterPropagationPolicy中设置preserveResourcesOnDeletion: true迁移失败需要回滚时直接删除控制面的资源模板成员集群工作负载将保留且已与 Karmada 脱钩业务无缝回落策略删除与资源删除语义不同该字段不作用于策略自身的删除——删除策略不会触发任何级联删除资源模板与成员集群资源保持不变除非显式删除粒度限制字段对所有成员集群统一生效不支持按集群差异化配置同时由于绑定在策略层面在一个策略管理多个资源的场景下无法仅对其中某个资源单独启用保留策略提案原文In one policy vs multi resource scene, we cant execute delete policy just by per resourcenamespace 等自动传播资源不受支持未被 PP/CPP 管理的资源如自动传播的 namespace无法通过该字段控制删除行为保留 ≠ 永久托管保留资源时 execution 控制器会主动清理util.ManagedResourceLabels/util.ManagedResourceAnnotations定义的托管标记资源将脱离 Karmada 的管理范围后续如需重新纳入 Karmada 管控需要重新接入。结语迁移回滚保护是 Karmada 面向业务迁移这一真实运维场景提供的关键兜底能力。它以最小化的 API 变更一个贯穿 Policy → Binding → Work 的布尔字段换取迁移过程中的快速回滚保障同时通过清理托管标签/注解实现资源与 Karmada 的干净解耦。设计文档中记录的 Annotation 与独立 CRD 两条备选路线也从配置灵活性与API 规范性两个维度给出了完整权衡为后续在删除策略上的进一步演进例如按集群差异化策略、联邦资源级联策略保留了清晰的扩展空间。读者可直接参照本文的配置示例在当前仓库对应的控制器源码中追踪该特性的完整执行链路。【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考