标签AWS EKS GPU配额管理 Kueue Gang Scheduling 多租户调度

大家好,我是 [锅巴王子]。在前两篇实战中,我们搞定了 EKS 的多机多卡底层通信(Training Operator)以及硬件级监控(Observability)。但在真实的企业环境中,当多个算法团队(如 NLP 组、CV 组)共用一个集群时,最棘手的问题往往是“算力内卷”: 大家都在无序提交任务,导致谁都凑不够起步所需的 GPU 卡数,全部死锁在 Pending 状态,一边算不出结果,一边狂烧云服务器的账单。

今天,我们来补齐云原生 AI 架构的最后一块核心拼图:Amazon SageMaker HyperPod 任务治理 (Job Governance)。我们将基于开源 Kueue 引擎,实战演练如何实现“精准的多租户配额管理”以及“残酷但必须的优先级抢占(Preemption)”。


核心设计理念:Kueue 的“三权分立”

在原生的 K8s 中,Pod 是一个个调度的。而加入了任务治理后,我们引入了 Gang Scheduling(全有或全无调度),并且通过三个维度的资源来进行精准匹配:

  1. ClusterQueue (全局总金库):定义集群总共的 GPU 资产池,并制定全局的“抢占规则”。

  2. LocalQueue (部门财务室):建在各团队专属的 Namespace 下,从总金库认领属于自己的配额。

  3. PriorityClass (任务军衔):定义任务的轻重缓急。注意:优先级是跟着“任务(Job)”走的,而不是跟着“队列(Queue)”走的。


实战步骤:配置队列与优先级体系

假设我们有一个 32 张 A100 的节点池,现在要为 NLP 团队配置队列,并允许“老板的高优任务”插队。

Step 1: 制定军衔级别 (PriorityClass)

我们需要在 K8s 全局声明哪些是高优,哪些是普通。新建 priorities.yaml

# 1. 定义最高优的“老板加急”军衔
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: boss-urgent
value: 1000000  # 数值越大,优先级越高
globalDefault: false
description: "核心任务,允许插队杀掉其他任务"
---
# 2. 定义普通的“日常训练”军衔
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: normal-batch
value: 1000
globalDefault: true # 所有任务默认是这个级别
description: "普通任务,资源不足时乖乖排队"

执行:kubectl apply -f priorities.yaml

Step 2: 建立总金库并开启“抢占” (ClusterQueue)

新建 cluster-queue.yaml。这里最关键的是 preemption 字段,它赋予了高优任务“杀富济贫”的权力。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: hyperpod-global-queue
spec:
  namespaceSelector: {} 
  # 【核心配置:开启优先级抢占】
  preemption:
    reclaimWithinCohort: Any
    withinClusterQueue: LowerPriority # 允许高优任务挤掉当前队列里的低优任务
  resourceGroups:
  - coveredResources: ["nvidia.com/gpu"]
    flavors:
    - name: "p4d-flavor"
      resources:
      - name: "nvidia.com/gpu"
        nominalQuota: 32  # 集群总资产

Step 3: 建立团队专属窗口 (LocalQueue)

新建 nlp-local-queue.yaml,将其绑定到 NLP 团队的 Namespace。

apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: nlp-team-queue
  namespace: nlp-team  # 【精准匹配】建在 NLP 组的专属命名空间里
spec:
  clusterQueue: hyperpod-global-queue # 指向全局总账本

执行应用:kubectl apply -f cluster-queue.yaml -f nlp-local-queue.yaml


终极对决:算法工程师提交高优任务

现在,所有规矩都定好了。首席科学家要提交一个需要 16 张 A100 的大模型微调任务。他需要在 PyTorchJob 中同时亮出“部门印章”“高优军衔”:

apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
  name: boss-urgent-llama-finetune
  namespace: nlp-team
  labels:
    # 【盖部门公章】:指定去 NLP 团队的队列排队
    kueue.x-k8s.io/queue-name: nlp-team-queue 
spec:
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      template:
        spec:
          # 【亮明军衔】:声明我是高优任务!
          priorityClassName: boss-urgent
          containers:
          - name: pytorch
            image: 你的镜像地址
            # ... 省略 GPU 申请等其他配置 ...

💥 魔法时刻:真实的抢占过程 (Preemption)

  1. 此时如果 32 张显卡正被 4 个普通任务 (normal-batch) 占满。

  2. 军法官 (Kueue) 识别到 boss-urgent 任务进场,发现没卡了。

  3. 它会立刻处决(发送 SIGTERM 强杀)其中 2 个普通任务,将它们踢回 Pending 状态。

  4. 空出 16 张卡后,高优任务瞬间拉起 (Running)。等高优任务跑完,那两个被杀掉的普通任务才会被重新恢复执行。

至此,一个从底层通信到上层配额治理的云原生 AI 算力底座,彻底搭建完毕!


附录:实战架构答疑 (Q&A)

在内部评审这套架构时,运维团队通常会有以下几个核心疑问:

Q1:配置这么复杂,Kueue 是如何实现“精准匹配”到不同团队的? A1:它通过“三点一线”的逻辑实现强隔离:

  1. LocalQueue 必须创建在明确的 Namespace(如 nlp-team)下。

  2. 算法工程师提交的任务,必须部署在这个 nlp-team 命名空间中。

  3. 任务的 YAML 里必须打上 Label (kueue.x-k8s.io/queue-name: nlp-team-queue)。 只有 Namespace 身份和 Label 公章完全对齐,Kueue 才会向总金库申请 GPU。随便写别人队列的名字是无法通过权限校验的。

Q2:既然有这么严格的排队机制,那我普通的 Java 后端、前端 Node.js 的 Pod,也要打排队标签吗? A2:不需要。 Kueue 默认只拦截“批处理/AI 计算任务”(如 PyTorchJob, Job 等)。你部署的普通微服务 Deployment,Kueue 根本不会拦截,直接走原生调度,不需要打任何 Tag。

Q3:如果不打 Tag,普通的 Java 微服务岂不是会偷偷跑去占用极其昂贵的 A100 显卡机器? A3:不会,靠“污点(Taints)”防线拦截。 在生产环境中,我们会对昂贵的 GPU 物理机打上污点(nvidia.com/gpu=present:NoSchedule)。 普通的 Java 业务 Pod 默认没有配置“容忍度(Tolerations)”,调度器死都不会把它们扔到 GPU 机器上。只有配置了对应容忍度的 PyTorchJob,才能落在这类机器上。这就实现了“同集群下,Web 业务与 AI 算力的完美物理隔离”。

更多推荐