【云原生 AI 实战(三)最终版】告别算力死锁!EKS 多租户 GPU 调度与优先级抢占完全指南
标签: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(全有或全无调度),并且通过三个维度的资源来进行精准匹配:
-
ClusterQueue(全局总金库):定义集群总共的 GPU 资产池,并制定全局的“抢占规则”。 -
LocalQueue(部门财务室):建在各团队专属的 Namespace 下,从总金库认领属于自己的配额。 -
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)
-
此时如果 32 张显卡正被 4 个普通任务 (
normal-batch) 占满。 -
军法官 (Kueue) 识别到
boss-urgent任务进场,发现没卡了。 -
它会立刻处决(发送 SIGTERM 强杀)其中 2 个普通任务,将它们踢回
Pending状态。 -
空出 16 张卡后,高优任务瞬间拉起 (
Running)。等高优任务跑完,那两个被杀掉的普通任务才会被重新恢复执行。
至此,一个从底层通信到上层配额治理的云原生 AI 算力底座,彻底搭建完毕!
附录:实战架构答疑 (Q&A)
在内部评审这套架构时,运维团队通常会有以下几个核心疑问:
Q1:配置这么复杂,Kueue 是如何实现“精准匹配”到不同团队的? A1:它通过“三点一线”的逻辑实现强隔离:
-
LocalQueue必须创建在明确的 Namespace(如nlp-team)下。 -
算法工程师提交的任务,必须部署在这个
nlp-team命名空间中。 -
任务的 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 算力的完美物理隔离”。
更多推荐



所有评论(0)