Karmada 依赖自动传播(PropagateDeps)深度指南:让 ConfigMap、Secret 随工作负载自动分发到成员集群
2026/9/18 5:06:26 网站建设 项目流程

Karmada 依赖自动传播(PropagateDeps)深度指南:让 ConfigMap、Secret 随工作负载自动分发到成员集群

【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada

导读

在多集群编排场景下,一个 Deployment 往往依赖 ConfigMap、Secret、PVC、Service 等周边资源。Karmada 的"依赖自动传播"(Dependencies Automatically Propagation,特性门控PropagateDeps)允许你只配置工作负载自身的 PropagationPolicy,让依赖资源自动跟随工作负载被分发到相同的成员集群。本文以 Karmada 官方提案 docs/proposals/dependencies-automatically-propagation/README.md 为主体,结合当前仓库源码与测试,完整讲解该特性的设计动机、API 变更、核心控制器实现原理与端到端使用示例,读完即可在真实集群中配置并验证该能力。

为什么需要依赖自动传播

在 Kubernetes 中,Pod 可以通过数据卷(volume)或环境变量引用 ConfigMap 与 Secret;一组 Pod 也可以由 Service 暴露为服务,并由 Ingress 对外提供访问。这意味着:当使用 Karmada 把一个 Deployment 部署到成员集群时,它所依赖的 ConfigMap、Secret,以及依赖它的 Service、Ingress,都必须"跟随"Pod 一起进入相同的成员集群,否则应用将无法正常运行。

用户通常不会直接创建 Pod,而是通过 Deployment、StatefulSet、Job 等工作负载资源间接创建。以 Deployment 为例,它的依赖资源包括 ConfigMap、Secret、PVC、Service 等,其依赖关系如下图所示:

在引入自动传播之前,Karmada 提供两种方式传播依赖资源:

  1. 所有依赖资源与 Deployment 共享同一个 PropagationPolicy:即把 ConfigMap、Secret 也写进resourceSelectors一起传播。
  2. 为每个依赖资源单独创建 PropagationPolicy:需要用户自行保证依赖资源被调度到需要的集群。

这两种方式在多数场景下可行,但各有明显缺点:

  • 方式一将依赖资源与某一个 Deployment"绑定",该 ConfigMap/Secret 被一个 Deployment 引用后,就无法再被其他 Deployment 复用,灵活性差。
  • 方式二要求用户持续关注 Deployment 的调度结果,人为维护成本高、易出错。

PropagateDeps提案的目标,就是提供一种"智能"策略:当用户传播一个工作负载时,Karmada 自动把其依赖资源传播到所需集群,免去手工维护。

提案目标与边界

Goals(目标)

  • 提供一种策略,自动把工作负载资源的依赖资源传播到所需集群。

Non-Goals(非目标)

  • 不废弃原有的默认传播流程:ConfigMap、Secret、Service 等资源依然可以像往常一样通过 PropagationPolicy 单独传播。
  • 不改变已有的独立传播语义,两种方式长期共存。

特性启用方式

依赖自动传播能力通过karmada-controller-manager的特性门控控制,启用命令为:

--feature-gates=PropagateDeps=true

从当前仓库源码看,该特性已进入 Beta 阶段且默认开启。在 pkg/features/features.go 中定义:

// PropagateDeps indicates if relevant resources should be propagated automatically PropagateDeps featuregate.Feature = "PropagateDeps"

其默认值在 pkg/features/features.go 中注册:

PropagateDeps: {Default: true, PreRelease: featuregate.Beta},

也就是说,当前仓库版本中PropagateDeps默认即为 true(Beta 特性默认开启)。如果你使用的版本默认关闭,则需要在部署karmada-controller-manager时显式追加上述参数。

整体方案架构

提案引入了一个新控制器——Dependencies Distributor(依赖分发器),用于智能传播依赖资源。当用户传播一个 Deployment 时,Karmada 各控制器协作完成依赖分发。整个方案分为五步:

  1. PropagationPolicyAPI 增加PropagateDeps字段;
  2. ResourceBindingAPI 增加PropagateDepsRequiredBy字段;
  3. 新增 Dependencies Distributor 控制器智能传播依赖资源;
  4. 新增 ResourceInterpreter 能力解析给定对象的依赖资源;
  5. 配套文档与架构图补充。

为便于说明,提案把资源绑定分为两类:

  • 独立资源绑定(independent resource binding):资源被某个 PropagationPolicy 匹配后创建的绑定;
  • 附属资源绑定(attached resource binding):控制器传播依赖资源时创建的绑定,每个依赖资源对应一个独立的附属绑定。

整体架构如下:

需要特别注意的三个设计约束:

  • 每个依赖资源拥有独立的附属资源绑定,因此同一个 Secret 可以被多个 Deployment 引用,天然支持资源共享。
  • 控制器从**原始资源模板(raw resource template)**中解析依赖,也就是说,由 OverridePolicy 引入的依赖无法被识别——解析发生在覆盖策略生效之前。
  • 用户的 User Story 场景为:创建 Deployment 与 PropagationPolicy 且propagateDepstrue时,即使 Deployment 引用的 ConfigMap/Secret 尚未创建,一旦它们被创建,也会被自动传播到与 Deployment 相同的集群。

API 变更详解

提案对PropagationPolicyResourceBinding两个 API 进行扩展(ClusterPropagationPolicyClusterResourceBinding同步扩展)。

PropagationPolicy:新增 PropagateDeps

在 pkg/apis/policy/v1alpha1/propagation_types.go 中,PropagationSpec新增PropagateDeps字段:

// PropagateDeps tells if relevant resources should be propagated automatically. // Take 'Deployment' which referencing 'ConfigMap' and 'Secret' as an example, when 'propagateDeps' is 'true', // the referencing resources could be omitted(for saving config effort) from 'resourceSelectors' as they will be // propagated along with the Deployment. In addition to the propagating process, the referencing resources will be // migrated along with the Deployment in the fail-over scenario. // // Defaults to false. // +optional PropagateDeps bool `json:"propagateDeps,omitempty"`

含义与注意事项:

  • propagateDepstrue时,被引用的资源(如 ConfigMap、Secret)可以省略resourceSelectors中,它们会随 Deployment 一起传播,显著减少配置量;
  • 除传播过程外,在**故障转移(fail-over)**场景下,被引用资源也会随 Deployment 一起迁移,保证业务连续性;
  • 默认值为false
  • 从源码看,提案早期设想的Association字段已被标记为Deprecated(已弃用),官方建议改用PropagateDeps(见 pkg/apis/policy/v1alpha1/propagation_types.go)。

ResourceBinding:新增 PropagateDeps 与 RequiredBy

在 pkg/apis/work/v1alpha2/binding_types.go 中,ResourceBindingSpec新增两个字段:

// ResourceBindingSpec represents the expectation of ResourceBinding. type ResourceBindingSpec struct { // PropagateDeps tells if relevant resources should be propagated automatically. // It is inherited from PropagationPolicy or ClusterPropagationPolicy. // default false. // +optional PropagateDeps bool `json:"propagateDeps,omitempty"` // RequiredBy represents the list of Bindings that depend on the referencing resource. // +optional RequiredBy []BindingSnapshot `json:"requiredBy,omitempty"` }
  • PropagateDeps:继承自PropagationPolicyClusterPropagationPolicy,默认 false;
  • RequiredBy:记录"依赖该资源的绑定"列表,即哪些独立绑定引用了当前依赖资源,是附属绑定与独立绑定之间的关联纽带。

BindingSnapshot定义在同文件 pkg/apis/work/v1alpha2/binding_types.go:

// BindingSnapshot is a snapshot of a ResourceBinding or ClusterResourceBinding. type BindingSnapshot struct { // Namespace represents the namespace of the Binding. // It is required for ResourceBinding. // If Namespace is not specified, means the referencing is ClusterResourceBinding. // +optional Namespace string `json:"namespace,omitempty"` // Name represents the name of the Binding. // +required Name string `json:"name"` // Clusters represents the scheduled result. // +optional Clusters []TargetCluster `json:"clusters,omitempty"` }

字段说明:

  • Namespace:绑定的命名空间;对ResourceBinding必填;未指定时表示引用的是ClusterResourceBinding(集群级绑定);
  • Name:绑定的名称,必填;
  • Clusters:调度结果快照,即被引用绑定最终调度到的成员集群列表。

正是通过RequiredBy中的Clusters快照,附属绑定可以"知道"独立绑定的调度结果,从而把依赖资源分发到相同集群;当独立绑定因故障转移或重新调度而改变目标集群时,快照会同步更新,依赖资源随之迁移。

核心实现:Dependencies Distributor 控制器

Dependencies Distributor 是自动传播依赖资源的核心控制器,代码位于 pkg/dependenciesdistributor/dependencies_distributor.go,控制器注册名为dependencies-distributor(见同文件第 68 行),并在 cmd/controller-manager/app/controllermanager.go 中挂载到 controller-manager。

控制器职责与工作模式

控制器的整体设计逻辑(源码注释 + 代码结构):

  • 当某个资源(如 Deployment)被 PropagationPolicy 匹配后,会创建对应的独立绑定(independent binding)
  • 当 Dependencies Distributor 工作时,它为依赖资源(如 Secret)创建或更新对应的附属绑定(attached bindings)

控制器同时实现了manager.Runnablemanager.LeaderElectionRunnable接口,意味着它以领导者选举模式运行,避免多副本同时分发产生竞争(见 pkg/dependenciesdistributor/dependencies_distributor.go)。

关键处理流程

Reconcile方法是主流程(见 pkg/dependenciesdistributor/dependencies_distributor.go),核心步骤:

  1. 获取独立绑定并校验:若绑定不存在则直接返回;若用户将PropagateDeps从 true 改为 false,或绑定正在删除,则调用handleIndependentBindingDeletion清理所有附属绑定并移除 finalizer(代码注释明确指出这一场景:in case users set PropagateDeps field from "true" to "false");
  2. 获取资源模板:通过FetchResourceTemplate从 informer 缓存获取独立绑定引用的工作负载对象;
  3. 检查解释器钩子:调用ResourceInterpreter.HookEnabled判断该 GVK 是否支持InterpreterOperationInterpretDependency操作,不支持则直接跳过;
  4. 解析依赖:调用ResourceInterpreter.GetDependencies获取依赖资源列表,成功/失败均上报 Kubernetes Event(EventReasonGetDependenciesSucceed/EventReasonGetDependenciesFailed);
  5. 添加 finalizeraddFinalizer为独立绑定添加util.BindingDependenciesDistributorFinalizer,确保删除独立绑定时有机会清理附属绑定;
  6. 同步调度结果syncScheduleResultToAttachedBindings把独立绑定的调度结果同步到所有附属绑定。

同步调度结果:syncScheduleResultToAttachedBindings

该函数(见 pkg/dependenciesdistributor/dependencies_distributor.go)依次执行:

  1. recordDependencies:把依赖列表序列化为 JSON,写入独立绑定的注解resourcebinding.karmada.io/dependencies(注解 key 定义在 pkg/util/constants.go)。若依赖未变化则跳过更新,减少写放大;
  2. removeOrphanAttachedBindings:找出不再被当前独立绑定引用的"孤儿"附属绑定,把当前独立绑定从它们的RequiredBy中移除;
  3. 遍历每个依赖项,通过RESTMapper转换为 GVR,动态注册 informer监听该类资源的新增/更新/删除事件,然后调用handleDependentResource创建或更新附属绑定。

handleDependentResource(见 pkg/dependenciesdistributor/dependencies_distributor.go)支持两种依赖定位方式:

  • 按名称(Name):直接获取指定名称的资源模板并创建附属绑定;若资源模板尚不存在(IsNotFound),静默跳过,等待资源创建后再处理——这正是"引用资源晚于 Deployment 创建也能被传播"的实现基础;
  • 按标签选择器(LabelSelector):通过FetchResourceTemplatesByLabelSelector获取所有匹配的资源并逐一创建附属绑定。

附属绑定的创建与合并:createOrUpdateAttachedBinding

buildAttachedBinding(见 pkg/dependenciesdistributor/dependencies_distributor.go)构建附属绑定时:

  • 名称由names.GenerateBindingName(object.GetKind(), object.GetName())生成;
  • 为附属绑定添加形如resourcebinding.karmada.io/depended-by-<独立绑定标识>的标签(key 前缀见第 78 行dependedByLabelKeyPrefix),用于后续按独立绑定关联检索附属绑定;label key 之所以要求唯一,正是因为同一个 Secret 可能被多个 Deployment 引用;
  • 设置OwnerReferences指向依赖资源本身(控制器引用),当依赖资源被删除时由垃圾回收器联动清理;
  • Spec.RequiredBy记录引用它的独立绑定快照;PreserveResourcesOnDeletionConflictResolution从独立绑定继承。

createOrUpdateAttachedBinding(见 pkg/dependenciesdistributor/dependencies_distributor.go)通过CreateOrUpdate幂等合并:调用mergeBindingSnapshot把新的 BindingSnapshot 合并进已有RequiredBy(相同 Namespace+Name 时更新 Clusters,否则追加),实现"一个依赖资源被多个工作负载共享"。

一个值得关注的细节:当多个独立绑定引用同一个依赖资源且策略字段不一致时,控制器会自动检测并消解冲突(见 pkg/dependenciesdistributor/policy_utils.go):

  • ConflictResolution:任一引用绑定为Overwrite则取Overwrite,否则取Abort
  • PreserveResourcesOnDeletion:任一引用绑定为 true 则取 true,否则 false;
  • 若检测到冲突,会通过recordEventIfPolicyConflict上报DependencyPolicyConflict类型警告事件。

事件驱动的资源模板监听

Dependencies Distributor 不仅 watch ResourceBinding,还会通过 informer 监听被引用的依赖资源本身。reconcileResourceTemplate(见 pkg/dependenciesdistributor/dependencies_distributor.go)在依赖资源发生增删改时,遍历同命名空间下所有 ResourceBinding,通过matchesWithBindingDependencies匹配注解resourcebinding.karmada.io/dependencies中记录的依赖引用(支持按 Name 精确匹配或按 LabelSelector 匹配),匹配成功的绑定会推入genericEvent通道触发 Reconcile。

事件过滤器(见SetupWithManager中 pkg/dependenciesdistributor/dependencies_distributor.go)的细节:

  • 创建事件:仅当Spec.PropagateDeps == true且已产生调度结果(Spec.Clusters非空)才入队,未完成调度的绑定被丢弃;
  • 更新事件:要求Generation发生变化,且新旧绑定任一开启PropagateDeps
  • 删除事件:仅处理开启PropagateDeps的绑定。

依赖解析:ResourceInterpreter 的 InterpretDependency 操作

自动传播的关键一环是"如何知道一个工作负载依赖哪些资源"。提案第 4 步引入的 ResourceInterpreter 支持InterpretDependency操作,返回[]DependentObjectReference

DependentObjectReference定义在 pkg/apis/config/v1alpha1/interpretercontext_types.go:

type DependentObjectReference struct { // APIVersion represents the API version of the referent. APIVersion string `json:"apiVersion"` // Kind represents the Kind of the referent. Kind string `json:"kind"` // Namespace represents the namespace for the referent. Namespace string `json:"namespace,omitempty"` // Name represents the name of the referent. // Name and LabelSelector cannot be empty at the same time. Name string `json:"name,omitempty"` // LabelSelector represents a label query over a set of resources. // If name is not empty, labelSelector will be ignored. LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` }

注意:NameLabelSelector不能同时为空;Namespace为空表示引用的是集群级(非命名空间)资源,例如ClusterRole

内置(默认)解释器

Karmada 内置解释器为常见工作负载提供了开箱即用的依赖解析,实现在 pkg/resourceinterpreter/default/native/dependencies.go,覆盖类型包括:

  • Deployment(第 56 行getDeploymentDependencies
  • ReplicaSet(第 70 行)
  • Job(第 84 行)
  • CronJob(第 99 行)
  • Pod(第 114 行)
  • DaemonSet(第 124 行)
  • 以及 StatefulSet 等

其实现模式高度统一:先把 unstructured 对象转换为类型化对象,再通过lifted.GetPodFromTemplate提取 Pod 模板,最终调用helper.GetDependenciesFromPodTemplate从 Pod 模板中解析出 ConfigMap、Secret、PVC、ServiceAccount 等依赖引用。也就是说,凡是"包含 Pod 模板"的工作负载,都能自动得到依赖清单

自定义解释器

对于内置解释器未覆盖的自定义资源(CRD),Karmada 提供两种自定义依赖解析方式:

  1. 声明式 Lua 脚本:通过ResourceInterpreterCustomization配置InterpretDependency的 Lua 脚本,由ConfigurableInterpreter.GetDependencies执行(见 pkg/resourceinterpreter/customized/declarative/configurable.go),底层由 Lua VM 调用脚本中的GetDependencies函数(见 pkg/resourceinterpreter/customized/declarative/luavm/lua.go);
  2. Webhook 解释器:通过CustomizedInterpreter.GetDependencies将请求转发给用户部署的 ResourceInterpreter Webhook(见 pkg/resourceinterpreter/customized/webhook/customized.go)。

内置解释器的默认分发入口在 pkg/resourceinterpreter/default/native/default.go 处,根据InterpretDependency操作分发到上述各类型解析函数。

实战示例:Deployment + ConfigMap 自动传播

下面完整复现提案中的示例。假设要创建一个名为myapp的 Deployment,它引用一个名为my-config的 ConfigMap 作为卷挂载。

第 1 步:创建 Deployment

apiVersion: apps/v1 kind: Deployment metadata: name: myapp labels: app: myapp spec: replicas: 1 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - image: nginx name: nginx volumeMounts: - name: configmap mountPath: "/configmap" volumes: - name: configmap configMap: name: my-config

第 2 步:创建 PropagationPolicy(开启 propagateDeps)

创建 PropagationPolicy 将 Deployment 传播到指定集群,关键点是设置propagateDeps: true,且无需把my-config写进resourceSelectors

apiVersion: policy.karmada.io/v1alpha1 kind: PropagationPolicy metadata: name: myapp-propagation spec: propagateDeps: true resourceSelectors: - apiVersion: apps/v1 kind: Deployment name: myapp placement: clusterAffinity: clusterNames: - member1 - member2 replicaScheduling: replicaSchedulingType: Duplicated

该配置使用Duplicated复制调度,将 Deployment 各副本复制到member1member2两个集群。

第 3 步:创建被引用的 ConfigMap

apiVersion: v1 kind: ConfigMap metadata: name: my-config data: nginx.properties: | proxy-connect-timeout: "10s" proxy-read-timeout: "10s" client-max-body-size: "2m"

第 4 步:观察自动生成的附属 ResourceBinding

my-config被创建后,Dependencies Distributor 会为它自动创建附属 ResourceBinding,其spec大致如下:

spec: requiredBy: - clusters: - name: member1 replicas: 1 - name: member2 replicas: 1 name: myapp-deployment namespace: default resource: apiVersion: v1 kind: ConfigMap name: my-config namespace: default resourceVersion: "757297"

可见:

  • requiredBy记录了引用者myapp-deployment(命名空间default)及其调度结果快照(member1member2各 1 副本);
  • resource指向实际依赖资源my-config
  • 附属绑定会据此把 ConfigMap 分发到与 Deployment 相同的member1member2集群,完成自动传播。

测试保障

该特性的正确性由大量单元测试保障,测试文件 pkg/dependenciesdistributor/dependencies_distributor_test.go 覆盖了:

  • 事件处理(Test_OnUpdate)、资源模板协调(Test_reconcileResourceTemplate);
  • 依赖匹配(Test_matchesWithBindingDependencies);
  • finalizer 添加与移除(Test_addFinalizerTest_removeFinalizer);
  • 独立绑定删除清理(Test_handleIndependentBindingDeletion);
  • 孤儿附属绑定清理(Test_removeOrphanAttachedBindingsTest_findOrphanAttachedBindings);
  • 附属绑定创建/更新与快照合并(Test_createOrUpdateAttachedBindingTest_mergeBindingSnapshotTest_deleteBindingFromSnapshotTest_buildAttachedBinding);
  • 策略冲突检测与消解(Test_detectAndResolveConflictResolutionTest_detectAndResolvePreserveOnDeletionTest_createOrUpdateAttachedBinding_emitsConflictEvent);
  • 调度结果同步(Test_syncScheduleResultToAttachedBindings_doesNotWaitForInformerCacheSync)等。

此外,提案中的 Test Plan 还明确了对应的 E2E 用例方向:验证依赖资源是否被传播到所需集群。对应 E2E 用例位于 test/e2e/suites 目录,可用于在真实多集群环境中回归验证本特性。

使用建议与注意事项

  1. 开启方式:确认karmada-controller-manager已启用PropagateDeps特性门控(当前仓库版本默认开启);创建 PropagationPolicy 时设置spec.propagateDeps: true
  2. 不要重复声明:开启自动传播后,被引用的 ConfigMap/Secret 无需再写入resourceSelectors,否则可能出现重复传播;官方也建议弃用早期的association字段。
  3. 解析范围限制:依赖解析基于原始资源模板,由 OverridePolicy 注入的依赖(例如在 override 中动态添加的 ConfigMap 引用)无法被识别。
  4. 资源共享:每个依赖资源拥有独立的附属绑定,同一个 Secret 可被多个 Deployment 共享;多个引用者的策略字段(ConflictResolutionPreserveResourcesOnDeletion)不一致时,控制器按"任一优先"规则消解并告警。
  5. 生命周期联动:删除独立绑定时,其RequiredBy快照会从附属绑定中移除;当最后一个引用者消失时,附属绑定上的策略字段被清空,后续由资源绑定控制器按常规流程回收。
  6. 自定义资源:如果你的 CRD 也需要自动传播依赖,可通过ResourceInterpreterCustomization(Lua 脚本)或 ResourceInterpreter Webhook 实现InterpretDependency操作。

总结

Karmada 的依赖自动传播(PropagateDeps)把"工作负载 + 周边依赖资源"作为一个整体进行多集群编排:通过PropagationPolicyResourceBinding的 API 扩展、Dependencies Distributor 控制器、以及 ResourceInterpreter 的InterpretDependency操作三层协作,实现了依赖资源的自动识别、自动跟随调度与故障转移迁移。它既免去了手工维护依赖资源目标集群的负担,又通过"一资源一附属绑定 + RequiredBy 快照"的设计保持了资源共享的灵活性,是多集群应用编排中极具实用价值的内置能力。

【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询