news 2026/9/17 22:14:28

Karmada 成员集群资源缓存实战:基于 karmada-search 与 ResourceRegistry 构建统一跨集群资源视图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Karmada 成员集群资源缓存实战:基于 karmada-search 与 ResourceRegistry 构建统一跨集群资源视图

Karmada 成员集群资源缓存实战:基于 karmada-search 与 ResourceRegistry 构建统一跨集群资源视图

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

在 Karmada 多集群编排体系中,管理员往往需要在多个成员集群之间查询同一类资源(例如分布在 member1、member2、member3 上的 Pod 与 Deployment),传统做法需要反复切换 kubeconfig 上下文,既低效又缺乏全局视图。本文以 Karmada 官方设计文档《Caching member cluster resources for Karmada》为主体,系统讲解其核心组件karmada-search、缓存范围声明资源ResourceRegistrycacheopensearch两类后端存储,并结合仓库源码(pkg/search/controller.go、pkg/apis/search/v1alpha1/searchregistry_types.go 等)剖析其底层实现。读完本文,你将掌握:如何通过一份ResourceRegistry声明缓存范围、如何理解缓存数据从成员集群流入 Karmada 控制面并经search/proxyAPI 暴露的完整链路,以及该能力的安全边界。

背景与动机:多集群资源查询的痛点

在多集群场景下,管理员想要查询分布在多个集群中的资源时会遇到明显的阻碍:

  • 需要频繁切换集群上下文:查询每个集群都要切换 kubeconfig 的 current-context,操作繁琐;
  • 缺少全局资源视图:无法一次性看到同一资源在多个集群中的分布情况;
  • 查询效率低:跨区域访问成员集群 API Server 的网络延迟不可控,请求处理速度受限;
  • 无法按标签跨集群过滤:原生的单集群查询能力无法实现"在多个集群中按 labels 获取资源"这类需求。

设计文档提出的解决方案是:为 Karmada 增加一个缓存层(caching layer),将成员集群中指定范围的 Kubernetes 资源缓存到 Karmada 控制面,管理员通过统一入口跨集群高效查询资源,所有查询结果均来自缓存,不再直接穿透到成员集群。对应地,cmd/karmada-search/app/karmada-search.go 中对该组件的定位描述是:"The karmada-search starts an aggregated server. It provides capabilities such as global search and resource proxy in a multi-cloud environment."

Goals(目标)

设计文档明确了该缓存层的五个目标:

  • 加速跨区域资源请求的处理速度:查询请求在控制面本地缓存命中,避免跨区域往返成员集群;
  • 提供跨集群资源视图:一次请求即可聚合多个集群中的资源;
  • 兼容多集群的多种 Kubernetes 资源版本:支持不同成员集群存在不同资源 API 版本的场景;
  • 统一资源请求入口:所有集群的资源查询都收敛到 Karmada 控制面的统一 API;
  • 降低成员集群 API Server 压力:缓存层承担读流量,成员集群的 API Server 不再直接面对大量查询请求。

Non-Goals

设计文档中 Non-Goals 部分未展开具体条目,但结合后续 "Risks and Mitigations" 可以明确其边界:该功能面向的是管理员而非终端用户,其定位是查询与视图能力,而非面向最终用户的通用搜索门户。

总体设计:karmada-search 组件与 search.karmada.io API 组

设计文档提出新增一个名为karmada-search的组件,它提供新的 API 组search.karmada.io。选择 "search" 命名的原因,文档提到是受 OpenSearch(社区驱动的开源搜索与分析套件)的启发,强调其"搜索与分析"的定位。

karmada-search组件当前支持两类后端存储(backend store):cacheopensearch,其中cache类型被用作 Karmada 缓存层的默认后端存储。缓存层本身可以是本地内存,也可以是外部数据库(如 OpenSearch)。

karmada-search作为一个聚合 API Server 运行,pkg/search/apiserver.go 展示了它在search.karmada.ioAPI 组下注册的 REST 资源:

  • resourceregistriesresourceregistries/statusResourceRegistry资源及其状态子资源;
  • search:全局搜索入口(由Controller提供);
  • proxying:资源代理入口(由ProxyController提供),即文档提到的search/proxyREST API。

核心 API:ResourceRegistry——声明"缓存什么、缓存谁的"

设计文档引入了一个新的资源类型ResourceRegistry,它属于search.karmada.io组,是集群级(Cluster 级)资源。它的作用正如其类型注释所写:"ResourceRegistry represents the configuration of the cache scope, mainly describes which resources in which clusters should be cached."—— 即缓存范围的配置:需要用户在控制面中手动指定要缓存的集群和资源

该类型定义在 pkg/apis/search/v1alpha1/searchregistry_types.go,与设计文档中的定义保持一致:

package v1alpha1 // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ResourceRegistry represents the configuration of the cache scope, mainly describes which resources in // which clusters should be cached. type ResourceRegistry struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` // Spec represents the desired behavior of ResourceRegistry. Spec ResourceRegistrySpec `json:"spec,omitempty"` // Status represents the status of ResourceRegistry. // +optional Status ResourceRegistryStatus `json:"status,omitempty"` } // ResourceRegistrySpec defines the desired state of ResourceRegistry. type ResourceRegistrySpec struct { // TargetCluster specifies the clusters where the cache system collect resource from. // +required TargetCluster policyv1alpha1.ClusterAffinity `json:"targetCluster"` // ResourceSelectors specifies the resources type that should be cached by cache system. // +required ResourceSelectors []ResourceSelector `json:"resourceSelectors"` // BackendStore specifies the location where to store the cached items. // +optional BackendStore *BackendStoreConfig `json:"backendStore,omitempty"` } // ResourceSelector specifies the resources type and its scope. 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 all namespaces. // +optional Namespace string `json:"namespace,omitempty"` } // BackendStoreConfig specifies backend store. type BackendStoreConfig struct { // OpenSearch is a community-driven, open source search and analytics suite. // +optional OpenSearch *OpenSearchConfig `json:"openSearch,omitempty"` } // OpenSearchConfig holds the necessary configuration for client to access and config an OpenSearch server. type OpenSearchConfig struct { // Addresses is a list of node endpoint(e.g. 'https://localhost:9200') to use. // +required Addresses []string `json:"addresses"` // SecretRef represents the secret contains mandatory credentials to access the server. // The secret should hold credentials as follows: // - secret.data.userName // - secret.data.password // +required SecretRef clusterv1alpha1.LocalSecretReference `json:"secretRef"` // More configurations such as transport, index should be added from here. } // ResourceRegistryStatus defines the observed state of ResourceRegistry type ResourceRegistryStatus struct { // Conditions contain the different condition statuses. // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` }

关键字段说明

字段位置必填语义与默认行为
spec.targetClusterResourceRegistrySpec指定从哪些集群采集资源,类型复用策略 API 组的policyv1alpha1.ClusterAffinity,支持按clusterNames或标签选择器匹配集群
spec.resourceSelectorsResourceRegistrySpec指定需要缓存的资源类型列表,由ResourceSelector描述
spec.backendStoreResourceRegistrySpec指定缓存落盘位置;不配置时使用默认的cache(本地内存)后端
resourceSelector.apiVersionResourceSelector目标资源的 API 版本,如v1apps/v1
resourceSelector.kindResourceSelector目标资源的 Kind,如PodDeployment
resourceSelector.namespaceResourceSelector目标资源所在的命名空间;默认为空,表示所有命名空间
backendStore.openSearch.addressesOpenSearchConfigOpenSearch 节点端点列表,例如https://localhost:9200
backendStore.openSearch.secretRefOpenSearchConfig访问 OpenSearch 的凭据 Secret 引用,要求 Secret 中包含userNamepassword两个 data 键

值得一提的是,文档定义中SecretRef的 data 键名写作userName/password,而实际实现 pkg/search/backendstore/opensearch.go 中读取的是username/password两个键(string(secret.Data["username"])string(secret.Data["password"])),以源码实现为准。该实现还表现出较好的容错性:若 Secret 缺失或凭据为空,会告警并尝试以无认证方式连接 OpenSearch。

校验规则

pkg/apis/search/validation/validation.go 定义了ResourceRegistry的准入校验逻辑:

  • 名称须符合 DNS 子域名规范(NameIsDNSSubdomain);
  • targetCluster复用ValidateClusterAffinity校验集群亲和性;
  • 每个ResourceSelectorapiVersion必须能通过schema.ParseGroupVersion解析,namespace必须是合法命名空间名;
  • openSearch.addresses中每个地址必须是带非空 scheme(且仅允许http/https)与非空 host 的合法 URL。

配置示例:创建 ResourceRegistry CR

设计文档给出了一个完整的示例。创建下面的ResourceRegistry后,Karmada 会spec.resourceSelectors中定义的资源执行 list/watch,并将实时清单缓存到缓存层(本地内存或数据库),后续所有查询结果均来自缓存

apiVersion: search.karmada.io/v1alpha1 kind: ResourceRegistry metadata: name: clustercache-sample spec: targetCluster: clusterNames: - member1 - member2 - member3 resourceSelectors: - kind: Pod apiVersion: v1 - kind: Ingress apiVersion: networking.k8s.io/v1 - kind: DaemonSet apiVersion: apps/v1 namespace: kube-system - kind: Deployment apiVersion: apps/v1

该示例演示了三个要点:

  1. 多集群目标targetCluster.clusterNames一次性声明 member1、member2、member3 三个集群;
  2. 多资源类型resourceSelectors可同时声明多种资源;PodIngressDeployment不限定命名空间(即缓存全部命名空间),而DaemonSet通过namespace: kube-system限定只缓存kube-system命名空间下的对象;
  3. API 版本显式声明:每个 selector 都必须给出apiVersion,控制器会据此结合 RESTMapper 将apiVersion + kind解析为GroupVersionResource(见 pkg/search/controller.go 中getResourcesrestmapper.GetGroupVersionResource的调用)。

底层工作流:ResourceRegistry Controller 如何把成员集群资源搬进缓存

设计文档描述了"list/watch 并缓存"的宏观行为,具体实现由 pkg/search/controller.go 中的Controller完成。它监听ClusterResourceRegistry两类对象的事件,通过 workqueue 驱动doCacheCluster完成缓存编排,核心步骤可概括为:

  1. 集群可用性检查clusterAbleToCache):集群不存在、正在删除(DeletionTimestamp非空)或状态不 Ready 时,停止该集群的 informer,不进行缓存;
  2. 计算注册关系reconcileClusterWithRegistries):列出全部ResourceRegistry,用util.ClusterMatches判断哪些注册表与该集群匹配,并对比集群当前已注册的注册表集合,算出新增/移除的注册表以及新增/移除的待监听资源(addedResources/removedResources);
  3. 卸载无引用集群(STEP1):若集群已不被任何ResourceRegistry引用(cr.unregistry()),停止该集群的 informer 管理器;
  4. 重建 informer(STEP2):当注册表或监听资源集合发生变化时,停止旧 informer 并为该集群构建新的多集群 informer 管理器(InformerManager.ForCluster),随后对每个待缓存资源的 GVR 注册sci.ForResource(gvr, handler)sci.Start()+WaitForCacheSync()
  5. API 兼容性检查:注册 informer 前会通过cls.APIEnablement(gvk)检查该资源在成员集群中是否启用,若APIDisabled则跳过并告警(支持成员集群资源版本/能力差异,呼应"兼容多种 Kubernetes 资源版本"的目标);
  6. 事件入缓存:每个资源的 informer 事件(Add/Update/Delete)被路由到对应后端存储的事件处理器(getRegistryBackendHandler)。

这里使用的InformerManager来自 pkg/util/fedinformer/genericmanager,是 Karmada 用于管理多个成员集群 informer 的通用多集群 informer 管理器。

后端存储:cache(默认本地内存)与 OpenSearch

设计文档说明karmada-search支持cacheopensearch两类后端。后端存储的统一抽象定义在 pkg/search/backendstore/store.go:

// BackendStore define BackendStore interface type BackendStore interface { ResourceEventHandlerFuncs() cache.ResourceEventHandler Close() }

backendstore.Init初始化全局后端管理器,AddBackend按集群注册后端:cfg == nilcfg.OpenSearch == nil,则使用默认后端NewDefaultBackend(cluster)——这正是文档所述"以 cache 类型作为默认后端存储"的落地实现。

cache 后端(默认)

pkg/search/backendstore/defaultstore.go 实现了默认后端:它为每个集群创建一组cache.ResourceEventHandlerFuncs,将 informer 的 Add/Update/Delete 事件中的*unstructured.Unstructured对象以日志(V(4) 级别)方式记录,并保留在 informer 内置的缓存中。也就是说,默认 cache 后端依托 informer 自带的本地内存索引存储资源实时清单,这与文档所述"cache(本地内存或数据库)"中的本地内存形态对应。

OpenSearch 后端

当在ResourceRegistry中显式配置spec.backendStore.openSearch时,控制器会为该集群创建 OpenSearch 后端(见 pkg/search/backendstore/opensearch.go)。其行为要点包括:

  • 索引命名:索引名为kubernetes-<kind 小写>defaultPrefix = "kubernetes"),首次写入某类资源时自动创建索引并应用内置 mapping;mapping 中将metadata.annotationslabelsspecstatus设为enabled: false(不参与分词检索,仅存储原始 JSON),而namenamespaceresourceVersion配置了 keyword 子字段以便精确过滤;
  • 写入策略:Add/Update 事件统一走upsert(以对象 UID 作为文档 ID 的 IndexRequest),Delete 事件按 UID 发起 DeleteRequest;实现中留有TODO: bulk upsert / bulk delete优化空间;
  • 来源标记:写入前会自动给对象注入clusterv1alpha1.CacheSourceAnnotationKey注解,值为集群名,用于标识缓存来源集群;
  • 认证:从secretRef指向的 Secret 读取凭据配置客户端,缺失时降级为无认证并告警。

需要说明的是,从源码结构看,当前 OpenSearch 后端仅负责把事件写入 OpenSearch 索引,而对外查询(Get/List/Watch)仍通过 pkg/search/proxy/store/multi_cluster_cache.go 的MultiClusterCache走内存缓存路径——设计文档中的 OpenSearch 更多扮演"分析型存储"的角色,其完整读写链路仍在演进中。

统一查询入口:search/proxy REST API 与 MultiClusterCache

文档在 "Risks and Mitigations" 中明确指出:被缓存的资源通过search/proxyREST API 暴露karmada-search的 API Server 在search.karmada.io组下注册了proxying资源(pkg/search/apiserver.go),并支持对proxying路径执行 watch/proxy 等长连接请求(见 cmd/karmada-search/app/karmada-search.go 的customLongRunningRequestCheck)。

从实现看,聚合查询的核心是MultiClusterCache(pkg/search/proxy/store/multi_cluster_cache.go),它实现了标准存储接口Store

  • Get:遍历各成员集群的 cluster cache,以ResourceVersion="0"强制走缓存读取;若对象在多个集群同时存在,会返回 Conflict("ambiguous objects in clusters [...]"),并在返回对象上通过addCacheSourceAnnotation标注来源集群、将resourceVersion改写为多集群资源版本;
  • List:按集群排序后逐个从各集群缓存拉取列表并合并;支持limit分页(multiClusterContinue记录"下一批从哪个集群、以什么 resourceVersion 继续"),并会为缺失 resourceVersion 的集群回填版本号(fillMissingClusterResourceVersion);
  • Watch:为每个集群建立 watch 并汇聚到 watch 多路复用器(watchMuxWithInvalidation),统一以多集群 resourceVersion 返回事件;当集群拓扑变化(新增集群或故障集群恢复)时,主动失效所有活跃 watch 连接以触发客户端重连,避免数据不一致(对应 issue #6963);
  • ReadinessCheck:在search-storage-cache-readinessPostStartHook 中等待所有注册集群的缓存就绪后才对外提供服务。

由此,管理员可经由统一入口完成跨集群的 get/list/watch,得到带cache-source来源注解与多集群 resourceVersion 的聚合结果。

安全风险与缓解(Risks and Mitigations)

设计文档明确列出了三项安全考量,这是使用该缓存能力时必须理解的边界:

  1. search/proxy的权限即缓存访问权限:该功能构建的缓存存有来自多个成员集群的任意资源,并经由search/proxyREST API 暴露。只要用户对search/proxy拥有访问权限,就可以直接读取缓存中的资源,而无须把请求路由到成员集群——这意味着成员集群侧的细粒度 RBAC 在该路径上不再生效。
  2. 可能绕过成员集群的 RBAC 限制:由于查询请求不会路由到成员集群,若某个 Secret 已被缓存到 Karmada 控制面,即使成员集群中的用户因 RBAC 限制无法通过成员集群 API Server 访问该 Secret,他仍可能通过 Karmada 控制面读到它。因此敏感资源(如 Secret)是否纳入缓存范围需要管理员审慎评估
  3. 面向管理员而非终端用户:该功能为需要在多集群间查询、查看资源的管理员设计,不应暴露给终端用户。若向终端用户开放此 API,可能导致用户查看到不属于自己的资源。

实践建议与源码索引

综合设计与实现,可以给出几条落地建议:

  • 按需声明缓存范围resourceSelectors尽量收敛,仅缓存确有全局查询诉求的资源类型,并通过namespace限定范围,控制控制面内存与 informer 开销;敏感资源(如v1/Secret)谨慎纳入。
  • 利用默认 cache 后端快速起步:不配置backendStore即可获得基于 informer 内存缓存的跨集群视图;只有需要索引、检索能力时才考虑配置 OpenSearch 后端。
  • 关注集群生命周期联动:集群删除或变为 NotReady 时,控制器会自动停止对应 informer 并清理后端存储,无需人工干预。

以下是本文涉及的关键源码与文档位置,便于继续深入:

  • 设计文档:docs/proposals/caching/README.md
  • 类型定义:pkg/apis/search/v1alpha1/searchregistry_types.go
  • 准入校验:pkg/apis/search/validation/validation.go
  • 缓存控制器:pkg/search/controller.go
  • 后端存储抽象与实现:pkg/search/backendstore/store.go、pkg/search/backendstore/defaultstore.go、pkg/search/backendstore/opensearch.go
  • API Server 注册:pkg/search/apiserver.go
  • 聚合查询存储:pkg/search/proxy/store/multi_cluster_cache.go
  • 组件入口:cmd/karmada-search/app/karmada-search.go

通过ResourceRegistry声明缓存范围、由karmada-search控制器完成跨集群 list/watch、再经统一search/proxy入口聚合暴露,Karmada 的成员集群资源缓存能力为多集群管理员提供了一条"统一入口 + 全局视图 + 本地化读取"的查询路径,同时也要求使用者在权限与敏感数据层面做好防护。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 22:11:02

TaoToken 写进容器 Secret,跑 Claude Docs

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 22:09:13

元初混沌体系 第四卷 太赫兹高频通信与超宽带频谱体系:第六十篇 跨运营商、跨星座频谱资源互通共享规则

第六十篇 跨运营商、跨星座频谱资源互通共享规则本篇单元定位本篇隶属第四卷太赫兹高频通信与超宽带频谱体系 第四单元超宽带频谱复用与智能频谱调度&#xff08;55–72&#xff09;第六篇全域互通规则篇&#xff0c;是鸿蒙智能频谱调度体系的全域破壁准则、跨主体协同法理、星…

作者头像 李华
网站建设 2026/9/17 22:08:37

智慧电厂数字化转型方案:从架构设计到预测性维护落地要点

简介&#xff1a;围绕发电企业向智慧化升级的核心诉求&#xff0c;这份52页PPT系统梳理了智慧电厂数字化转型的整体解决方案&#xff0c;适合发电集团、分&#xff08;子&#xff09;公司及电厂的信息化规划人员、技术管理者参考。内容从发电行业信息化历程切入&#xff0c;提出…

作者头像 李华
网站建设 2026/9/17 22:08:15

不等式常见考试题型总结:从分类讨论到基本不等式求最值

简介&#xff1a;这份《不等式常见考试题型总结》doc文档面向高中阶段、尤其高三复习不等式的学生与数学教师&#xff0c;聚焦高考中约占12%分值的核心考点&#xff0c;并兼顾理科证明不等式的综合要求。内容按题型梳理分式、根式、绝对值、含参及一元高次不等式的解法&#xf…

作者头像 李华
网站建设 2026/9/17 22:02:11

Zotero自定义PDF打开方式:从系统关联到脚本接管全攻略

在用 Zotero 管文献的过程中&#xff0c;百分之百会遇到一个特别磨人的问题——默认双击 PDF 附件&#xff0c;弹出的永远是 Zotero 自带那个阅读器。虽然内置阅读器做标注、做高亮、做笔记确实越来越顺手了&#xff0c;但总有些场景是它搞不定的&#xff1a;比如你想用福昕、A…

作者头像 李华