通过Kubernetes原生的ResourceQuota方式进行固定资源分配,集群的整体资源利用率不高。阿里云借鉴Yarn Capacity Scheduling的设计思路,基于Scheduling Framework的扩展机制,在调度时引入弹性配额组实现了Capacity Scheduling功能,在确保用户资源分配的基础上通过资源共享的方式来提升集群的整体资源利用率。本文介绍如何使用Capacity Scheduling功能。
前提条件
已创建Kubernetes版本大于等于1.20的ACK集群Pro版。详细信息,请参见创建Kubernetes托管版集群。
背景信息
当集群中有多个用户时,为了保障用户有足够的资源使用,管理员会将集群的资源固定分配给不同的用户。传统的方法是通过Kubernetes原生的ResourceQuota方式进行固定资源的分配。因为不同的用户使用资源的周期和方式不同,可能会出现在某一时刻,一些用户的资源不够用,而另一些用户的资源闲置。如果出现多个类似的情况,则整个集群会有很多资源浪费,导致集群的整体资源利用率下降。
Capacity Scheduling核心功能
针对上述问题,阿里云基于Scheduling Framework的扩展机制,在调度侧实现了Capacity Scheduling的功能,在确保用户的资源分配的基础上通过资源共享的方式来提升整体资源的利用率。Capacity Scheduling具有以下功能:
支持定义不同层级的资源配额。如下图所示,您可以根据具体情况(比如:公司的组织结构)配置多个层级的弹性配额。弹性配额组的叶子节点可以对应多个Namespace,但同一个Namespace只能归属于一个叶子节点。
支持不同弹性配额之间的资源借用和回收。
Min:您可以使用的保障资源(Guaranteed Resource)。当整个集群资源紧张时,所有用户使用的Min总和需要小于集群的总资源量。
Max:您可以使用的资源上限。
说明您的工作负载可以向其他用户借用其未使用的资源额度,但借用后可使用的资源总量依然不能超过Max。
您未使用的Min资源配额可以被其他用户借用,但当该用户需要使用资源时,可以通过抢占的方式回收借出的资源。
支持多种资源的配置。您除了可以配置CPU和内存资源,也可以配置如GPU等任何Kubernetes支持的扩展资源(Extended Resource)。
使用Capacity scheduling功能的使用示例
本文测试集群节点资源为1台ecs.sn2.13xlarge机器(56 vCPU 224 GiB)。
执行以下命令,创建对应的Namespace。
kubectl create ns namespace1 kubectl create ns namespace2 kubectl create ns namespace3 kubectl create ns namespace4
使用以下YAML文件样例,创建相应的弹性配额组。
根据以上YAML文件示例配置,在
namespaces
字段配置对应的命名空间,在children
字段配置对应子级的弹性配额。配置需要满足以下几点:同一个弹性配额中的Min≤Max。
子级弹性配额的Min之和≤父级的Min。
Root节点的Min=Max≤集群总资源。
Namespace只与弹性配额的叶子节点有一对多的对应关系,且同一个Namespace只能归属于一个叶子节点。
执行以下命令,查看弹性配额组是否创建成功。
kubectl get ElasticQuotaTree -n kube-system
预期输出:
NAME AGE elasticquotatree 68s
使用以下YAML文件样例,在
namespace1
中部署服务,Pod的副本数为5个,每个Pod请求CPU资源量为5核。执行以下命令,查看集群Pod的部署情况。
kubectl get pods -n namespace1
预期输出:
NAME READY STATUS RESTARTS AGE nginx1-744b889544-52dbg 1/1 Running 0 70s nginx1-744b889544-6l4s9 1/1 Running 0 70s nginx1-744b889544-cgzlr 1/1 Running 0 70s nginx1-744b889544-w2gr7 1/1 Running 0 70s nginx1-744b889544-zr5xz 0/1 Pending 0 70s
因为当前集群的资源有空闲(
root.max.cpu=40
),当在namespace1
下的Pod申请CPU资源量超过root.a.1
设置的min.cpu=10
时,还可以继续借用其他处于空闲的资源,最多可以申请使用root.a.1
设置的配额max.cpu=20
。当Pod申请CPU资源量超过
max.cpu=20
时,再申请的Pod会处于Pending状态。所以此时申请的5个Pod中,有4个处于Running状态,1个处于Pending状态。
使用以下YAML文件样例,在
namespace2
中部署服务,其中Pod的副本数为5个,每个Pod请求CPU资源量为5核。执行以下命令,查看集群Pod的部署情况。
kubectl get pods -n namespace1
预期输出:
NAME READY STATUS RESTARTS AGE nginx1-744b889544-52dbg 1/1 Running 0 111s nginx1-744b889544-6l4s9 1/1 Running 0 111s nginx1-744b889544-cgzlr 1/1 Running 0 111s nginx1-744b889544-w2gr7 1/1 Running 0 111s nginx1-744b889544-zr5xz 0/1 Pending 0 111s
kubectl get pods -n namespace2
预期输出:
NAME READY STATUS RESTARTS AGE nginx2-556f95449f-4gl8s 1/1 Running 0 111s nginx2-556f95449f-crwk4 1/1 Running 0 111s nginx2-556f95449f-gg6q2 0/1 Pending 0 111s nginx2-556f95449f-pnz5k 1/1 Running 0 111s nginx2-556f95449f-vjpmq 1/1 Running 0 111s
同
nginx1
一样,因为当前集群有资源空闲(root.max.cpu=40
),当在namespace2
下Pod申请CPU资源量超过root.a.2
设置的min.cpu=10
时,还可以继续借用其他处于空闲的资源,最多可以申请使用root.a.2
设置的配额max.cpu=20
。当Pod申请CPU资源量超过
max.cpu=20
时,再申请的Pod处于Pending状态。所以此时申请的5个Pod中,有4个处于Running状态,1个处于Pending状态。此时集群中
namespace1
和namespace2
中的Pod所占用的资源已经为root
设置的root.max.cpu=40
。
使用以下YAML文件样例,在
namespace3
中部署服务,其中Pod的副本数为5个,每个Pod请求CPU资源量为5核。执行以下命令,查看集群Pod的部署情况。
kubectl get pods -n namespace1
预期输出:
NAME READY STATUS RESTARTS AGE nginx1-744b889544-52dbg 1/1 Running 0 6m17s nginx1-744b889544-cgzlr 1/1 Running 0 6m17s nginx1-744b889544-nknns 0/1 Pending 0 3m45s nginx1-744b889544-w2gr7 1/1 Running 0 6m17s nginx1-744b889544-zr5xz 0/1 Pending 0 6m17s
kubectl get pods -n namespace2
预期输出:
NAME READY STATUS RESTARTS AGE nginx2-556f95449f-crwk4 1/1 Running 0 4m22s nginx2-556f95449f-ft42z 1/1 Running 0 4m22s nginx2-556f95449f-gg6q2 0/1 Pending 0 4m22s nginx2-556f95449f-hfr2g 1/1 Running 0 3m29s nginx2-556f95449f-pvgrl 0/1 Pending 0 3m29s
kubectl get pods -n namespace3
预期输出:
NAME READY STATUS RESTARTS AGE nginx3-578877666-msd7f 1/1 Running 0 4m nginx3-578877666-nfdwv 0/1 Pending 0 4m10s nginx3-578877666-psszr 0/1 Pending 0 4m11s nginx3-578877666-xfsss 1/1 Running 0 4m22s nginx3-578877666-xpl2p 0/1 Pending 0 4m10s
nginx3
的弹性配额root.b.1
的min
设置为10
,为了保障其设置的min
资源,调度器会将root.a
下之前借用root.b
的Pod资源归还,使得nginx3
能够至少得到配额min.cpu=10
的资源量,保证其运行。调度器会综合考虑
root.a
下作业的优先级、可用性以及创建时间等因素,选择相应的Pod归还之前抢占的资源(10核)。因此,nginx3
得到配额min.cpu=10
的资源量后,有2个Pod处于Running状态,其他3个仍处于Pending状态。使用以下YAML文件样例,在
namespace4
中部署服务nginx4
,其中Pod的副本数为5个,每个Pod请求CPU资源量为5核。执行以下命令,查看集群Pod的部署情况。
kubectl get pods -n namespace1
预期输出:
NAME READY STATUS RESTARTS AGE nginx1-744b889544-cgzlr 1/1 Running 0 8m20s nginx1-744b889544-cwx8l 0/1 Pending 0 55s nginx1-744b889544-gjkx2 0/1 Pending 0 55s nginx1-744b889544-nknns 0/1 Pending 0 5m48s nginx1-744b889544-zr5xz 1/1 Running 0 8m20s
kubectl get pods -n namespace2
预期输出:
NAME READY STATUS RESTARTS AGE nginx2-556f95449f-cglpv 0/1 Pending 0 3m45s nginx2-556f95449f-crwk4 1/1 Running 0 9m31s nginx2-556f95449f-gg6q2 1/1 Running 0 9m31s nginx2-556f95449f-pvgrl 0/1 Pending 0 8m38s nginx2-556f95449f-zv8wn 0/1 Pending 0 3m45s
kubectl get pods -n namespace3
预期输出:
NAME READY STATUS RESTARTS AGE nginx3-578877666-msd7f 1/1 Running 0 8m46s nginx3-578877666-nfdwv 0/1 Pending 0 8m56s nginx3-578877666-psszr 0/1 Pending 0 8m57s nginx3-578877666-xfsss 1/1 Running 0 9m8s nginx3-578877666-xpl2p 0/1 Pending 0 8m56s
kubectl get pods -n namespace4
预期输出:
nginx4-754b767f45-g9954 1/1 Running 0 4m32s nginx4-754b767f45-j4v7v 0/1 Pending 0 4m32s nginx4-754b767f45-jk2t7 0/1 Pending 0 4m32s nginx4-754b767f45-nhzpf 0/1 Pending 0 4m32s nginx4-754b767f45-tv5jj 1/1 Running 0 4m32s
同理,
nginx4
的弹性配额root.b.2
的min
设置为10
,为了保障其设置的min
资源,调度器会将root.a
下之前借用root.b
的Pod资源归还,使得nginx4
能够至少得到配额min.cpu=10
的资源量,保证其运行。调度器会综合考虑
root.a
下作业的优先级、可用性以及创建时间等因素,选择相应的Pod归还之前抢占的资源(10核)。因此,nginx4
得到配额min.cpu=10
资源量后,有2个Pod处于Running状态,其他3个仍处于Pending状态。此时,集群所有的弹性配额都使用其
min
设置的保障资源(Guaranteed Resource)。
相关文档
kube-scheduler发布记录,请参见kube-scheduler。
ACK基于新版的Kube-scheduler框架实现Gang scheduling的能力。Gang scheduling主要用于确保一组关联的Pod能够同时调度到集群中的节点上,或者如果无法满足这种条件,则这些Pod都不会被调度,解决原生调度器无法支持All-or-Nothing作业调度的问题。这种策略在执行需要严格同步或共享资源的分布式应用时非常有用,例如Spark、Hadoop等大数据处理任务。详细信息,请参见使用Gang scheduling。