Kubernetes 带来的云原生技术革命已席卷全球,越来越多的业务应用运行在 Kubernetes 平台上,随着业务规模的扩展,集群数量与日俱增,多集群内部资源管理和检索越来越复杂,多云管理面临巨大挑战。在多集群时代,我们可以通过 Cluster-api 来批量创建管理集群,使用 Karmada/Clusternet 来分发部署应用。不过我们貌似还是缺少了什么功能,我们要如何去统一地查看多个集群中的资源呢?
Clusterpedia,名字借鉴自 Wikipedia,同样也展现了 Clusterpedia 的核心理念 —— 多集群的百科全书。
通过聚合多集群资源,在兼容 Kubernetes OpenAPI 的基础上额外提供了更加强大的检索功能,让用户更快更方便地在多集群中获取到想要的任何资源。
01
Kube-APIServer:API 服务器是 Kubernetes 控制平面的组件, 该组件负责公开了 Kubernetes API,负责处理接受请求的工作,API 服务器是 Kubernetes 控制平面的前端。
KubernetesOpenAPI: 外部第三方系统通过该接口可以与 Kubernetes 进行交互。
Aggregated APIServer: 是为了将原来的 APIServer 这个应用给拆分,为了方便用户开发自己的 APIServer 集成进来,而不用直接修改 Kubernetes 官方仓库的代码,这样一来也能将 APIServer 解耦,方便用户使用实验特性。
CustomResourceDefinition: 通过定制化的代码给你的 Kubernetes API 服务器增加资源对象,而无需编译完整的定制 API 服务器。
GVK/GVR: 当我们在一个特定的群组版本 (Group-Version) 中提到一个 Kind 时,我们会把它称为 GroupVersionKind,简称 GVK。与 资源 (resources) 和 GVR 一样,每个 GVK 对应 Golang 代码中的 Go type。
PediaCluster:每个集群对应一个 PediaCluster,具体指代该集群的认证信息以及需要同步缓存的资源信息。
Clusterpedia APIServer: 以 Aggregated API 的方式注册到 Kube APIServer,通过统一的入口来提供服务。
Cluster Synchro:每个集群会对应一个同步资源对象,收集需要同步与缓存的数据信息。
ClusterSynchro Manager: 在部署的控制面层进行 Cluster Synchro 实例的管理。
Karmada: 全称 Kubernetes Armada,是一个 Kubernetes 管理系统,可让您跨多个 Kubernetes 集群和云运行您的云原生应用程序,而无需更改应用程序;通过使用 Kubernetes 原生 API 并提供高级调度功能,Karmada 可以实现真正的开放式多云 Kubernetes。
0.4.0 后,Clusterpedia 提供了更加友好地接入多云平台的方式。用户通过创建 ClusterImportPolicy 来自动发现多云平台中纳管的集群,并将这些集群自动同步为 PediaCluster,用户不需要根据纳管的集群来手动去维护 PediaCluster 了。
在下面业务模型介绍时候,会详细介绍该功能。
Clusterpedia 不仅仅支持多集群的聚合查询,也可以指定某一个集群进行查询,如下图所示,我们接入了 arm 和 x86 两套 k8s 集群。
可以指定 x86 集群进行 po 的检索。
原生 Kubernetes 当前只支持对 metadata.name 和 metadata.namespace 的字段过滤,而且操作符只支持 =,!=,==,能力非常有限。
Clusterpedia 在兼容已有的 field selector 功能的基础上,提供了更加强大的功能,支持和 label selector 相同的操作符,如下检索:
也可以支持直接拼接 sql 语句查询:
kubectl get --raw="/apis/clusterpedia.io/v1beta1/resources/apis/apps/v1/deployments?whereSQL=(cluster='arm-cluster-example') OR (namespace IN ('kube-system','default'))"
也支持查询 pod 的创建时间区间。
4. 多集群检索
多个集群资源一起搜索也是可以的, 如下展示:
5. 特性功能 FeatureGates
用户在使用特性功能时,需要开启相应的特性开关。
Clusterpedia APIServer 和 Clusterpedia ClusterSynchro Manager 分别具有不同的特性功能。
如下图开启特性所示:
APIServer 支持:RemainingItemCount,AllowRawSQLQuery,默认都不开启。
ClusterSynchro Manager 支持:PruneManagedFields(默认开启),PruneLastAppliedConfiguration(默认开启),AllowSyncCustomResources,AllowSyncAllResources。
它是 Kubernetes 之上的一个增强资源分页功能。在 Kubernetes 分页的基础上,Clusterpedia 复用 ListOptions.Limit 和 ListOptions.Continue 字段作为分页的 size 和 offset,但为了避免 kubectl 进行对全量数据的分片拉取,Clusterepdia 不会在响应中携带 Continue 信息。
在一些 UI 场景下,往往会需要获取到当前检索条件下的资源总量,所以打开 RemainingItemCount 功能即可获得资源分页时候的总数与剩余数量,由于存储层返回剩余资源数量会导致额外的 COUNT 查询,对性能有一定影响,所以该功能默认是关闭的。
该特性专属于 Clusterpedia APIServer,可以在资源查询时候直接使用原生的 sql 语句。
不同的用户的需求可能是不同的,尽管 Clusterpedia 提供了很多简便的检索条件,例如指定一组命名空间或者资源名称,也可以指定 owner 进行查询,但是用户依然可能会有更加复杂的查询。
URL="/apis/clusterpedia.io/v1beta1/resources/apis/apps/v1/deployments"
kubectl get --raw="$URL?whereSQL=(cluster='global') OR (namespace IN ('kube-system','default'))"
示例中,我们传递一个用于 WHERE 查询的 SQL 语句 ,(cluster=‘global’) OR (namespace IN (‘kube-system’,‘default’)),
这个语句会检索 global 集群内所有命名空间下以及其他集群中 kube-system 和 default 命名空间下的 deployments。
资源的 Metadata 中有一些字段在实际搜索中通常没有太大用处,所以在同步时我们会默认裁剪这些字段。
我们使用该特性来分别控制这些字段是否在资源同步时被裁剪,这些特性专属于 Clustersynchro Manager 组件,且默认开启
自定义资源不同于 Kubernetes 的内置资源,Kubernetes 的内置资源通常不会经常性变动,而自定义资源的类型可能会被动态的创建和删除。
用户如果想要根据被纳管集群中的 CRD 的变动来自动调节同步的资源类型,那么用户在 PediaCluster 中指定同步所有自定义资源。
该功能可能会导致大量的长连接,所以需要在 Clustersynchro Manager 中开启该 feature。
用户可以通过全资源通配符来同步所有类型的资源,当被纳管集群中发生任意的资源类型变动(例如 Kubernetes 版本升级,group/version 被禁用,CRD 或者 APIService 变动)都会导致同步的资源类型发生修改。
该功能会创建大量的长连接,建议指定具体的资源类型,如果需要对自定义资源进行动态同步,可以使用同步所有的自定义资源(AllowSyncAllCustomResources)。
自定义资源类型 CRD,PediaCluster 有两个作用: 配置集群的认证信息 , 配置同步的资源。
通过上面的生成对象,可接入 APIServer 对应配置到 Clusterpedia 里面进行资源检索与同步。
有三个字段可以配置同步的资源:
spec.syncResources 配置该集群需要同步的资源。
spec.syncAllCustomResources 同步所有的自定义资源。
spec.syncResourcesRefName 引用公共的集群资源同步配置。
也可配合下面的 ClusterImportPolicy 生成 PediaClusterLifecycle,进行自动的集群发现与管理。
自定义资源类型 CRD,提供了公共的集群资源同步配置
spec.syncResources 字段和 PediaCluster 的 spec.syncResources 配置方式相同,可参考 PediaCluster 使用方式。
当我们修改 ClusterSyncResources 时,所有引用它的 PediaCluster 内同步的资源都会发生相应的修改。
Clusterpedia 的默认存储层支持 MySQL 和 PostgreSQL 两种存储组件。
用户在安装 Clusterpedia 时,可以使用已存在的存储组件,不过需要创建相应的默认存储层配置 ConfigMap 和存储组件密码 Secret。
Clusterpedia 的安装 yaml 会从 internalstroage-password 的 Secret 中获取密码。
将存储组件的密码配置到 Secret 中
通过上图,大概描述下,ClusterImportPolicy,PediaClusterLifecycle,PediaCluster , Cluster 的关系。
(1) 创建一个 ClusterImportPolicy 对象,可以通过 spec 里面的 source/references 等关键字找到 Karmada 集群
(2) Karmada 认证信息打通后,并返回给策略 ClusterImportPolicy
(3) 接下来会根据 ClusterImportPolicy 配置的 PediaClusterLifecycle 创建时机,当条件满足时候,会创建 PediaClusterLifecycle,
(4) 一个 PediaClusterLifecycle 会对应一个 PediaCluster , 当某个集群加入 Karmada 的控制面时候,会传递事件过来,PediaClusterLifecycle 监听到后同步创建 PediaCluster
(5) 每个 PediaCluster 会对应具体的一个集群, 会把里面的具体配置的资源进行缓存同步,以便检索时候快速搜索。
下面,我们会举一个例子,当我们通过该动态策略,接入已有的 Karmada 集群。
如下是我提前准备的 karmada 集群,接入了 member1 和 member2 集群
下发测试资源进行验证。
下面会在该 Karmada 集群进行 Clusterpedia 的部署。
部署成功, 下发资源 ClusterImportPolicy 前集群状态。
下发资源后集群状态。
测试下跨集群的访问。
上面大概演示了下, 在 Karmada 平面集成 Clusterpedia 的情况,下面说明下各字段的具体含义,
spec.source 和 spec.references 分别定义 Source 资源和 References 资源。
什么叫 Source 资源和 References 资源呢?因为该特性是为了可以动态接入集群。Clusterpedia 需要接入集群的话,要先定义 PediaCluster,配置每个集群的认证信息, 而通过该特性,现在可以定义一个 ClusterImportPolicy ,这个里面包含的 Source 就是该多云平台的来源认证信息,而 References 资源是额外被引用的资源信息,比如 Karmada 集群接入,还需要 secrets 等信息等,这时候该 secrets 信息就可以放入 References 进行定义。
spec.nameTemplate 定义生成的 PediaClusterLifecycle 和 PediaCluster 的名称,可以根据 Source 资源进行渲染。
spec.template 和 spec.creationCondition 定义资源模版创建,以及创建的条件。
当一个 Source 被创建后,ClusterImportPolicy Controller 会根据 ClusterImportPolicy 创建相应的 PediaClusterLifecycle 资源。
它是和 ClusterImportPolicy 相关的资源,通过上面介绍 ClusterImportPolicy 时候,我们可以知道该 CRD 的大概功能。
下面我们具体详细的描述下该资源信息:
通过上面定义的 ClusterImportPolicy 进行内部转化生成,PediaClusterLifecycle 资源的名字是通过 ClusterImportPolicy 的 spec.nameTemplate 字段来设置的。
首先需要在 ClusterImportPolicy 资源中定义监听(想要转换的)的资源类型,我们将被监听的资源称为 Source 资源。
PediaClusterLifecycle 会根据具体的 Source 资源的变动情况,来进行相应 PediaCluster 的创建/更新/删除等动作。
对于 ClusterImportPolicy 和 PediaClusterLifecycle 的调协由 Clusterpedia Controller Manager 内的 ClusterImportPolicy Controller 和 PediaClusterLifecycle Controller 负责。
如图所示,Clusterpedia 主要有两个组件:
ClusterSynchroManager :管理主集群内的 PediaCluster 资源,通过 PediaCluster 配置认证信息连接到指定集群,并且实时同步相应的资源。
APIServer:同样会监听主集群内的 PediaCluster 资源,并根据集群同步的资源以兼容 Kubernetes OpenAPI 的方式来提供对资源的复杂检索,Clusterpedia APIServer 会以聚合式 API 的方式注册到主集群的 APIServer 中, 这样我们通过和主集群相同的入口便可访问 Clusterpedia。
在较新的版本中,引入了 ControllerManager 控制器,主要为了实现外部集群的动态发现与注册:包括了两个 Controller ,分别对于 ClusterImportPolicy,PediaClusterLifecycle,PediaCluster 资源对象的调协作用。
Clusterpedia-APIServer 大概逻辑是以 Aggregated API 的方式注册到 Kube APIServer,通过统一的入口来提供服务。
部署程序时候,会注册 APIService,通过扩展 Aggregated APIServer 接口进行程序开发。
主要负责是接受外部访问请求,通过调用 Storage 接口,从数据库返回用户需要的数据。
内部通过 RESTStorage 实现了 k8s.io/apiserver/pkg/registry/rest/rest.go 的 Lister 和 Getter 接口,在用户通过 API 访问到该接口时候,通过内嵌的 Storage 的接口从数据库获取数据,加快了返回与检索速度
如下图描述内部的逻辑流向:
(1) cmd 启动时候,初始化
(2) genericeServer 扩展接口的代理实现体
(3) 注册 GenericeServer
(4) 绑定对应的资源/版本到 generice 实现接口里面
ClustersynchroManager 大概逻辑如下:
(1) 启动入口,配置启动相应的参数以及初始化 Storage 存储接口
(2) 初始化 Kubernetes ClientSet 接口,ClusterSynchro Crd ClientSet 接口和对应的 Informer Factory,并启动队列进行事件的监听与派发
(3) 启动多个协程,进行队列的逻辑处理
(4) 启动 Informer 进行事件的捕获与本地队列存储
(5) 通过 informer 机制,监听集群资源 PediaCluster CR 的增删改事件,并进入队列进行 Reconcile 调和,通过 PediaCluster CR Spec 里面定义的 syncResources 字段信息,以及监听到 PediaCluster 变化,加入事件队列,缓存并更新到 DB 进行存储
(6) 首先判断该 PediaCluster 是否被打上了 delete 标记时候,如果是:会进行集群的清理动作,包括停止该集群的监听与缓存程序,同时会清除 DB 数据库表 Resource 存储的该集群 cluster 相关的记录;然后判断是否有 finalizer 字段,如果有的话则删除 PediaCluster 资源的 finalizer 字段;没有被标记删除事件,会判断该 PediaCluster 是否有 finalizer 字段,没有的话要给该 PediaCluster 加上 finalizer 字段;通过 PediaCluster 存储的集群信息,创建访问 Kubernetes 集群的 config 对象。
(7) 这时候会分两步进行判断,先看全局缓存中,是否该 PediaCluster 已经在运行了,如果有了缓存并且配置文件没有变化,则使用缓存的对象 ;如果没有缓存,或者配置文件发生了变化,则删除缓存数据,重新创建一个 ClusterSynchro 并运行主 Run 方法。
(8) 通过 ClusterSynchro 的 SetResource 方法,设置具体需要同步的资源。
(1) 初始化 ClusterSynchro,每一个 PediaCluster 对象,会生成一个对应的 ClusterSynchro,通过 Informer 缓存 PediaCluster 定义的 SyncResources 资源组,存储进 DB 数据库供外部访问使用。
(2) dynamicDiscoveryManager 会通过 startAggregatorResourceWatcher,startServerVersionWatcher 监控资源 GVR 和缓存的比对情况, 或者集群是否更新,比如版本的升级等。如有变动,触发事件 chan 通知 resetSyncResources 函数运行,让资源再次开始同步动作。
(3) 分别启动 crdController,apiServiceController 进行资源变动的监听,负责通过 chan 事件进行资源的缓存与同步 DB 操作。
(4) 分别启动 monitor 协程,通过定时器访问 Kubernetes 集群的/readyz 或者/healthz 接口,检测集群是否健康,根据集群当前的健康状态,自动启停资源收集;通过 monitor 健康检测的结果,启动和停止资源的收集与缓存功能。
(5) 集群状态更新,如果有更新事件,则会更新集群的 status 状态
(6) 触发资源同步的 chan。
(7) 资源同步 chan,等待信号量,收到时候,即进入 setSyncResources()处理缓存的同步。
(8) 缓存变更逻辑时候,会通过 ResourceNegotiator 判断是否需要更新资源情况,根据以前缓存的信息与当前集群信息进行比对,判断是否更新 DB 数据。
(9) 通过 PediaCluster 定义的需要缓存的 GVR 等信息,分类通过 ResourceSynchro 进行检测以及更新,它会通过 Storage 接口访问 DB 层,把集群的对应 GVR 数据与 DB 进行同步。
Clusterpedia-controller-managerr 大概逻辑如下:
(1) 启动入口,参数初始化,运行环境初始化;
(2) 内部运行函数,会进行一系列的初始化工作;
(3) 初始化 clusterimportpolicy controller;
(4) clusterimportpolicy controller 内部监听的资源以及变动更新回调函数;
(5) 初始化 lifecycle controller;
(6) lifecycle controller 内部监听的资源以及变动更新回调函数;
(7) 启动 controller。
当前多云已经越来越流行,跨集群检索能力也受到了很高的重视,以前的单集群 Informer 机制已经不能满足检索要求。
目前社区里面有 Karmada 自身,在 1.2 版本引入的 karmada-search 组件进行资源检索能力,通过提交 ResourceRegistry 资源对象在 Karmada-apiserver 控制平面注册,便可使用 search API 进行检索。
另一方面 ClusterPedia 社区也在蓬勃的发展中,ClusterPedia 在检索上最大的优势就是除了可以使用 Kubectl 操作,以及很多复杂条件(模糊搜索,字段过滤,列表字段支持,根据父辈以及祖辈 Owner 查询,分页与排序等)以外,还能够纳管版本 1.10 到 1.24 的集群,而且即使这些集群内的资源版本不同,我们也可以使用统一的资源版本来检索,目前 0.4 以后的版本,也增强了集群接入能力,可以和 Karmada、Kubevela、Cluster-api 等其他多云平台一起结合,自动转换集群信息进入 ClusterPedia 控制面,用户只需要定义一个资源对象 ClusterImportPolicy。
本文作者
杨刚
现任「DaoCloud 道客」云原生资深研发工程师
参考文档:
https://clusterpedia.io/zh-cn/
https://github.com/clusterpedia-io/clusterpedia