Kueue 如何管理 DRA 模式下的 GPU 配额

前面两篇:Kubernetes 官方出品:一个 Controller 搞定 Job 排队和资源配额 和 终于搞懂 Kueue:5 个核心对象一次讲透 把 Kueue 的基本玩法和核心对象走了一遍,不过 Demo 都跑在 CPU 上。
真到 GPU 集群里,大家更关心的是另一个问题:
Kueue 能不能管理 DRA 模式下的 GPU?
这一篇就把它跑通。NVIDIA DRA Driver 负责把整卡 GPU 发布成 DeviceClass / ResourceSlice,Kueue 在 Job 准入阶段读取 DRA 设备申请,判断这个 Job 能不能进入队列。
对 DRA 不熟悉的同学,可以先看这篇:DRA P1:DRA 能解决什么问题?从部署到使用的完整体验
1. 环境准备
环境准备分几段走:先创建 K8s 集群,再用 GPU Operator 把 GPU Driver / Container Runtime 准备好,最后装 Kueue 和 NVIDIA DRA Driver。
1.1 创建 Kubernetes 集群
集群使用 KubeClipper 创建。KubeClipper 1.6.0 默认支持 Kubernetes 1.36.1、containerd 2.2.4,和本文环境一致,详细步骤可以参考:KubeClipper 1.6.0 发布:kcctl 优化与 K8s 1.36 支持。
快速创建单节点集群的命令如下:
curl -sfL https://oss.kubeclipper.io/get-kubeclipper.sh | KC_REGION=cn bash -
kcctl deploy
kcctl create cluster --name demo --master <NODE_IP> --untaint-master
集群起来后确认版本:
$ kubectl get node -owide
NAME STATUS ROLES VERSION CONTAINER-RUNTIME
lixd-test-gpu Ready control-plane v1.36.1 containerd://2.2.4
1.2 GPU 环境准备(GPU Operator)
GPU Driver、NVIDIA Container Toolkit 等基础组件使用 GPU Operator 安装。完整说明可以参考之前这篇:GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建。
本文后面会安装 NVIDIA DRA Driver,所以安装 GPU Operator 时需要关闭 DevicePlugin:
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update
helm upgrade --install --wait gpu-operator \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--version=v26.3.1 \
--set driver.enabled=true \
--set devicePlugin.enabled=false
--set devicePlugin.enabled=false:关闭 DevicePlugin,避免与后续安装的 DRA Driver 冲突。
安装完成后,确认 GPU Operator 组件正常运行:
$ kubectl get pods -n gpu-operator
NAME READY STATUS
gpu-operator-7bcbd487f5-h8dsd 1/1 Running
nvidia-container-toolkit-daemonset-zbpbv 1/1 Running
nvidia-operator-validator-8l2r5 1/1 Running
再确认节点能看到 GPU:
$ kubectl get node lixd-test-gpu -o jsonpath='{.metadata.labels.nvidia\.com/gpu\.product}{"\n"}'
Tesla-T4
1.3 安装 Kueue
Kueue 使用 0.18.1,安装方式和前两篇一样:
helm install kueue oci://registry.k8s.io/kueue/charts/kueue \
--version=0.18.1 \
--namespace kueue-system \
--create-namespace \
--wait --timeout 300s
如果测试环境访问 registry.k8s.io 不稳定,也可以用 GitHub Release 里的 chart 包:
helm install kueue \
https://github.com/kubernetes-sigs/kueue/releases/download/v0.18.1/kueue-0.18.1.tgz \
--namespace kueue-system \
--create-namespace \
--wait --timeout 300s
1.4 接入 NVIDIA DRA Driver
最后安装 NVIDIA DRA Driver 25.12.0:
helm upgrade --install nvidia-dra-driver-gpu \
nvidia/nvidia-dra-driver-gpu \
--version 25.12.0 \
-n nvidia-dra-driver-gpu \
--set gpuResourcesEnabledOverride=true \
--create-namespace \
--wait --timeout 300s
安装完成后,先看 DRA Driver 组件:
$ kubectl get pods -n nvidia-dra-driver-gpu
NAME READY STATUS
nvidia-dra-driver-gpu-controller-76864c66cb-s8tch 1/1 Running
nvidia-dra-driver-gpu-kubelet-plugin-grkbg 2/2 Running
再看 DeviceClass:
$ kubectl get deviceclass
NAME AGE
compute-domain-daemon.nvidia.com 2m47s
compute-domain-default-channel.nvidia.com 2m47s
gpu.nvidia.com 2m47s
mig.nvidia.com 2m47s
vfio.gpu.nvidia.com 2m47s
整卡调度用的是 gpu.nvidia.com。对应的 ResourceSlice 里能看到节点上的 T4:
$ kubectl get resourceslice
NAME NODE DRIVER POOL
lixd-test-gpu-gpu.nvidia.com-p8cdq lixd-test-gpu gpu.nvidia.com lixd-test-gpu
后面的 Job 会直接引用 gpu.nvidia.com 这个 DeviceClass,实际可分配设备则来自这些 ResourceSlice。
2. Demo:使用 Kueue 调度 DRA 模式下的 GPU
2.1 修改 Kueue DRA 配置
DRA 和 Kueue 使用的资源名称并不是同一个。ResourceClaimTemplate 里写的是 deviceClassName: gpu.nvidia.com,而 ClusterQueue 里扣配额用的是资源名,所以这里需要做一次映射:
apiVersion: config.kueue.x-k8s.io/v1beta2
kind: Configuration
integrations:
frameworks:
- batch/job
resources:
deviceClassMappings:
- name: nvidia.com/gpu
deviceClassNames:
- gpu.nvidia.com
这段配置的意思是:只要 Workload 通过 ResourceClaimTemplate 申请 gpu.nvidia.com 这个 DeviceClass,Kueue 就把它折算成 nvidia.com/gpu 这个逻辑资源来扣配额。

修改 Kueue manager config 后,需要重启 kueue-controller-manager 让新配置生效。
2.2 创建队列
这次只用一个队列,GPU 配额只给 1 张 T4。这样后面再提交第二个 Job 时,Pending 状态会看得很清楚。
apiVersion: v1
kind: Namespace
metadata:
name: p3-dra-demo
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
name: p3-t4
spec:
nodeLabels:
nvidia.com/gpu.product: Tesla-T4
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
metadata:
name: p3-dra-cq
spec:
namespaceSelector: {}
resourceGroups:
- coveredResources:
- cpu
- memory
- nvidia.com/gpu
flavors:
- name: p3-t4
resources:
- name: cpu
nominalQuota: "8"
- name: memory
nominalQuota: 16Gi
- name: nvidia.com/gpu
nominalQuota: 1
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
metadata:
name: p3-dra-queue
namespace: p3-dra-demo
spec:
clusterQueue: p3-dra-cq
注意这里的 coveredResources 里包含 nvidia.com/gpu。这是映射后的逻辑资源名,不是 Pod 里直接写的扩展资源。
2.3 用 ResourceClaimTemplate 申请整卡
使用 DRA 之后,Job 不再写 resources.limits.nvidia.com/gpu: 1,而是引用一个独立的 ResourceClaimTemplate。
这里 Kueue 做的事情很直接:读取 Workload 引用的 ResourceClaimTemplate,识别里面的 deviceClassName 和 count,再通过 deviceClassMappings 折算成 ClusterQueue 里的配额资源。
本文不展开 extended resource 路径,避免把两种 DRA 接入方式混在一起。
先创建 ResourceClaimTemplate:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: single-gpu
namespace: p3-dra-demo
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.nvidia.com
allocationMode: ExactCount
count: 1
这里有两个细节容易混:
deviceClassName是gpu.nvidia.com,不是nvidia.com/gpuExactCount + count: 1表示申请 1 张整卡
再提交 Kueue 管理的 Job:
apiVersion: batch/v1
kind: Job
metadata:
name: kueue-dra-running-a
namespace: p3-dra-demo
labels:
kueue.x-k8s.io/queue-name: p3-dra-queue
spec:
parallelism: 1
completions: 1
suspend: true
template:
spec:
resourceClaims:
- name: gpu-claim
resourceClaimTemplateName: single-gpu
restartPolicy: Never
containers:
- name: cuda-test
image: nvcr.io/nvidia/cuda:12.6.0-base-ubuntu24.04
command: ["bash", "-c", "nvidia-smi -L && echo KUEUE_DRA_RUNNING_A && sleep 3600"]
resources:
claims:
- name: gpu-claim
Job 被 Kueue 准入后,会从 Suspended 变成 Running:
$ kubectl get job,pod,workload -n p3-dra-demo
NAME STATUS COMPLETIONS
job.batch/kueue-dra-running-a Running 0/1
NAME READY STATUS
pod/kueue-dra-running-a-kj7nw 1/1 Running
NAME QUEUE RESERVED IN ADMITTED
workload.kueue.x-k8s.io/job-kueue-dra-running-a-c3b3c p3-dra-queue p3-dra-cq True
ResourceClaim 已经分配:
$ kubectl get resourceclaim -n p3-dra-demo
NAME STATE
kueue-dra-running-a-kj7nw-gpu-claim-c6j4j allocated,reserved
容器里能看到 T4:
$ kubectl logs -n p3-dra-demo -l job-name=kueue-dra-running-a
GPU 0: Tesla T4 (UUID: GPU-6d8fdeb7-6247-e04e-81a7-7b42dc45180c)
KUEUE_DRA_RUNNING_A
2.4 看 Kueue 是否真的扣了 GPU 配额
直接看 Workload 的准入结果:
status:
admission:
clusterQueue: p3-dra-cq
podSetAssignments:
- count: 1
flavors:
nvidia.com/gpu: p3-t4
resourceUsage:
nvidia.com/gpu: "1"
这说明 deviceClassMappings 已经生效:用户写的是 ResourceClaimTemplate,Kueue 扣的是 nvidia.com/gpu 这个逻辑配额。
再看看 ClusterQueue,可以发现 GPU 配额已经被扣掉了:
status:
admittedWorkloads: 1
flavorsUsage:
- name: p3-t4
resources:
- name: nvidia.com/gpu
total: "1"
2.5 第二个整卡 Job 会被 Kueue 卡住
队列里只有 1 张 GPU 配额。如果再提交一个同样申请 single-gpu 的 Job:
kueue-dra-running-a:1 x gpu.nvidia.com
kueue-dra-pending-b:1 x gpu.nvidia.com
$ kubectl get job,workload -n p3-dra-demo
NAME STATUS
job.batch/kueue-dra-running-a Running
job.batch/kueue-dra-pending-b Suspended
NAME QUEUE RESERVED IN ADMITTED
workload.kueue.x-k8s.io/job-kueue-dra-running-a-c3b3c p3-dra-queue p3-dra-cq True
workload.kueue.x-k8s.io/job-kueue-dra-pending-b-494a3 p3-dra-queue
再看 Workload 状态,就能发现为什么第二个 Job 一直起不来:
status:
conditions:
- reason: Pending
message: 'couldn''t assign flavors to pod set main: insufficient unused quota
for nvidia.com/gpu in flavor p3-t4, 1 more needed'
resourceRequests:
- name: main
resources:
nvidia.com/gpu: "1"
3. 几个容易踩坑的点
DeviceClass 和配额资源不是同一个东西
ResourceClaimTemplate 里写的是:
deviceClassName: gpu.nvidia.com
ClusterQueue 里写的是:
coveredResources:
- nvidia.com/gpu
两者靠 Kueue 配置里的 deviceClassMappings 关联起来。少了这段映射,Workload 会被标成 Inadmissible,原因类似:
DeviceClass gpu.nvidia.com is not mapped in DRA configuration
ResourceClaimTemplate 要单独创建
这两个名字最容易看混:
single-gpu:前面单独创建的ResourceClaimTemplate,定义“我要 1 张gpu.nvidia.com整卡”。gpu-claim:Pod 里的本地 claim 名字,后面容器通过它来使用 GPU。
Pod 里这段不是重新定义一个模板,而是引用已经存在的 single-gpu 模板:
resourceClaims:
- name: gpu-claim
resourceClaimTemplateName: single-gpu
容器里再通过同一个 gpu-claim 关联到这次申请到的设备:
resources:
claims:
- name: gpu-claim
所以完整关系是:ResourceClaimTemplate(single-gpu) -> Pod resourceClaims(gpu-claim) -> container resources.claims(gpu-claim)。
Kueue 准入不等于设备已经分配
这里要注意一点:Kueue 只是负责“准入”,真正把 GPU 分配给 Pod 的还是 kube-scheduler 和 DRA Driver。
所以生产环境里建议同时关注 ResourceClaim 状态和 Pod 状态。如果希望 Workload 在 Pod 长时间起不来时释放 Kueue 配额,可以结合 waitForPodsReady 做保护。
4. 小结
到这里可以看到,Kueue 并没有直接参与 GPU 分配,而是站在 Job 准入这一层,通过 deviceClassMappings 把 DRA 的设备申请转换成队列里的配额资源。这样既保留了 DRA 的设备模型,也让 GPU 可以继续纳入 Kueue 的统一配额管理。
| 组件 | 负责什么 |
|---|---|
| NVIDIA DRA Driver | 把 GPU 作为 gpu.nvidia.com DeviceClass / ResourceSlice 发布出来 |
| Kueue | 通过 deviceClassMappings 把 DRA 设备折算成 nvidia.com/gpu 配额 |
| kube-scheduler | 在 Pod 调度阶段完成 ResourceClaim 的实际设备分配 |
下一篇继续往前走一步,把 HAMi 引入进来:一张 GPU 被切成多份 vGPU 之后,Kueue 是否还能继续管理显存和算力配额。

浙公网安备 33010602011771号