Kubernetes多集群管理之路

2023-02-14 00:00:00 集群 多个 状态 资源 联邦

随着Kubernetes在企业中的应用愈发广泛、普及,越来越多的公司开始在生产环境中运维多个Kubernetes集群。本文主要讲述了一些对于Kubernetes多集群管理的思考,包括为什么需要多集群、多集群的优势以及现有的一些基于Kubernetes衍生出的多集群管理架构。

作者:王琦, 中国移动云能力中心软件开发工程师,专注于云原生、Istio、微服务、Spring Cloud 等领域。

01

Kubernetes集群联邦


对于Kubernetes单集群,官方声称可以支持5000个节点和15万个Pod。但在实际的应用中,很少有公司会选择部署如此庞大的一个单集群。相反,可能更多的还是会选择部署多个集群。对于多集群的使用场景而言,如何对这些集群进行统一的管理,则是集群联邦(Federation)架构产生的原因。

1.1 为什么需要多集群?

l单集群负载能力有限:Kubernetesv1.8版本开始已经可以具备了上述5000节点、15Pod的负载能力。而衍生至今,新的v1.25版本在单集群承载能力方面并没有太大的变化,可想而知,社区并没有将单集群负载能力的提升作为其发展的重心。原因我想也很简单,如果实际的业务场景需要5000+节点,甚至更多,那么不管单集群的性能如何加强,在可扩展性方面终究还是存在固有的短板。

l多云(Multi-Clund)和混合云(Hybird Cloud)的实际业务场景:随着云原生技术的不断发展,以及为了避免独立供应商带来的锁定、成本问题,企业基于多云和混合云架构搭建多集群使用场景的方式愈发普遍。其实这不难理解,谁也不想只把鸡蛋放在同一个篮子里,生产环境需要具备高可用的能力似乎已经成为了业界既定的标准。

1.2 集群联邦概念

Kubernetes在设计之初并非是为了多集群的场景,而面对多集群分布式的使用需求,社区从v1.3版本开始着手设计,集群联邦的概念也应运而生。通过集群联邦可以实现对多个Kubernetes集群进行管理,其架构主要围绕以下两个模块来进行构建:

l跨集群资源同步:处于集群联邦中的所有集群具备彼此间保持资源同步的能力,即多个集群之间可以分布负载。比如,可以实现同一个Deployment资源被分发到多个或者全部联邦集群并同时部署服务。

l跨集群服务发现:集群联邦提供动态的DNS配置服务,并具有对所有联邦集群进行负载均衡的能力。比如,可以通过以一个DNS记录或者全局虚拟IP的方式访问多个或者全部的联邦集群。

以上两个模块的架构给予了集群更大范围的高可用能力,大限度的减少了集群故障带来的影响。同时,因具备了跨集群应用迁移的能力,可以使得集群的搭建不再局限于某一厂商,从根本上也杜绝了集群厂商锁定带来的影响。

1.3 集群联邦演进

集群联邦一直是Kubernetes社区非常重视的功能之一,Federatioin设计目的也是希望可以实现一种单一集群统一管理多个Kubernetes集群的机制,这些集群可以是跨区域的,也可以是不同云厂商的,甚至是用户本地自建的。只要他们加入到联邦集群中,就可以利用Federation API资源来统一管理多个集群的Kubernetes API资源,这带来了很多好处,比如:

l简化了对多个Kubernetes集群资源的管理,比如DeploymentService等。

l提升了应用服务的可靠性,应用的工作负载不再局限于某个单集群,而是可以均匀的分散在多个联邦集群中。并且具备了跨集群资源编排的能力,可以根据预先制定的编排策略将应用部署至联邦集群。

l实现了跨集群的服务发现,应用服务可以快速、便捷的在联邦集群中完成迁移。可以很好的适配多云、混合云的实际业务场景。


02

K8s Federation v1


我们必须知道的是,虽然Kubernetes社区很早就已经实现了Federation v1的机制,发布了相关组件和命令行工具(kubefed),并在v1.6版本时正式推动其进入Beta阶段。但在那之后,Federation v1发展便没有再进一步,关于原因,我会在接下来的部分进行说明。

2.1 Federation v1架构

 

1 Kubernetes Federation v1架构

从架构图可以看出,Federation v1版本与Kubernetes控制平面原生的架构相似,其主要包括四个组件:

lFederation API Server:提供统一的资源管理入口,功能类似Kubernetes API,但只允许使用 Adapter 拓展支持的Kubernetes API资源。

lFederation Controller Manager:提供多个联邦集群间的资源调度,协调不同集群之间的状态。

lKubefedFederation CLI集群管理工具,用于将一个Kubernetes集群Join/UnJoin到联邦集群中。

lEtcd:存储Federation相关的资源对象,用于Control Plane同步状态。

2.2 Kubefed集群管理

通过调用Federation API Server创建和维护所有联邦集群的资源,数据被持久化存储在Federation的ETCD中。需要注意的是,宏观上的中央管控集群只负责维护和规划整个联邦环境,而实际提供应用服务的应是加入进来的所有联邦集群(KubeFedCluster)。

2.2.1 Kubefed集群资源定义

apiVersion: core.kubefed.io/v1beta1kind: KubeFedClustermetadata:  creationTimestamp: "2022-11-14T11:34:44Z"  generation: 1  labels:    ... ...  name: bbf93fd3-921b-488a-a06b-ceff037cb923  namespace: kube-federation-system  resourceVersion: "115683914"  uid: 39950081-c5d1-4eeb-a69c-6892149142b2spec:  apiEndpoint: https://127.0.0.1:6443  caBundle: xxx  secretRef:    name: bbf93fd3-921b-488a-a06b-ceff037cb923-secretstatus:  conditions:  - lastProbeTime: "2022-11-14T21:21:38Z"    lastTransitionTime: "2022-11-14T11:34:53Z"    message: /healthz responded with ok    reason: ClusterReady    status: "True"    type: Ready

KubeFedCluster相关的结构体被定义在/pkg/apis/core/kubefedcluster_*.go里,具体内容如下:

type KubeFedClusterSpec struct {    APIEndpoint string `json:"apiEndpoint"`    CABundle []byte `json:"caBundle,omitempty"`    SecretRef LocalSecretReference `json:"secretRef"`    DisabledTLSValidations []TLSValidation `json:"disabledTLSValidations,omitempty"`} type KubeFedClusterStatus struct {    Conditions []ClusterCondition `json:"conditions"`    Zones []string `json:"zones,omitempty"`    Region *string `json:"region,omitempty"`} type ClusterCondition struct {    Type common.ClusterConditionType `json:"type"`    Status apiv1.ConditionStatus `json:"status"`    LastProbeTime metav1.Time `json:"lastProbeTime"`    LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`    Reason *string `json:"reason,omitempty"`Message *string `json:"message,omitempty"`}

其中,common.ClusterConditionType表示联邦集群和中心管控集群的链接状态,并通过LastProbeTime记录近一次的探针检查时间。


2.2.2 Kubefed集群Client

Kubefed通过集群Client监听不同集群的状态,并以此实现联邦集群的信息同步,相关结构体定义在/pkg/controller/kubefedcluser/clusterclient.go里,具体如下:

type ClusterClient struct {    kubeClient  *kubeclientset.Clientset    clusterName string}

简单来说,只是在一个用于集群访问的ClientSet基础上添加了一个集群名称用于标记具体的联邦集群,而在ClusterClient初始化时则会添加操作用于获取联邦集群更多的信息。

func NewClusterClientSet (c *fedv1b1.KubeFedCluster, client generic.Client, fedNamespace string, timeout time.Duration) (*ClusterClient, error) {    clusterConfig, err := util.BuildClusterConfig(c, client, fedNamespace)    if err != nil {        return nil, err    }    clusterConfig.Timeout = timeout    var clusterClientSet = ClusterClient{clusterName: c.Name}    if clusterConfig != nil {        clusterClientSet.kubeClient = kubeclientset.NewForConfigOrDie((restclient.AddUserAgent(clusterConfig, UserAgentName)))        if clusterClientSet.kubeClient == nil {            return nil, nil        }    }    return &clusterClientSet, nil}


其中,BuildClusterConfig用于从上述KubeFedCluster资源中获取相关的配置参数,并终生成一个可以访问联邦集群的REST Client。而对于ClusterClient如何获取集群的状态信息,Kubefed也给出了相关的定义和方法,具体如下。

func (self *ClusterClient) GetClusterHealthStatus() (*fedv1b1.KubeFedClusterStatus, error) {    body, err := self.kubeClient.DiscoveryClient.RESTClient().Get().AbsPath("/healthz").Do().Raw()    if err != nil {        runtime.HandleError(errors.Wrapf(err, "Failed to do cluster health check for cluster %q", self.clusterName))        clusterStatus.Conditions = append(clusterStatus.Conditions, newClusterOfflineCondition)        metrics.RegisterKubefedClusterTotal(metrics.ClusterOffline, self.clusterName)    } else {        if !strings.EqualFold(string(body), "ok") {            metrics.RegisterKubefedClusterTotal(metrics.ClusterNotReady, self.clusterName)            clusterStatus.Conditions = append(clusterStatus.Conditions, newClusterNotReadyCondition, newClusterNotOfflineCondition)        } else {            metrics.RegisterKubefedClusterTotal(metrics.ClusterReady, self.clusterName)            clusterStatus.Conditions = append(clusterStatus.Conditions, newClusterReadyCondition)        }    }    return &clusterStatus, err}

可以看到,Kubefed先是通过上述的REST Client获取联邦集群的健康状态,再根据获取到的状态信息回写入集群Condition中。

2.2.3 Kubefed集群控制器

Kubefed/pkg/controller/kubefedcluser/controller.go中定义了集群数据的结构体ClusterData,用于存放联邦集群客户端、集群状态等变量。而ClusterController则是重要的控制器结构体,其内含了一个基于Cache Controller的集群控制器用于注册事件回调,并增加了clusterDataMapfedNamespace用于存储多个节点的集群状态信息和记录Kubefed位于中心管控集群的具体命名空间,具体如下。

type ClusterData struct {    clusterKubeClient *ClusterClient    clusterStatus *fedv1b1.KubeFedClusterStatus    resultRun int64cachedObj *fedv1b1.KubeFedCluster} type ClusterController struct {    client genericclient.Client    clusterHealthCheckConfig *util.ClusterHealthCheckConfig    mu sync.RWMutex    clusterDataMap map[string]*ClusterData    clusterController cache.Controller    fedNamespace string    eventRecorder record.EventRecorder}

其中,clusterHealthCheckConfig是一个健康检测的配置项,可以用于设置控制器的检测周期、连接数以及超时时间。接下来看一下具体创建控制器的函数逻辑,代码如下。

func newClusterController(config *util.ControllerConfig, clusterHealthCheckConfig *util.ClusterHealthCheckConfig) (*ClusterController, error) {   ... ...   kubeConfig := restclient.CopyConfig(config.KubeConfig)   cc := &ClusterController{      client:                   client,      clusterHealthCheckConfig: clusterHealthCheckConfig,      clusterDataMap:           make(map[string]*ClusterData),      fedNamespace:             config.KubeFedNamespace,   }   ... ...   kubeClient := kubeclient.NewForConfigOrDie(kubeConfig)    var err error   _, cc.clusterController, err = util.NewGenericInformerWithEventHandler(      config.KubeConfig,      config.KubeFedNamespace,      &fedv1b1.KubeFedCluster{},      util.NoResyncPeriod,      &cache.ResourceEventHandlerFuncs{         DeleteFunc: func(obj interface{}) {            castObj, ok := obj.(*fedv1b1.KubeFedCluster)            if !ok {               tombstone, ok := obj.(cache.DeletedFinalStateUnknown)               if !ok {                  klog.Errorf("Couldn't get object from tombstone %#v", obj)                  return               }               castObj, ok = tombstone.Obj.(*fedv1b1.KubeFedCluster)               if !ok {                  klog.Errorf("Tombstone contained object that is not expected %#v", obj)                  return               }            }            cc.delFromClusterSet(castObj)         },         AddFunc: func(obj interface{}) {            castObj := obj.(*fedv1b1.KubeFedCluster)            cc.addToClusterSet(castObj)         },         UpdateFunc: func(oldObj, newObj interface{}) {            var clusterChanged bool            cluster := newObj.(*fedv1b1.KubeFedCluster)            cc.mu.Lock()            clusterData, ok := cc.clusterDataMap[cluster.Name]            if !ok || !equality.Semantic.DeepEqual(clusterData.cachedObj.Spec, cluster.Spec) ||               !equality.Semantic.DeepEqual(clusterData.cachedObj.ObjectMeta.Annotations, cluster.ObjectMeta.Annotations) ||               !equality.Semantic.DeepEqual(clusterData.cachedObj.ObjectMeta.Labels, cluster.ObjectMeta.Labels) {               clusterChanged = true            }            cc.mu.Unlock()            if !clusterChanged {               return            }            cc.delFromClusterSet(cluster)            cc.addToClusterSet(cluster)         },      },   )   return cc, err}


可以看出,这是一个近乎原生Controller。首先创建了一个Informer用于监听KubeFedCluster资源的变化,并定义了监听资源变化的处理方法。通过go断言对资源对象进行处理,再执行相应的增删改操作。其中,更新操作需要比对缓存数据和更新数据是否一致若一,则新版本与缓存版本并无变化,无更新,反之,则执行更新操作。操作的方式也很简单,直接删除旧的数据,再创建新的版本。

2.3 为什么v1被弃用?

通过上述,我们了解了Kubefed是如何实现多集群管理,并能监听联邦集群的事件变化的。而至于Federation v1版本为什么会在v1.11版本被正式弃用,根本的原因还是很多问题在v1架构设计的时候没有考虑的很全面。而且,随着Kubernetes身不断的发展,v1对其的兼容性也变得越来越差,比如:

l控制平面的组件可能会出现问题,而这会直接影响整个联邦集群的工作效率。

l初的设计里重用了Kubernetes API,且没有办法兼容新的Kubernetes API资源。

l不支持RBAC等权限校验,无法有效的对多个集群进行细粒度的权限管控。没有考虑到使用CRD功能,在定制化资源方面不具备可扩展性。

l联邦集群层级的设定和策略依赖Annotations的内容,可扩展性较差。

而从代码层面,Federation v1API Server是基于k8s.io/apiserver的基础上进行开发的,采用了AAAPI Aggregation)的方式对Kubernetes原生API进行扩充,终的Federation API在前文已经提到,是以Adapter的形式管理Kubernetes API资源的,这些Adapter在底层写死了API的版本,比如说Deployment资源只支持extensions/v1beta1版本的API,此时想要创建一个apps/v1版本的Deployment,并在Annotations内设置联邦策略,就会使得Deployment资源无法正常运作。因此,如果想要支持其他资源或版本,就必须在Federation *中新增与之相匹配的Adapter,然后通过Code Generator产生API所需的Client-go组件给Controller Manager使用,并重新构建一个版本来更新API ServerController Manager以提供对其他资源和版本的支持,这无疑繁琐,也使得Federation v1在可扩展性方面变得非常局限。

正因为上述的这些原因,使得Federation v1在后续的发展中渐渐与社区脱节,并终被弃用。但弃用并不代表着结束,多集群仍然需要可行的解决方案,因此,Kubernetes SIG Multi-Cluster团队在Federation v1之后又提出新的架构Federation v2


03

K8s Federation v


Federation v2Federation v1的基础上,简化了扩展Federated API的过程,并加强了跨集群服务发现与编排的能力。同时,Federation v2在设计之初就提出了两个重要的核心理念,分别是Modularization(模块化)与Customizable(定制化),而提出这两个理念的原因大概也是希望Federation v2可以紧跟Kubernetes社区的发展,并且保持足够的可扩展性。

3.1 Federation v2架构

 

2 Kubernetes Federation v2核心CRD及交互流程图

Federation v2在组件上大的改变应该是移除了API Server,然后使用CRD机制来完成对Federated Resources的扩展。通过Kubefed Controller对这些CRD进行管理,并实现了跨集群的资源同步和编排等能力。简而言之,在功能可扩展方面,Federation v2的提升非常明显。Federation v2通过CRD的方式新增四种API群组来实现联邦机制的核心功能,分别是:

API Group

用途

core.kubefed.k8s.io

集群状态、联邦资源状态、KubeFed Controller相关设定等。

*.kubefed.k8s.io

被用于联邦的Kubernetes API资源定义。

scheduling.kubefed.k8s.io

副本调度编排策略。

multiclusterdns.kubefed.k8s.io

跨集群的服务发现。

而对于这些核心功能用途的理解,相信在了解后文的一些基础概念之后,便可以有一个比较清晰的认识。

3.2 Cluster Configuration

用来定义加入联邦的Kubernetes集群。可通过Kubefed工具执行kubefedctl join/unjoin来加入/删除集群。当集群成功加入联邦后,会创建一个KubeFedCluster的资源对象来储存集群相关信息,比如API EndpointCA Bundle等(详见3.2.1章节)。而这些信息也将会被Kubefed Controller获取并应用在不同的Kubernetes集群之上,以确保能够创建Kubernetes API资源用于访问,示意图如下所示。

 

3 Kubefed Controller工作流程示意图

3.3 Type Configuration

用于管理和定义哪些Kubernetes API资源需要被用于联邦集群。比如说想将ConfigMap 资源通过联邦机制建立在不同的联邦集群上,就必须先在Host集群中,通过CRD创建与之相对应的FederatedConfigMap资源对象,然后再创建名为configmapsType ConfigurationFederatedTypeConfig)资源,并在其中描述出ConfigMap要被FederatedConfigMap管理,这样KubeFed Controller才能知道如何创建Federated 资源。以下是对上述例子的一个示范:

apiVersion: core.kubefed.k8s.io/v1beta1kind: FederatedTypeConfigmetadata:  name: configmaps  namespace: kube-federation-systemspec:  federatedType:    group: *.kubefed.k8s.io    kind: FederatedConfigMap    pluralName: federatedconfigmaps    scope: Namespaced    version: v1beta1  propagation: Enabled  targetType:    kind: ConfigMap    pluralName: configmaps    scope: Namespaced    version: v1

如果想要支持自定义资源(CR)的联邦,即通过CRD来扩展Federated API的话,则可以通过执行kubefedctl enable <target kubernetes API type>指令来进行创建,具体如下:

// 启用CRD,执行后会会创建一个CRD:federatedcustomresourcedefinitions.*.kubefed.iokubefedctl enable customresourcedefinitions// 检索-Kubefed控制平面所在命名空间是否生成与上述一样的CRDkubectl -n kube-federation-system get federatedtypeconfigs.core.kubefed.io// 执行创建名为envoyfilters.networking.istio.io的联邦资源kubefedctl federate crd envoyfilters.networking.istio.io

而一个联邦资源通常具备三个主要功能,可以在Spec中由使用者自行定义,示例如下:

apiVersion: *.kubefed.k8s.io/v1beta1kind: FederatedDeploymentmetadata:  name: fed-deployment  namespace: fed-namespacespec:  template:    metadata:      labels:        app: nginx    spec:      ... ...  placement:    clusters:    - name: cluster1    - name: cluster2  overrides:  - clusterName: cluster2    clusterOverrides:    - path: spec.replicas      value: 6

其中,placement用于定义联邦资源需要分发到哪些集群上。如果没有该配置,则资源不会分发至任何集群,示例中将会同步在集群1Cluster1)和集群2两个集群创建相同的Deployment。当然,也可以使用spec.placement.clusterSelector的方式来选择需要分发的集群。

overrides则用于定义修改指定集群,联邦资源中spec.template内的相关配置。比如部署FederatedDeployment至不同的厂商集群时,便可以通过spec.overrides参数来调整目标集群的Volume大小或副本数,示例中将集群2的副本数调整为6


3.4 Scheduling

Kubefed 目前只能做到一些简单的集群间调度,即手动指定。而对于手动指定的调度方式主要分为两种方式,一种是直接在资源中指定目标集群,参考上述placement的配置方式或使用clusterSelector等亦可。还有一种是通过ReplicaSchedulingPreference参数项进行比例分配,可以实现在多个集群间按比例区别调度,具体如下。

apiVersion: scheduling.kubefed.k8s.io/v1alpha1kind: ReplicaSchedulingPreferencemetadata:  name: fed-deployment  namespace: fed-namespacespec:  targetKind: FederatedDeployment  totalReplicas: 10  clusters:    cluster1:      minReplicas: 3      maxReplicas: 6      weight: 1    cluster2:      minReplicas: 5      maxReplicas: 12      weight: 2

其中,totalReplicas 定义了联邦集群的总副本数,clusters描述了不同联邦集群的大/小副本数的范围以及权重。目前,ReplicaSchedulingPreference参数项只支持DeploymentReplicaset两种资源配置。

04

Karmada


Karmada是华为开源的多云容器编排项目,可以看作是Kubernetes Federation v1v2版本的延续,或者也可以将其理解为是 Federation v3Karmada吸取了Federation项目的经验,在保持原有Kubernetes API不变的情况下,通过添加与多云应用资源编排相关的一套新的API和控制面组件,方便用户将应用部署到多云环境中,实现多集群管理可扩容、高可用等目标

4.1 Karmada架构

 

4 karmada架构

Karmada管理的多云和混合云多集群环境包含两类集群:

lHost集群:由karmada控制面构成的集群,接受用户提交的应用部署需求,将之同步到member集群,并从member集群同步应用后续的运行状况。

lMember集群:由一个或多个k8s集群构成,负责运行用户提交的应用。

4.1.1 Member集群的注册和注销

KarmadaMember集群支持PushPull两种模式注册到Host集群进行管理。Push模式下由Karmada控制面将应用推送到成员集群,而Pull模式下由运行在成员集群侧的Karmada Agent将应用下拉本地。本文选取Push模式进行示例,在Push模式下,用户可以通过karmdactl命令工具的join/unjoin命令很方便的在Karmada集群联邦中注册/注销一个Kubernetes集群,示例如下。

karmadactl join member1 --cluster-kubeconfig=$HOME/.kube/karmada.configkarmadactl unjoin member1 --cluster-kubeconfig=$HOME/.kube/karmada.config

参数member1$HOME/.kube/karmada.config文件中集群的Context名称,也是该集群成功加入联邦后在Karmada控制面中的名称。在集群注册到联邦后,Karmada便会在控制面创建Cluster对象,将该Cluster对象以Yaml的格式输出,可以得到类似如下的内容。

kind: Clustermetadata:  finalizers:  - karmada.io/cluster-controller  name: member1spec:  apiEndpoint: https://127.0.0.1:6443  secretRef:    name: member1    namespace: karmada-cluster  syncMode: Push

其中,.spec.syncModepush,说明Member集群加入Karmada集群联邦选用的模式,.spec.apiendpoint则是Member集群的API Server地址.sepc.secretRef表示的是在集群Join的过程中,Karmada为其创建的Service Account以及对应Secrets。通过Cluster对象spec中的apiEndpointsecretRef等资源,Karmada控制面便可以在member1集群中操作各种资源对象,实现对Member集群的管理。

4.1.2 Member集群的状态跟踪

Karmada通过ClusterLeaseCluster Status三个Controller共同完成Member集群的状态跟踪,并将状态写入Cluster对象的status。在Karmada集群联邦Cluster Status ControllerCSC可能运行在两个地方对于Push模式的Member集群,Karmada控制面中的Karmada Controller Manager会运行一个CSC。而对于Pull模式的集群,在Member集群侧运行的Karmada Agent会运行一个Karmada Controller Manager再由其启动一个CSC,这一点与Push模式一致。了解完相关组件,接下来对Member集群状态跟踪的流程进行一下梳理,这里同样以Push模式为例。

CSC基于sigs.k8s.io/controller-runtime框架实现,负责监控Karmada控制面中Cluster对象的操作事件,这些增删改事件会在ClusterPredicateFunc进行函数过滤,完成后可以实现CSC只处理Push模式的Cluster操作事件。CSC默认以10秒钟为间隔获取集群的相关信息并写入Cluster对象的status,具体如下:

lMember集群的在线状态(Online)和健康状态(Healthy)。CSC使用ClientSet访问Member集群的API Server/readyz,如果/readyz无法访问则使用/healthz。如果/readyz/healthz能够正常访问,则表示集群Online状态为true,如果能够得到200HTTP响应,则表示集群Healthy状态为true。之后,CSC会根据获取到的信息设置集群状态,并写入Cluster对象对象的.status.conditions

lMember集群的Node状态,并写入Cluster对象的.status.nodeSummary

lMember集群的资源使用状态,并写入Cluster对象的.status.resourceSummary

lMember集群的版本,并写入Cluster对象的.status.kubernetesVersion

lMember集群的API支持情况,并写入Cluster对象的.status.apiEnablements

下面示例一个Push模式下Member集群的status,即将Cluster对象以Yaml的格式输出。由于支持跟踪的API资源较多,示例只展示部分,具体如下。

apiVersion: cluster.karmada.io/v1alpha1kind: Clustermetadata:  finalizers:  - karmada.io/cluster-controller  name: member1spec:  apiEndpoint: https://127.0.0.1:6443  secretRef:    name: member1    namespace: karmada-cluster  syncMode: Pushstatus:  apiEnablements:  - groupVersion: v1    resources:    - kind: Endpoints      name: endpoints    - kind: Node      name: nodes    - kind: Pod      name: pods- kind: Service      name: services    ... ...  conditions:  - lastTransitionTime: "2022-11-13T10:18:36Z"    message: cluster is reachable and health endpoint responded with ok    reason: ClusterReady    status: "True"    type: Ready  kubernetesVersion: v1.21.5  nodeSummary:    readyNum: 3    totalNum: 3  resourceSummary:    allocatable:      cpu: "32"      pods: "256"    ... ...

可以看到Cluster资源对象中集群状态是Ready/readyz返回HTTP 200,并且该状态的转变时间(lastTransitionTime2022-11-13T10:18:36Z,时间戳在集群状态发生变化时会同步更新。对于Push模式的Member集群耳炎,只要CSC正常运行,那么它就会一直尝试访问Member集群API Server/readyz/healthz,无论请求是否成功将根据结果判断Member集群的状态并对Cluster对象进行更新

4.2 Karmada核心概念

 

5 Karmada API工作流程图

lResource Template:资源模板。Kubernetes原生API定义,包括CRD。无需修改即可创建多集群应用。

lPropagation Policy:分发策略。可重用的应用多集群调度策略。

lResource Binding:通用类型,驱动内部流程。

lOverride Policy:差异化调度策略。跨集群可重用的差异化配置策略。

lWorkMember集群终资源在联邦层面的映射。

4.2.1 Propagation Policy示例

apiVersion: policy.karmada.io/v1alpha1kind: PropagationPolicymetadata:  name: example-policyspec:  resourceSelectors:  - apiVersion: apps/v1    kind: Deployment    name: deployment-1    labelSelector:   propagateDependensies: false  placement:    clusterAffinity:      clusterNames:        - cluster1        - cluster3     clusterTolerations:     spreadConstraints:       - spreadByLabel: failuredomain.kubernetes.io/zone         maxGroups: 3         minGroups: 3   schedulerName: default

其中,resourceSelector支持关联多种资源类型,并可以使用NamelabelSelector对资源对象进行筛选。clusterAffinity定义倾向调度的目标集群,相似的,它也可以支持通过NamelabelSelector的方式对目标集群进行筛选。clusterTolerations类似单集群中Pod TolerationsNode TaintsspreadConstraints定义应用分发的HA策略,支持按RegionAZ、特性label分组对集群动态分组,实现不同层级的HA

4.2.2 Override Policy示例

apiVersion: policy.karmada.io/v1alpha1kind: OverridePolicymetadata:  name: example-override  namespace: defaultspec:  resourceSelectors:    - apiVersion: apps/v1      kind: Deployment  targetCluster:    labelSelector:      matchLabels:      failuredomain.kubernetes.io/region: region1  overriders:    imageOverrider:    - component: prefix      operator: replace      value: "region-1.registry.io

其中,resourceSelector支持使用NamelabelSelector对资源对象进行筛选。overriders支持多种override插件类型,而imageOverrider是针对容器镜像的差异化配置插件。

4.3 Karmada案例演示

首先,定义资源模板(Resource Template)。为何称为模板?是因为在每个集群中实际部署Deployment对象时都可以以它为模板创建,但又允许通过Override Policy修改其配置。

apiVersion: apps/v1kind: Deploymentmetadata:  name: busybox  namespace: default  labels:    app: busyboxspec:  replicas: 1  selector:    matchLabels:      app: busybox  template:    metadata:      labels:        app: busybox    spec:      containers:      - image: busybox:latest        name: busybox

然后,定义分发策略(Propagation Policy)。指定将上述nginx deployment部署到member1member2两个由karmada管理的member集群中。在propagation policy对象的设置中我们应该注意以下几点:

  l.spec.resourceSelectors指定了需要部署到member集群中的资源:deployment—>nginx

l.spec.placement指定了nginx需要部署到member1member2两个member集群中。

l.spec.dependentOverrides表示需要karmada控制面等待下面的差异化配置(override policy)创建之后再将应用部署到member1member2集群。

apiVersion: policy.karmada.io/v1alpha1kind: PropagationPolicymetadata:  name: nginx-propagation  namespace: defaultspec:  resourceSelectors:    - apiVersion: apps/v1      kind: Deployment      name: busybox  placement:    clusterAffinity:      clusterNames:        - member1        - member2  dependentOverrides:    - nginx-override

后,定义差异化调度策略(override policy),指定将部署在member2集群中的nginx deploymentreplica数量改为2,即需要在member2集群中运行2nginx实例。

apiVersion: policy.karmada.io/v1alpha1kind: OverridePolicymetadata:  name: busybox-override  namespace: defaultspec:  resourceSelectors:    - apiVersion: apps/v1      kind: Deployment      name: busybox  targetCluster:    clusterNames:      - member2  overriders:    plaintext:    - path: "/spec/replicas"      operator: replace      value: 2

将上述三个yamlkubectl apply提交给karmada控制面后,便可以登录到member1集群和member2集群查看应用服务分发的完成情况。比如,登录member2集群执行kubectl get deployment,可以看到busybox服务的实例数为2。或者直接在Host集群执行kubectl get deployment,可以发现busybox服务的Pod总数已经变成了3个。


05

总结

本文介绍了Kubernetes多集群管理发展的,主要包括以下几个部分内容:

l部分,介绍了Kubernetes集群联邦的基础知识,包括为什么需要多集群、集群联邦的概念和演进过程;

l第二部分,介绍了Kubernetes Federation v1v2版本的发展历程,包括从代码层面剖析了Kubefed如何实现多集群管理,以及Federation v2版本的一些新特性;

l第三部分,介绍了Karmada多云容器编排项目,包括其架构、Member集群的管控、Karmada核心概念等,并提供了一个Karmada管理多集群实现服务应用分发的示例

参考文献

[1]https://github.com/kubernetes-sigs/kubefed

[2]https://github.com/karmada-io/karmada

[3]https://zhuanlan.zhihu.com/p/407990257

[4]https://www.kubernetes.org.cn/5702.html

[5]https://zhuanlan.zhihu.com/p/411022709

相关文章