集群队列

一个集群范围的资源,用于管理资源池,定义使用限制和公平共享规则。

ClusterQueue 是一个集群范围的对象,用于管理资源池,如 Pod、CPU、内存和硬件加速器。ClusterQueue 定义

  • ClusterQueue 管理的 资源风味 的配额,包括使用限制和消耗顺序。
  • 集群中多个 ClusterQueue 之间的公平共享规则。

只有 批处理管理员 才能创建 ClusterQueue 对象。

示例 ClusterQueue 如下所示

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {} # match all.
  resourceGroups:
  - coveredResources: ["cpu", "memory", "pods"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 36Gi
      - name: "pods"
        nominalQuota: 5

此 ClusterQueue 仅在以下情况下允许 工作负载

  • CPU 请求的总和小于或等于 9。
  • 内存请求的总和小于或等于 36Gi。
  • Pod 的总数小于或等于 5。

您可以将配额指定为 数量

Cohort

资源

在 ClusterQueue 中,您可以为多个 计算资源(CPU、内存、GPU、Pod 等)定义配额。

对于每个资源,您可以为多个类型定义配额。类型表示资源的不同变体(例如,不同的 GPU 型号)。您可以使用 ResourceFlavor 对象 定义类型。

在为 ClusterQueue 定义配额时,您可以设置以下值

  • nominalQuota 是在特定时间可用于 ClusterQueue 的此资源的数量。
  • borrowingLimit 是此 ClusterQueue 允许从同一 队列 中其他 ClusterQueue 的未使用名义配额中借用的最大配额量。
  • lendingLimit 是此 ClusterQueue 允许队列中的其他 ClusterQueue 在此 ClusterQueue 未使用其名义配额时借用的最大配额量。

在称为 准入 的过程中,Kueue 为 工作负载 Pod 集 分配每个 Pod 集请求的资源的类型。Kueue 分配 ClusterQueue 的 .spec.resourceGroups[*].flavors 列表中的第一个类型,该类型在 ClusterQueue 或 ClusterQueue 的 队列 中有足够的未使用 nominalQuota 配额。

由于 pods 资源名称是 保留的,并且其值是由 Kueue 在 准入 期间计算的,而不是由 批处理用户 提供的,因此 批处理管理员 可以使用它来限制同时允许的零或非常小的资源请求工作负载的数量。

资源组

ClusterQueue 中的多个资源可能具有相同的类型。这对于 cpumemory 来说很常见,其中类型通常与机器系列或虚拟机可用性策略相关联。若要将两个或更多资源绑定到同一组类型,您可以将它们列在同一资源组中。

具有多个资源组的 ClusterQueue 的示例如下所示

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {} # match all.
  resourceGroups:
  - coveredResources: ["cpu", "memory", "pods"]
    flavors:
    - name: "spot"
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 36Gi
      - name: "pods"
        nominalQuota: 50
    - name: "on-demand"
      resources:
      - name: "cpu"
        nominalQuota: 18
      - name: "memory"
        nominalQuota: 72Gi
      - name: "pods"
        nominalQuota: 100
  - coveredResources: ["gpu"]
    flavors:
    - name: "vendor1"
      resources:
      - name: "gpu"
        nominalQuota: 10
    - name: "vendor2"
      resources:
      - name: "gpu"
        nominalQuota: 10

在上面的示例中,cpumemory 属于一个资源组,而 gpu 属于另一个资源组。

资源类型必须属于至多一个资源组。

命名空间选择器

您可以通过在 .spec.namespaceSelector 字段中设置 标签选择器来限制哪些命名空间可以在 ClusterQueue 中接纳工作负载。

要允许来自所有命名空间的工作负载,请将空选择器 {} 设置为 spec.namespaceSelector 字段。

有多种方法可以允许特定命名空间访问 Cluster Queue。使用 matchLabels 将工作负载与命名空间 team-a 匹配的示例 namespaceSelector 如下所示

namespaceSelector:
  matchLabels:
    kubernetes.io/metadata.name: team-a

此处 kubernetes.io/metadata.name: team-a 指的是 Kubernetes 控制平面在所有命名空间上设置的不可变标签 kubernetes.io/metadata.name。标签的值是命名空间名称,在本例中为 team-a

但是,matchLabels 可以采用与 Namespace 对象中存在的标签匹配的任何键。例如,假设 team-ateam-b 在队列 team-a-b 中,并且用户定义的标签 research-cohort: team-a-b 存在于这两个命名空间中,如下所示

apiVersion: v1
kind: Namespace
metadata:
  name: team-a
  labels:
    research-cohort: team-a-b
apiVersion: v1
kind: Namespace
metadata:
  name: team-b
  labels:
    research-cohort: team-a-b

允许这两个命名空间将作业提交到此 ClusterQueue 的 namespaceSelector 配置如下所示

namespaceSelector:
  matchLabels:
    research-cohort: team-a-b

配置 namespaceSelector 的另一种方法是使用 matchExpressions。有关更多详细信息,请参阅 Kubernetes 文档

排队策略

您可以使用 .spec.queueingStrategy 字段在 ClusterQueue 中设置不同的排队策略。排队策略决定了工作负载在 ClusterQueue 中的排序方式以及在 接纳尝试失败后如何重新排队。

以下为受支持的排队策略

  • StrictFIFO:工作负载首先按优先级排序,然后按.metadata.creationTimestamp排序。无法准入的较旧工作负载会阻止较新的工作负载,即使较新的工作负载符合可用配额。
  • BestEffortFIFO:工作负载的排序方式与StrictFIFO相同。但是,无法准入的较旧工作负载不会阻止符合可用配额的较新的工作负载。

默认队列策略为BestEffortFIFO

队列组

ClusterQueues 可以分组为队列组。属于同一队列组的 ClusterQueues 可以相互借用未使用的配额。

要将 ClusterQueue 添加到队列组,请在.spec.cohort字段中指定队列组的名称。所有具有匹配spec.cohort的 ClusterQueues 都属于同一队列组。如果spec.cohort字段为空,则 ClusterQueue 不属于任何队列组,因此无法从任何其他 ClusterQueue 借用配额。

类型和借用语义

当 ClusterQueue 是队列组的一部分时,Kueue 满足以下准入语义

  • 在分配类型时,Kueue 会遍历 ClusterQueue 的相关 ResourceGroup 中类型的列表(.spec.resourceGroups[*].flavors)。对于每种类型,Kueue 会尝试根据 ClusterQueue 中为该类型定义的配额和队列组中未使用的配额来适应工作负载的 Pod 集。如果工作负载不适合,Kueue 会评估列表中的下一种类型。
  • 如果资源的总请求
    1. 小于或等于 ClusterQueue 中该类型的未使用的nominalQuota;或
    2. 小于或等于队列组中 ClusterQueues 中该类型的未使用的nominalQuota总和,并且
    3. 小于或等于 ClusterQueue 中该类型的未使用的nominalQuota + borrowingLimit。在 Kueue 中,当满足 (2) 和 (3) 但不满足 (1) 时,这称为借用配额
  • ClusterQueue 只能借用 ClusterQueue 定义的类型的配额。
  • 对于工作负载中的每个 Pod 集资源,ClusterQueue 只能借用一种类型的配额。

注意:在队列中,Kueue 优先调度可以满足 nominalQuota 的工作负载。默认情况下,如果多个工作负载需要 借用,Kueue 会先尝试调度具有较高 优先级 的工作负载。如果设置了功能闸门 PrioritySortingWithinCohort=false,Kueue 会尝试调度具有最早 .metadata.creationTimestamp 的工作负载。

您可以通过在 ClusterQueue 中设置 flavorFungibility 来影响风味选择和借用的某些语义。

借用示例

假设您创建了以下两个 ClusterQueue

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu", "memory"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 36Gi
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-b-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu", "memory"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 12
      - name: "memory"
        nominalQuota: 48Gi

ClusterQueue team-a-cq 可以根据以下场景接纳工作负载

  • 如果 ClusterQueue team-b-cq 没有接纳任何工作负载,则 ClusterQueue team-a-cq 可以接纳资源总计为 12+9=21 个 CPU 和 48+36=84Gi 内存的工作负载。
  • 如果 ClusterQueue team-b-cq 有待处理的工作负载,并且 ClusterQueue team-a-cq 已用完其全部 nominalQuota 配额,则 Kueue 会在 team-a-cq 中接纳任何新工作负载之前,在 ClusterQueue team-b-cq 中接纳工作负载。因此,Kueue 确保满足 team-b-cqnominalQuota 配额。

借用限制

要限制 ClusterQueue 可以从其他 ClusterQueue 借用的资源数量,您可以设置 .spec.resourcesGroup[*].flavors[*].resource[*].borrowingLimit 数量 字段。

例如,假设您创建了以下两个 ClusterQueue

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu", "memory"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 9
        borrowingLimit: 1
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-b-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu", "memory"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 12

在这种情况下,由于我们在 ClusterQueue team-a-cq 中设置了 borrowingLimit,如果 ClusterQueue team-b-cq 没有接纳任何工作负载,则 ClusterQueue team-a-cq 可以接纳资源总计为 9+1=10 个 CPU 的工作负载。

如果对于给定的风味/资源,borrowingLimit 字段为空或为 null,则 ClusterQueue 可以借用队列中所有 ClusterQueue 的名义配额之和。因此,对于上面列出的 yamls,team-b-cq 最多可以使用 12+9 个 CPU。

LendingLimit

要限制 ClusterQueue 在队列中可以借用的资源数量,您可以设置 .spec.resourcesGroup[*].flavors[*].resource[*].lendingLimit 数量 字段。

功能状态 Kueue v0.6 起的 alpha

例如,假设您创建了以下两个 ClusterQueue

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 9
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-b-cq"
spec:
  namespaceSelector: {} # match all.
  cohort: "team-ab"
  resourceGroups:
  - coveredResources: ["cpu"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 12
        lendingLimit: 1

在此,您在 ClusterQueue team-b-cq 中设置 lendingLimit=1。这意味着,如果 ClusterQueue team-b-cq 中所有已准入的工作负载的总配额使用量低于 nominalQuota(小于或等于 12-1=11 个 CPU),则 ClusterQueue team-a-cq 可以准入资源总计为 9+1=10 个 CPU 的工作负载。

如果未指定 lendingLimit 字段,则 ClusterQueue 可以借出其所有资源。在这种情况下,team-b-cq 最多可以使用 9+12 个 CPU。

抢占

当 ClusterQueue 或其队列中没有足够的配额时,传入的工作负载可以根据 ClusterQueue 的策略触发对先前准入的工作负载的抢占。

启用抢占的 ClusterQueue 配置如下所示

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  preemption:
    reclaimWithinCohort: Any
    borrowWithinCohort:
      policy: LowerPriority
      maxPriorityThreshold: 100
    withinClusterQueue: LowerPriority

以上字段执行以下操作

  • reclaimWithinCohort 确定待处理的工作负载是否可以抢占队列中其他 ClusterQueue 中使用量超过其名义配额的工作负载。可能的值有

    • Never(默认):不抢占队列中的工作负载。
    • LowerPriority:如果待处理的工作负载符合其 ClusterQueue 的名义配额,则仅抢占队列中优先级低于待处理工作负载的工作负载。
    • Any:如果待处理的工作负载符合其 ClusterQueue 的名义配额,则抢占队列中的任何工作负载,无论优先级如何。
  • borrowWithinCohort 确定待处理的工作负载在需要借用时是否可以抢占其他 ClusterQueue 中的工作负载。此字段需要指定可能的子字段 policy,其值如下

    • Never(默认):如果需要借用,请不要抢占队列中的工作负载。
    • LowerPriority:如果待处理的工作负载需要借用,则仅抢占优先级低于待处理工作负载的队列中的工作负载。仅当启用 reclaimWithinCohort(不同于 Never)时才支持此抢占策略。此外,在这种情况下,只有优先级达到 maxPriorityThreshold 指示的优先级的负载才能被抢占。
  • withinClusterQueue 确定不符合其 ClusterQueue 的标称配额的待处理工作负载是否可以抢占 ClusterQueue 中的活动工作负载。可能的值为

    • Never(默认):不要抢占 ClusterQueue 中的工作负载。
    • LowerPriority:仅抢占优先级低于待处理工作负载的 ClusterQueue 中的工作负载。
    • LowerOrNewerEqualPriority:仅抢占 ClusterQueue 中优先级低于待处理工作负载或优先级相等且比待处理工作负载新的工作负载。

请注意,传入的工作负载可以抢占 ClusterQueue 和队列中的工作负载。

阅读 抢占 以了解 Kueue 为尽可能少地抢占工作负载而实施的启发式方法。

FlavorFungibility

当 ResourceFlavor 中没有足够的标称资源配额时,传入的工作负载可以借用配额或抢占 ClusterQueue 或 Cohort 中正在运行的工作负载。

Kueue 按顺序评估 ClusterQueue 中的 flavor。您可以通过设置 flavorFungibility 字段来影响在尝试在下一个 flavor 中容纳工作负载之前,是优先考虑抢占还是借用。

配置此行为的 ClusterQueue 如下所示

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  flavorFungibility:
    whenCanBorrow: TryNextFlavor
    whenCanPreempt: Preempt

以上字段执行以下操作

  • whenCanBorrow 确定工作负载是否应停止查找更好的分配,如果它可以通过在当前 ResourceFlavor 中借用获得足够的资源。可能的值为
    • Borrow(默认):ClusterQueue 停止查找更好的分配。
    • TryNextFlavor:ClusterQueue 尝试下一个 ResourceFlavor 以查看工作负载是否可以获得更好的分配。
  • whenCanPreempt 确定工作负载是否应在尝试下一个之前在当前 ResourceFlavor 中尝试抢占。可能的值为
    • Preempt:如果抢占失败,ClusterQueue 将停止在当前 ResourceFlavor 中尝试抢占并从下一个开始。
    • TryNextFlavor(默认):ClusterQueue 尝试下一个 ResourceFlavor 以查看工作负载是否可以适合 ResourceFlavor。

默认情况下,如果工作负载可以获得足够的借用资源,则传入的工作负载将停止尝试下一个 flavor。并且 Kueue 仅在 Kueue 确定剩余的 ResourceFlavor 无法容纳工作负载后才触发抢占。

请注意,只要有可能并且配置的策略允许,如果 Kueue 可以通过借用来容纳工作负载,它就会避免抢占。

StopPolicy

StopPolicy 允许集群管理员通过在 spec 中设置其值来临时停止 ClusterQueue 中工作负载的准入,如下所示

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "team-a-cq"
spec:
  stopPolicy: Hold

上面的示例将停止 ClusterQueue 中新工作负载的准入,同时允许已准入的工作负载完成。HoldAndDrain 将产生类似的效果,但除此之外,它还将触发已准入工作负载的驱逐。

如果设置为 None 或删除 spec.stopPolicy,ClusterQueue 将恢复正常准入行为。

AdmissionChecks

AdmissionChecks 是一种机制,允许 Kueue 在准入工作负载之前考虑其他标准。

有关使用准入检查的示例 ClusterQueue 配置,请参阅 准入检查

下一步是什么?