最近,2021年的北美 KubeCon 在线上举行,Kubernetes sig-scheduling 小组的 scheduler-plugin 开源项目的活跃贡献者 @denkensk 和 @yuanchen8911 带来了 Capacity scheduling 弹性容量配额调度器插件的演讲。
1、 背景
相关提案:KEP9: Capacity scheduling.
源码地址:Capacity scheduling
- 当前 Kubernetes 原生的 ResourceQuota 配额机制局限于单个 namespace(只能为每个 namespace 配置 ResourceQuota 资源限额)
- Pod 发生调度抢占时只根据其 PriorityClass 的优先级做为是否抢占的标准
- 缺少一种跨 namespace 的、更灵活的资源配额机制
- 批量调度应用时,集群资源使用率低(存在一些 Pod 创建时占用了 namespace 的配额,但调度失败了,资源没有真正被使用)
2、功能简介
Capacity scheduling 调度器插件设计了一套全新的 Quota 机制(Elastic Quota),其中包含了几个概念:
- max:Pod 申请资源的上限,整个 namespace 所能提供的最多资源
- min:Pod 申请资源的下限,当 min 资源处于空闲时(requests + allocated <= min),该 min 资源会:1、被其它 Quota 抢占,2、优先让被抢占的 victim Pod 使用
- 在 “min” 和 “max” 之间的资源配额缓冲区可以容忍 Pod 运行异常。例如,Pod 创建使用了整个 Elastic Quota 的 min,但运行异常(并没有实际使用),在没达到 max 之前, 其它 Pod 也能被成功创建。而如果使用 Kubernetes 原生的 resourceQuota,一旦 Pod 创建使用达到了 Quota 值,新的 Pod 就无法创建了。
- 允许使用 A Elastic Quota 的工作负载“借用”(不超过 A 的 max 的前提下)其它 B Elastic Quota 未使用的 min 配额。这样能确保该工作负载的 Pod 被抢占后(成为 victim),能及时通过“借用” 空闲 Elastic Quota 的配额重新创建出来
3、实现原理
Elastic Quota CRD 简介
Elastic Quota CRD 包含以下字段结构:
// ElasticQuota sets elastic quota restrictions per namespace
type ElasticQuota struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec ElasticQuotaSpec
Status ElasticQuotaStatus
}
// ElasticQuotaSpec defines the Min and Max for Quota.
type ElasticQuotaSpec struct {
Min v1.ResourceList
Max v1.ResourceList
}
// ElasticQuotaStatus defines the observed use.
type ElasticQuotaStatus struct {
Used v1.ResourceList
}
配置样例:
apiVersion: scheduling.sigs.k8s.io/v1alpha1
kind: ElasticQuota
metadata:
name: test
namespace: test
spec:
max:
cpu: 20
memory: 40Gi
nvidia.com/gpu: 2
min:
cpu: 10
memory: 20Gi
nvidia.com/gpu: 1
和 Resource Quota 之间的关系
Resource Quota 带有字段 “hard”,而 Elastic Quota 带有字段 “min” 和 “max”,后期有望能将 Elastic Quota 整合进 Resource Quota 中:
-
hard: 在 Pod 创建时,通过 admission 准入机制检查 Pod 所需资源(requests)是否在 hard 配置范围内,只要 Pod 提交了创建请求,就会统计到 hard 资源占用中,因此该值应该比所有 Pod 的 requests 值大且大于或等于 max 值。
-
max: 在 Pod 调度的 Prefilter 阶段,检查 Pod 所需资源(requests)是否在 max 配置范围内,只有成功调度的 Pod 才会统计到 max 资源的占用中。
-
min: Quota 的最少资源保证
-
min <= max <= hard
调度器插件原理
a. 调度器扩展点
-
Prefilter 阶段确保 Pod 所需资源不超过 Quoota 的 max 值
-
Postfilter 阶段检查是否达到 Quoota min 的最少资源保证,如果没有达到,进入自定义抢占逻辑()
-
Reserved 阶段对本次调度的 Pod 所需资源进行 Quota 预保留,防止下个调度周期的 Pod 重复使用了 Quota,如果在 Bind 阶段 Pod 和节点绑定失败了,再把预保留的 Quota 释放掉
b. 调度样例
我们假设当前集群总 GPU 容量为10,集群中有 QuotaA (min:4, max:6) 和 QuotaB (min:6, max:8)
最开始,QuotaA 和 QuotaB 的使用量都没有达到 min
这时,用户创建 Workload,让 QuotaA 达到了 min,此时 QuotaB 仍然没有达到 min(有空闲),集群还剩下 10 - 4 - 3 = 3 个 GPU 分配
这时,如果用户想继续使用集群里剩下的 3 个 GPU,发现最多还能用 2 个(达到 QuotaA 的 max),此时相当于 QuotaA 从 QuotaB 的 min 里面“借用”了资源
这时,如果用户创建 Workload,想用 QuotaB 的配额,会优先满足让 Pod 使用达到 QuotaB 的 min,此时会把原来 QuotaA “借用”的资源归还给 QuotaB(通过抢占),最终两个 Quota 都满足 min 最少保证资源的使用。
3、使用手册
调度器插件配置样例:
apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: false
clientConnection:
kubeconfig: "REPLACE_ME_WITH_KUBE_CONFIG_PATH"
profiles:
- schedulerName: default-scheduler
plugins:
preFilter:
enabled:
- name: CapacityScheduling
postFilter:
enabled:
- name: CapacityScheduling
disabled:
- name: "*"
reserve:
enabled:
- name: CapacityScheduling
pluginConfig:
- name: CapacityScheduling
args:
kubeConfigPath: "REPLACE_ME_WITH_KUBE_CONFIG_PATH"
配置 Elastic Quota:
apiVersion: scheduling.sigs.k8s.io/v1alpha1
kind: ElasticQuota
metadata:
name: quota1
namespace: quota1
spec:
max:
cpu: 2
min:
cpu: 0
---
apiVersion: scheduling.sigs.k8s.io/v1alpha1
kind: ElasticQuota
metadata:
name: quota2
namespace: quota2
spec:
max:
cpu: 2
min:
cpu: 0
配置Pod Priority Class:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "Sample High Priority Class"