【调度器插件专题】Capacity-scheduling弹性容量配额调度器插件

Posted by Hao Liang's Blog on Saturday, October 23, 2021

最近,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"