首页 > 云计算 > > Kubernetes 多集群调度方案调研

❝本文转自 Edwardesire 的博客,原文:https://edwardesire.com/posts/multi-cluster-scheduling/,版权归原作者所有。
Kubernetes 是一个容器编排平台,用于调度、部署和管理容器化应用。并且经过几年的发展,k8s 已经成为事实上的容器编排平台标准。集群是 k8s 架构的构建块(building block)。集群由多个工作节点(物理机或者虚拟机)组成,提供一个可供容器运行的资源池。一个集群拥有:
每个集群都是独立的系统,可以部署在公司的自建机房,也可以部署在云厂商的一个可用区。k8s 负责管理集群的可用资源:调度器将容器调度到适当的机器、kubelet 负责 pod/ 容器的生命周期管理。这些都是大家很熟悉的概念了。但是 k8s 的作用范围是集群内,一个集群内的控制平面无法感知另一个集群的资源余量和服务状态。当多集群的应用场景出现时,我们怎么处理多个集群的调度呢。
通常来说,一个集群的大小需要预估业务的资源总量。当资源不够时,可以通过增加机器数量来进行集群扩容。但是集群规模也不是无限上升的。当节点 /pod 的数量越多,对控制平台的组件的压力就越大,进而影响集群整体稳定性。
当然,使用多集群调度肯定会增加整体架构的复杂度,集群之间的状态同步也会增加控制面的额外开销。所以,多集群的主要攻克的难点就是跨集群的信息同步和跨集群网络连通方式。跨集群网络连通方式一般的处理方式就是确保不同机房的网络相互可达,这也是最简单的方式。
而我比较干兴趣的是跨集群的信息同步。多集群的服务实例调度,需要保证在多集群的资源同步的实时,将 pod 调度不同的集群中不会 pod pending 的情况。控制平面的跨集群同步主要有两类方式:
KubeFed[1]和Liqo[2]都是开源项目,两者的主要区别如下:
| 场景 | Kubefed | Liqo |
|---|---|---|
| Pod 调度 | 静态调度、动态调度只支持 Deployment/ReplicaSet | 动态调度,但是还是会出现 pending 情况 |
| 控制平面 | 一套中心化管理组件 | 每个集群对等管理 |
| 集群-发现 | 主动注册 | 动态发现和注册新集群 |
本文不会详细介绍这些项目在部署和网络管理的内容,重点分析两个项目(Kubefed、Liqo)的跨集群资源调度。
KubeFed 的目的是通过统一的 API 管理多个 k8s 集群,实现单一集群管理多个 k8s 集群的机制,并通过 CRD 的机制来扩展现有的 k8s 资源跨集群编排的能力。
KubeFed 将集群分为 host cluster,member cluster 两个角色:
kubefedctl join 的方式手动注册到 host cluster。被联邦管理的 k8s API 资源统称为 Federated Resources,例如 FederatedDeployments。开启跨集群调度也需要在 host cluster 上显式地对应用资源开启集群联邦管理,开启的方式是通过 kubefedctl federate 创建 FederatedTypeConfig。KubeFed 也支持联邦化 namespace,让 namespace 下面的资源都联邦化。通过 federation control plane 来下发资源到 member cluster。

KubeFed 概览
那 KubeFed 怎么支持跨集群部署的呢?在开启资源的联邦化之后,Federated Resources 的 spec 分成 3 个基础配置 :
用户在部署服务时,可以在 spec 下定义部署的集群(placement)和集群指定参数(overrides)。比如服务需要在 cluster2 集群上增加副本数,修改 deployment 的 replicas,需要在 overrides 上填写“/spec/replicas”的修改。可以想到为了通用,overrides 的配置有一定复杂性。
spec:
template:
metadata:
labels:
app: nginx
spec:
replicas: 3
...
placement:
clusters:
- name: cluster2
- name: cluster1
overrides:
- clusterName: cluster2
clusterOverrides:
- path: "/spec/replicas"
value: 5
...
Federated Resources 还支持通过另一个 CRD 来 ReplicaSchedulingPreference 配置资源类型级别的集群调度倾向。该方法通过指定资源类型在 member cluster 的最大 / 最小副本数实现了依据剩余可用资源的动态平衡。最终 controller 会将 preference 信息更新到 Federated Resources 的 overrides 上,从而重新分配实例。不过该功能还只支持 deployment/replicaset 资源。
可以看出在 KubeFed 的架构,还是区分了不同集群的配置和动态调度。
具体看看静态调度和动态调度。
首先,FederatedTypeConfig 的 spec 里定义了 host cluster 中的 Federated Resources 和 member cluster 集群的所代表的资源:
spec 对应的 federatedType 是定义的联邦资源(由用户创建),而 targetType 实际上是真正的 k8s workload 资源, KubeFed 的 controller 可以通过 federatedType 的 placement 和 overrides 来控制各个集群里的对应的 targetType 的资源的分布。具体来说,负责下发 targetType 的 SyncController 会调用 dispatcher 生成 JSONPatch 并向 member cluster 创建底层资源。

KubeFed 静态调度
默认创建 Federated 资源时,可以在 spec 下定义部署的集群(placement)和集群指定参数(overrides),实现多集群的 pod 分配。但是这种调度是静态的。如果 overrides 设置的副本数超过集群剩余可用资源,那么新 pod 在集群里因为资源不够导致 pending。静态方式在大规模场景下无非是低效的,扩集群调度在极端情况下需要人工观测集群剩余资源来规划集群分布。但是很多服务是不需要感知集群信息的,他最在意的是还是服务实例能够正常启动,希望服务在不同集群的打散是一种动态被平台托管的。所以 KubeFed 引入了 ReplicaSchedulingPreference(RSP)的功能。
RSP 的实现依赖于一个新的 CRD——ReplicaSchedulingPreference 关键的几个配置项为:
例如,下面这个例子期望 cluster1 和 cluster2 以 2:3 的比例下发服务的副本。当集群资源充足时,cluster1 会分配 4 个 pod,cluster2 被分配 6 个实例。当集群 cluster2 资源不足时,rebalance 的设置允许将 cluster2 的副本挪到 cluster1。
spec:
targetKind: FederatedDeployment
totalReplicas: 10
rebalance: true
intersectWithClusterSelector: false
clusters:
cluster1:
weight: 2
cluster2:
weight: 3
下图描述了 KubeFed RSP 的工作流程,起作用的组件是 ReplicaScheduler。

KubeFed ReplicaSchedulingPreference workflow
ReplicaScheduler 首先会获取 PreferredClusters,将健康的集群的信息收集。然后获取当前服务副本的集群分布,会统计每个集群的 currentReplicasPerCluster、estimatedCapacity:

ReplicaScheduler.Planner 依据上述信息(RSP 的 spec、currentReplicasPerCluster 和 estimatedCapacity),在可用集群中分配副本。Planner 的第一步会依据 ReplicaSchedulingPreference 中集群权重将可用集群排序,并划定每个集群的可分配上界和下界:
最终依据集群权重,将副本分配到各集群中。
ReplicaScheduler.Plugin 获得新的 overrides 信息将原 Federated resources 的 spec 更新。之后的流程就是原有静态调度负责下发到集群。整个 ReplicaSchedulingPreference 能够动态地在多个集群分配 pod,并且包含了处理 Pending Pod 的情况。
但是局限在于只支持 Deployment/ReplicaSet 资源。
与 KubeFed 相比,Liqo 有着 pod 无缝调度、去中心化治理等优势。
Liqo 将集群划分为 home cluster 和 foreign cluster。在每个集群都需要安装 liqo 的组件来管理多集群,这两类集群本质上没有区别。只是对于 home cluster 来说,其他的 foreign cluster 都是通过 Virtual Kubelet[3] 的方式映射成本集群节点。home cluster 将 foreign cluster 当作一个大的节点来使用。这样就能做到用户在 home cluster 上创建资源时,能够调度到 foregin cluster 上。Virutal Kubelet 具体的内容可以看这篇文章《Virtual Kubelet[4]》。Virutal Kubelet 依据目的实现如下功能,就能在 k8s 集群内注册一个虚拟的节点。
❝自定义的 provider 必须提供以下功能:
提供 pod、容器、资源的生命周期管理的功能 符合 virtual kubelet 提供的 API 不直接访问 k8s apiserver,定义获取数据的回调机制,例如 configmap、secrets
在集群注册方面,Liqo 支持动态发现集群(mDNS、LAN 两种方式)。在安装 Liqo 组件时,可配置集群通过何种方式发现和集群共享出去的资源百分比(默认 30%)。所以 Liqo 的多集群前提是集群之间已经网络可达。
在调度方面,因为 remote cluster 通过 virtual kueblet 将该集群视为一个大节点,可以直接依赖本集群的 kube-scheduler 来调度。但是这里还是有个问题。当 remote cluster 的资源碎片比较多时,大节点上报资源时会聚合成一个大块资源。导致大套餐的 pod 调度到 remote cluster 会出现 pending。
Ligo 的工作流程如下图所示 . 当用户创建一个 deployment 时,默认调度器负责判断能否调度到 virtual node 上。之后这个服务的 pod 创建会被 virtual kubelet 接管。当 pod 被调度的 virtual node 上时,virtual kubelet 会在 remote cluster 会对应的 replicaSet。使用 replicaSet 原因是 remote cluster 上的 pod 被驱逐时,能够在集群上重建,而不是在 home cluster 重新调度。

在了解多集群调度的细节之前,需要先弄清楚 virtual kubelet 的工作机制。我们知道 Kubelet 有个 SyncHandler, virtual kubelet 也有 PodLifecycleHandler,处理 pod 被 k8s 创建、更新、删除的情况。例如,Liqo 的 virtual kubelet 处理创建 pod 时,会先将 pod 的元数据里包含 home cluster 的信息转换成 foreign cluster (比如 label 和 namespace),然后将 pod 包在 ReplicaSet 里通过 foreign Client 创建 ReplicaSet 资源。
上面介绍了一个 pod 在不同集群之间的映射。接下来从 foreign cluster 的角度发现 home cluster 的共享资源的具体流程。下属 Controller 和 CRD 都是由 Liqo 安装。

可以看到 home cluster 和 foreign cluster 没有本质上的区别,两者可以相互使用对方允许共享的机器资源。当然,集群 A 也可以配置成不贡献自己的资源出去,只使用别的集群分享出来的资源。
本文介绍了 KubeFed/Liqo 两个开源项目多集群的资源管理和 pod 调度。
KubeFed 的资源调度总体上比较静态,需要创建 workload 之前确保各集群有充足资源。它的动态调度能力有限,因为只能对 Deployment/ReplicaSet 开启。当 KubeFed 下发 pod 到集群出现 pod pending 的情况时,动态调度的能力能够将 pod 迁移到其他集群。
Liqo 是能做到动态的资源发现和 pod 调度,但是它的方法是将一个集群抽象成一个工作节点。这种方式必然忽略了资源碎片的现象,使得虚拟节点的资源余量存在失真。比如集群内的 2 个节点共剩余 40 核可用资源,并不一定代表集群还能调度一个 40 核资源的 pod。但是从 Liqo 的方式来看,这个集群是能够调度 40 核 pod 的。并且目前来看,如果集群内出现 pod pending 的情况,也没法自动迁移 pod 到其他集群。
不过 Liqo 的整个思想是比较新颖的,通过对等的 P2P 方式将多个集群聚合成一个大的可用资源池。对于现有集群也不会引入一套新的 API。或许 Liqo 可以引入 descheduler 将 pending pod 迁移到其他机器来解决资源余量的误差问题。
KubeFed: https://github.com/kubernetes-sigs/kubefed
[2]Liqo: https://github.com/liqotech/liqo
[3]Virtual Kubelet: https://github.com/virtual-kubelet/virtual-kubelet
[4]Virtual Kubelet: https://www.huweihuang.com/kubernetes-notes/virtual-kubelet/virtual-kubelet.html


?
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31509949/viewspace-3129368/ ,如需转载,请注明出处,否则将追究法律责任。