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 支持

快速创建单节点集群的命令如下:

bash 复制代码
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

集群起来后确认版本:

bash 复制代码
$ 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:

bash 复制代码
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 组件正常运行:

bash 复制代码
$ 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:

bash 复制代码
$ kubectl get node lixd-test-gpu -o jsonpath='{.metadata.labels.nvidia\.com/gpu\.product}{"\n"}'
Tesla-T4

1.3 安装 Kueue

Kueue 使用 0.18.1,安装方式和前两篇一样:

bash 复制代码
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 包:

bash 复制代码
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:

bash 复制代码
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 组件:

bash 复制代码
$ 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:

bash 复制代码
$ 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:

bash 复制代码
$ 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 里扣配额用的是资源名,所以这里需要做一次映射:

yaml 复制代码
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 状态会看得很清楚。

yaml 复制代码
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,识别里面的 deviceClassNamecount,再通过 deviceClassMappings 折算成 ClusterQueue 里的配额资源。

本文不展开 extended resource 路径,避免把两种 DRA 接入方式混在一起。

先创建 ResourceClaimTemplate:

yaml 复制代码
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

这里有两个细节容易混:

  • deviceClassNamegpu.nvidia.com,不是 nvidia.com/gpu
  • ExactCount + count: 1 表示申请 1 张整卡

再提交 Kueue 管理的 Job:

yaml 复制代码
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

bash 复制代码
$ 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 已经分配:

bash 复制代码
$ kubectl get resourceclaim -n p3-dra-demo
NAME                                                        STATE
kueue-dra-running-a-kj7nw-gpu-claim-c6j4j                   allocated,reserved

容器里能看到 T4:

bash 复制代码
$ 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 的准入结果:

yaml 复制代码
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 配额已经被扣掉了:

yaml 复制代码
status:
  admittedWorkloads: 1
  flavorsUsage:
  - name: p3-t4
    resources:
    - name: nvidia.com/gpu
      total: "1"

2.5 第二个整卡 Job 会被 Kueue 卡住

队列里只有 1 张 GPU 配额。如果再提交一个同样申请 single-gpu 的 Job:

text 复制代码
kueue-dra-running-a:1 x gpu.nvidia.com
kueue-dra-pending-b:1 x gpu.nvidia.com
bash 复制代码
$ 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 一直起不来:

yaml 复制代码
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 里写的是:

yaml 复制代码
deviceClassName: gpu.nvidia.com

ClusterQueue 里写的是:

yaml 复制代码
coveredResources:
- nvidia.com/gpu

两者靠 Kueue 配置里的 deviceClassMappings 关联起来。少了这段映射,Workload 会被标成 Inadmissible,原因类似:

text 复制代码
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 模板:

yaml 复制代码
resourceClaims:
- name: gpu-claim
  resourceClaimTemplateName: single-gpu

容器里再通过同一个 gpu-claim 关联到这次申请到的设备:

yaml 复制代码
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 是否还能继续管理显存和算力配额。

5. 参考文章

相关推荐
EterNity_TiMe_2 小时前
别让告警变成噪音:用Prometheus和Alertmanager搭一套可远程查看的监控系统
数据库·人工智能·ai·postgresql·prometheus·cpolar
曦尧2 小时前
RuView:用普通 WiFi 信号实现穿墙空间感知与生命体征监测
ai·自动化
Aray12343 小时前
论服务网格在云原生微服务项目中的设计与落地实践
微服务·云原生·架构
Zhao_yani3 小时前
k8s基础知识
云原生·容器·kubernetes
doiito4 小时前
Gliding Horse 时间感知系统:让 AI Agent 真正“感知“时间流逝
ai·rust·架构设计·系统设计·ai agent
小白跃升坊4 小时前
1Panel企业版+AI门户|从零构建企业专属AI门户
人工智能·ai·1panel·skills·ai运维·ai门户
styshoo4 小时前
NVSentinel 数据流梳理
kubernetes·gpu·ai-infra·nvsentinel
日志易4 小时前
日志易5.6 AI特别版:构建行业知识库,全面升级智能运维体验
ai·智能运维·日志易
运维大师4 小时前
【云原生与DevOps】07-Istio服务网格落地:从试点到全量的踩坑记录
云原生·istio·devops