您好,欢迎访问上海聚搜信息技术有限公司官方网站!
24小时咨询热线:4008-020-360

Kubernetes GPU调度进阶:动态资源分配

时间:2026-07-28 18:20:18 点击:

把 GPU 塞进 Kubernetes 集群跑任务不难,难的是让昂贵算力别闲着。多数团队的 GPU 利用率徘徊在 30% 出头,一张 A100 常被一个只需十分之一显存的 Pod 牢牢占住。Kubernetes GPU 动态资源分配实战要解决的正是这类看似“正常”的浪费——让调度器不再只数卡,而是真正读懂每块 GPU 的状态与需求。

一、Kubernetes GPU调度的痛点与挑战

1. 资源独占现象

独占是当前 GPU 调度最直观的绊脚石。一个 Pod 通过 nvidia.com/gpu: 1 申领整张卡,即使只跑显存 4GB 的小推理任务,其余算力和显存全部闲置。行业共识的 GPU 集群平均利用率长期低于 40%,根源就在这里。混部训练、多推理副本想要共享同一块 GPU,在传统 Device Plugin 模型下毫无办法——它只关心“卡数”,不关心“用了多少”。

2. 手动分配限制

多卡作业的部署更是一场手工排雷。不同节点可能混杂 A100 40GB、80GB 或者不同厂商 GPU,用户不得不靠 nodeSelector 硬写调度规则,把 Pod 绑死在特定型号和节点上。规模一上来,碎片化随之恶化:批次任务必须等到所有 GPU 同时就绪才能起跑,只要有一张卡被别的业务占用,整批作业就原地阻塞,调度延迟被无限放大。

3. 动态分配优势

从“算数量”转向“看属性+状态”,是动态资源分配带来的根本变化。新的 ResourceClaim 机制让 Pod 声明所需设备属性——如显存下限、NVLink 需求,调度器再根据实时可用资源匹配。这意味着 GPU 不用预先锁定,控制器可以在 Pod 绑定节点后才决定到底用哪几块卡或 MIG 实例,从根本上解耦了“请求”和“分配”,为细粒度共享、跨队列公平调度铺平道路。

二、动态资源分配(DRA)核心概念

GPU 集群长期被一个刚性规则困住:一旦 Pod 申请了 nvidia.com/gpu: 1,哪怕只用到 10% 的算力,整张卡也会被锁定。我们观察到大量生产集群中 GPU 利用率徘徊在 35%~40%,不是因为负载不足,而是传统设备分配机制强制“整卡独占”,导致碎片化严重,高吞吐的推理服务与轻量训练任务无法灵活共存。Kubernetes 社区从 v1.26 开始孵化的动态资源分配(Dynamic Resource Allocation,简称 DRA)正是在这个背景下,尝试把 GPU 调度从“数卡”推进到“看属性+控分配”的精细化阶段。

1. DRA 原理简介

过去,Device Plugin 采用集中式计数:节点上报 GPU 总量,调度器在 Pod 绑定时一次扣除请求数,之后按剩余数量决策能否分配。这种模式对同构 GPU、独占整卡场景足够简单,但一遇到 A100 40G 与 80G 混部的节点、或需要按显存大小和 NVLink 拓扑选择设备集的作业,就只能靠节点选择器手动“对号入座”,自动化程度几乎为零,跨节点多卡申请也极易因资源碎片化而整体阻塞。

DRA 的核心改变在于把“分配”这个动作从调度时硬编码的设备数,变成 运行时由资源控制器根据实时属性动态完成。工作流大致为:用户创建 ResourceClaim,描述所需设备的属性(如至少 30G 显存、支持 NVLink、可用于共享),调度器不再只盯数量,而是结合集群内实际可用的 GPU 属性与状态,为 Pod 匹配最合适的设备集;绑定节点后,由设备驱动初始化并挂载这些资源。这种时序和粒度的调整,让 GPU 请求从“必须提前知道型号、数量、所在节点”进化为“声明需求、系统自动适配”,从而大幅压缩碎片化等待时间,也为后续更复杂的拓扑感知、动态共享铺平了道路。

截至 Kubernetes v1.29,DRA 仍为 Alpha 特性,需在 kube-apiserver 和 kubelet 上同时开启 DynamicResourceAllocation 特性门控,调度器和资源控制器版本须严格对齐。虽然特性门控带来一定的早期采用成本,但主流硬件厂商(如 NVIDIA)已发布兼容 DRA 的 GPU Operator,使生产集群可以保留原有的 Device Plugin 路径,同时在需要细粒度控制的负载上启用 DRA,形成两条路径并存的过渡策略。

2. 对比 Device Plugin

常见的顾虑是:上了 DRA 是不是就得抛弃 Device Plugin?事实正好相反。二者并不是互斥关系,而是分层协作——Device Plugin 依然是内核驱动与 Kubernetes 之间的基础适配层,负责上报设备数量、健康状态;DRA 则叠加在它之上,相当于增加了一层动态调度和分配逻辑。

一个直观的对比:传统路径下发 nvidia.com/gpu: 2 时,调度器只关心“节点上还有没有 2 张完整的 GPU”,至于这两张卡是哪张、显存多少、是否直连 NVLink,统统不管。而 DRA 的 ResourceClaim 可以使用结构化参数表达约束,比如:“请求两张显存≥40G、支持 NVLink 的 GPU,且允许使用已切分的 MIG 实例”。调度器会结合设备属性做匹配,即便节点上只有部分满足条件的卡,也不会直接判为调度失败,而是可能等待部分资源被释放或通过排队机制顺序满足。

从数据上看,多数团队的 GPU 集群中,独占整卡的训练作业依然占主流,此时 Device Plugin 足够成熟稳定;而需要多任务共享一卡、动态组合多卡拓扑、或不同厂商 GPU 混部的场景,才真正体现 DRA 的价值。因此实操上的最佳做法是:按场景分层使用。为标准的单卡训练保留 nvidia.com/gpu: 1 这类简单请求;对于推理服务共享、MIG 分区使用或跨卡 NVLink 指定的高级需求,才转向 ResourceClaim。这样既避免全局切换带来的不稳定,又能精准地在合适的地方提高利用率。

3. ResourceClaim 是什么

ResourceClaim 是 DRA 引入的核心 API 对象,在命名空间内代表一组请求的设备资源。它既可以被单个 Pod 独占,也可以被多个 Pod 共享(共享模式下,由驱动决定如何隔离上下文),从而在 Kubernetes 原生语义里实现了设备的“复用”。与过去必须将设备数量直接写入 Pod spec 不同,ResourceClaim 可以在 Pod 部署前单独创建、修改(处于未绑定状态时),让运维或调度器有更多编排空间。

编写 ResourceClaim 时,一个关键建议是:尽量使用结构化参数,避免硬编码节点名或设备 ID。例如:

spec:
  devices:
    requests:
    - name: gpu-req
      deviceClassName: gpu.nvidia.com
      allocationMode: All
      attributes:
        memoryGB: ">=40"
        nvlink: "true"

这段配置让调度器去寻找满足条件的所有设备,而不是指定“node-12 上的 GPU-0”,从而保留调度弹性。一旦 Claim 被 Pod 绑定,修改就会受限,通常需要 Pod 终止才能释放。因此,引入 DRA 的团队必须配套碎片监控(如 DCGM + Prometheus 持续追踪已分配但闲置的设备)和回收策略,防止僵死的 ResourceClaim 造成资源泄漏。

需要纠正一个常见误读:DRA 本身并不提供 GPU 切分能力,它负责的是“发布”和“申请”这些细粒度资源。真正把一张 A100 拆成多个实例,靠的是 NVIDIA MIG 控制器等驱动层工具,DRA 只是让这些切好的实例能以统一的方式被调度和挂载。这也解释了为什么 DRA 上线过程中,Device Plugin 依然不可或缺——内核驱动、设备发现这些地基活,仍然由它完成,而 DRA 带来的是上层调度策略的升级。

三、环境准备:开启GPU动态分配

在开始构建动态分配的调度链路之前,需要让集群的底层组件承认这样一个事实:GPU 不再只是一个靠“数量”来计算的静态资源,而是带有显存、拓扑、互联方式等多维属性的可描述设备。这一层的准备工作,直接决定了 DRA 是跑在一个稳健的地基上,还是悬浮在半空中的实验特性。

1. 集群与驱动要求

最低门槛是 Kubernetes 1.27,但从社区反馈和 bug-fix 密度来看,1.29 才是第一个值得严肃对待 DRA 的版本——resource.k8s.io/v1alpha2 在 1.29 中已经冻结了绝大部分字段,后续变动将主要围绕控制器的稳定性和调度吞吐展开。因此,如果要在生产集群上尝试,基线建议定在 1.29,且 control plane 与所有 GPU 节点的 kubelet 版本必须严格一致;混部不同小版本的 kubelet 会直接导致 ResourceClass 在节点上解析失败,这是最容易踩到的第一个坑。

节点侧需要提前安装好 NVIDIA 驱动,版本至少 525.60.13,容器运行时推荐使用 containerd 配合 nvidia-container-toolkit。这里一个容易被忽视的关键是,Driver 必须暴露可被 DRA 感知的资源接口,而不只是支持传统的 Device Plugin 挂载。实际做法是启用 NVIDIA GPU Operator 的 driver.enabled=truekataManager 等模块,并确认 ClusterPolicy 中 driver.rdma.enabled 等标识已按需打开。效果验证很简单:登录任一 GPU 节点执行 nvidia-smi topo -m,确认 GPU 与 NUMA 亲和、NVLink 连接状态等信息可被正确读取——这是 DRA 在后续调度中判断“哪个 GPU 更适合 Pod”的基础数据。

如果集群中还混有不同厂商的加速器(例如某些节点插着 AMD Instinct 或 Intel Data Center GPU Max),则需要为每种设备安装对应的驱动栈,并在后文构建 ResourceDriver 时分别注册。不过,本次实战以 NVIDIA GPU 为主,跨厂商场景可以作为下一步演进方向。

2. 启用 DRA 特性门控

DRA 在 1.29 仍然挂在 Alpha 门控下,所以 kube-apiserver、kube-controller-manager、kube-scheduler 和 kubelet 必须同时传入 --feature-gates=DynamicResourceAllocation=true。只改其中一两个组件会导致 Pod 被调度后被卡在“等待资源声明”的不可恢复状态。这在多个社区 issue 中被反复提及——因为 scheduler 以为资源已经分配出去,但 kubelet 完全无视 ResourceClaim,最终 Pod 永远无法启动。

在 kubeadm 部署的集群中,修改方式如下(示例为 apiserver 配置片段):

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
  extraArgs:
    feature-gates: "DynamicResourceAllocation=true"
controllerManager:
  extraArgs:
    feature-gates: "DynamicResourceAllocation=true"
scheduler:
  extraArgs:
    feature-gates: "DynamicResourceAllocation=true"

kubelet 则需要逐节点修改 /var/lib/kubelet/config.yaml 或等价配置文件,加入 featureGates: DynamicResourceAllocation: true,然后用 systemctl restart kubelet 使其生效。完成所有组件修改后,可以用 kubectl api-versions | grep resource 验证——如果看到 resource.k8s.io/v1alpha2,说明 DRA API 已经就绪。

这里有一个典型的踩坑点:仅仅启用门控还不够,必须确保调度器加载了 DRA 插件。如果采用的是自定义调度器(比如集成 Kueue 或 Volcano),需要在调度器配置的 plugins 列表中显式启用 DynamicResources。默认的 kube-scheduler 在门控打开后会自动加载该插件,但一旦脱离默认部署,就需要手动核对 KubeSchedulerConfiguration 的配置文件。

启用后的直观效果是,你可以通过 kubectl get resourceclasses 看到系统内置的 ResourceClass(如果已经安装了资源驱动),而所有节点在 kubectl describe node 的 Capacity 中不再只是显示 nvidia.com/gpu: 8,还会出现由结构化参数描述的细粒度属性——这一步标志着集群已经初步脱离“以卡为原子单位”的旧世界。

3. 安装必要组件

门控打开只是拿到了入场券,核心的分配逻辑需要由 DRA 控制器和资源驱动共同实现。目前最成熟的选择是部署 NVIDIA GPU DRA driver,它与传统的 GPU Operator 共享节点驱动,但在上层提供了一组面向 ResourceClaim 的 CRD。

部署过程大致如下:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm install gpu-dra-driver nvidia/gpu-dra-driver \
  --create-namespace -n gpu-dra-system

部署完成后,需要创建至少一个 ResourceClass 来告诉调度器“我可以管理什么样的 GPU”。例如,以下模板描述了一个请求任意共享 GPU 的 ResourceClass:

apiVersion: resource.k8s.io/v1alpha2
kind: ResourceClass
metadata:
  name: shared-gpu.example.com
driverName: gpu.resource.nvidia.com
parametersRef:
  apiGroup: gpu.resource.nvidia.com
  kind: GpuConfig
  name: shared-config

其中 GpuConfig 可以指定显存下限、NVLink 需求、时钟频率要求等结构化参数。这里的一个关键判断是——不要直接把节点名或 GPU UUID 写死在 Claim 模板里,而是用属性来描述需求,否则调度弹性会被削弱回传统节点选择器的水平,DRA 的灵活匹配优势就消失了。

安装验证可以直接创建一个简单的测试 Claim,并用 kubectl describe resourceclaim 观察其 Phase 从 Pending 变为 Allocated,说明整条链路已经联通。此时如果在一个 GPU 节点上故意部署一个只请求 1GB 显存的 Pod,通过 nvidia-smi 可以看到它并未独占整张卡,而是与其他工作负载共享——这正是 DRA 区别于 Device Plugin 的直接体验变化。

需要提醒的是,在生产环境引入这套组件之前,最好先在隔离的 staging 集群上盲测一两周,重点关注控制器内存泄漏、大型 Claim 回收延迟以及组件版本升级时 CRD 兼容性,毕竟 DRA 的 GC 和调度性能在 1.29 上仍然处在早期阶段,不少企业的内部 slack channel 里都能看到“半夜被 ResourceClaim 泄漏唤醒”的讨论。

四、实战:配置GPU动态分配

动态资源分配(DRA)解决的是传统 nvidia.com/gpu: 1 分配模型中最顽固的痛点:一旦请求,整张卡就被独占,即使任务只用 10% 的算力,集群 GPU 利用率也很难突破 40% 的行业常态。DRA 将调度逻辑从“数卡”转向“看属性+看状态”,让 Pod 能够在资源就绪后再绑定,而不是在调度那一刻就固化。下面我们从零开始走通一个基于 ResourceClaim 的 GPU 动态分配全流程——请先确认你的集群已通过 DynamicResourceAllocation 特性门控启用该 Alpha 功能(Kubernetes v1.29 以上),且已安装兼容 DRA 的 GPU 驱动声明,例如 NVIDIA GPU Operator 的对应版本。

1. 创建 ResourceClaim:把 GPU 抽象为可临时占用的声明

第一步不是直接写 Pod,而是定义一个 ResourceClaim。这个对象类似“资源代金券”,指明你需要的设备属性,而非指定某张物理卡。推荐使用结构化参数声明需求,比如要求至少 40GB 显存、支持 NVLink,但不硬编码节点名称,以保留调度弹性。

下面是一个基础示例,在 gpu-claim 命名空间下创建一个名为 nv-gpu-claim 的 ResourceClaim,请求一个来自 nvidia.com 驱动、型号为 gpu 的设备:

apiVersion: resource.k8s.io/v1alpha2
kind: ResourceClaim
metadata:
  name: nv-gpu-claim
spec:
  allocationMode: Allocate
  resourceClassName: nvidia-gpu
  parameters:
    apiVersion: gpu.example.com/v1alpha1
    kind: GPURequirements
    spec:
      minMemoryGB: 40
      count: 1

这里的关键是 resourceClassName 所引用的 ResourceClass 需要事先由集群管理员配置,它指定了驱动是谁、设备属性如何匹配。提交后,DRA 控制器会在后台为这个声明寻找合适的 GPU,但此时不会立即分配,只是在资源池中锁定一个符合条件的位置。操作完成后可用 kubectl get resourceclaims 看到声明的状态从 pending 变为 reserved,这表明未来被 Pod 引用时,有对应资源可以配给。

2. 在 Pod 中引用 ResourceClaim:让调度与设备绑定解耦

有了 ResourceClaim,Pod 就不再直接写 nvidia.com/gpu,而是通过 claims 字段引用声明。正是这一步,让调度器不再在评分阶段就硬绑 GPU,而是等到 Pod 已经选中节点后,再由资源控制器去完成设备的分配与初始化。

下面的 Pod 示例启动一个简单的 CUDA 测试容器,它将使用上一步创建的 nv-gpu-claim

apiVersion: v1
kind: Pod
metadata:
  name: gpu-test
spec:
  containers:
  - name: cuda-container
    image: nvidia/cuda:12.1-base
    command: ["nvidia-smi"]
    resources:
      claims:
      - name: gpu
  resourceClaims:
  - name: gpu
    source:
      resourceClaimName: nv-gpu-claim

提交 Pod 后,你可以观察调度过程的时序变化:调度器为 Pod 选择节点时,只要求节点上资源池有尚未被其他 Claim 占用的 GPU;绑定节点后,节点上的 DRA kubelet 插件才会执行具体的 GPU 分配。也就是说,即使集群中有碎片化空闲 GPU,只要它们能够组合满足 ResourceClaim 的要求,Pod 就能成功启动——这正是 DRA 区别于静态计数的核心价值。

3. 验证动态分配:看日志、查节点、理解“使用时才分配”

Pod 启动后,执行 kubectl logs gpu-test 应能看到 nvidia-smi 输出的 GPU 信息,同时如果所申请的设备支持共享模式(例如预先用 NVIDIA MIG 划好的 10GB 小实例),你可能会发现容器只看到被分配的那一份算力,而非整张卡。这验证了 DRA 并未简单传回一个“我占了一张卡”的符号,而是真正向容器注入了那一部分设备。

更深入的验证可以在节点的 kubelet 日志中观察到类似 "Staging resource for claim" 的记录,表明资源在 Pod 启动时才被“实例化”。另外,通过 DCGM + Prometheus 监控这类节点,能看到已分配但未被活跃使用的 GPU 区块,这种透明性对后续的碎片回收至关重要。若此时删除 Pod,ResourceClaim 会自动释放,资源回归池中;如果重复申请,其他 Pod 可复用同一块资源。

需要指出,DRA 并不能原生将一张 A100 随意切成 MIG 实例,实际的 GPU 划分仍需管理员预先配置 MIG 控制器,DRA 只是负责把这些“定制好的资源包”按单分配给合适的 Pod,所以切勿期待 ResourceClaim 会替你完成驱动层面的划分。对于简单的整卡独占任务,Device Plugin 路径仍是更稳的选择——直到 DRA 在后续版本完成 Beta 甚至 GA 演进。

五、生产优化与问题排查

把 DRA 从概念验证推进到生产环境,最终还是要落到两个问题上:能不能让 GPU 不被浪费,以及出了问题能不能快速定位。下面这三个环节,是多数团队从“能跑起来”到“跑得稳”的必经之路。

1. 避免资源碎片

传统整卡分配带来的碎片是静默但致命的——一个 Pod 哪怕只用 8 GB 显存,也会占满一张 80 GB 的 A100,导致集群层面 GPU 利用率普遍低于 40%。DRA 的改进不在于它能切割 GPU,而在于它让调度器可以感知“部分”资源,并通过 ResourceClaim 描述真实需求而非简单计数。

在调度侧,要避免碎片需要抓住两个要点:结构化参数一定不要退化成节点名。在 ResourceClaim 模板里尽量使用 attributes 描述所需显存、拓扑域、NVLink 需求,而不是通过 nodename 强行绑定。这样调度器才能在节点池里根据实际剩余资源动态匹配,而不是硬装进去就完事了。例如:

spec:
  devices:
    requests:
    - name: gpu
      deviceClassName: nvidia-gpu
      allocationMode: ExactCount
      count: 1
      adminAccess: false
  constraints:
  - requests:
    - gpu
    matchAttribute: "nvidia.com/gpu.memory"
    matchExpression: ">= 32Gi"

这样写的是“我需要至少 32 GB 显存的 GPU”,而不是“我要 node-13 那个 80G 的卡”。实际效果上,碎片从静态“算数量”变成了动态“看库存”,结合 Kueue 排队后,中等负载任务可以自动填补大作业留下的间隙,整集群碎片率能从约 35% 压到 15% 以内。

另一层避免碎片的关键在回收机制。被终止或僵尸的 Pod 有可能残留已绑定的 ResourceClaim,如果不主动清理,它们占用的 GPU 既不能被调度,也不会出现在空闲列表里。建议部署一个清理控制器(例如简单的 CronJob),定期检查 status.deallocationRequested 为 true 但仍有设备引用的 Claim,强制释放。配合 Kubernetes 原生的 TTLController 清理已完成 Job 所属的资源,可以显著减少半永久性的资源泄漏。

2. GPU 使用监控

DRA 引入后,监控视角必须从“节点上有几张卡在工作”下钻到“每个 ResourceClaim 实际用了多少显存和计算”。否则你会看到 GPU 并未空闲,但实际负载几乎为零的尴尬局面。

目前行业里比较标准的方案是用 DCGM Exporter + Prometheus + Grafana 打底,然后添加 DRA 特定的告警规则。需要关注三个核心指标:

  • DCGM_FI_DEV_MEM_COPY_UTILDCGM_FI_DEV_GPU_UTIL:底层 GPU 的实际算力与显存带宽使用率。如果这两个值持续低于 10%,同时 Pod 状态为 Running,基本可以判定是资源申请过多。

  • kubelet_resource_claim_status:由自定义指标导出器从 kube-apiserver 拉取,用于标记 Claim 是否绑定、分配到哪个节点、申请的设备型号。把节点维度的 GPU 总量减去已绑定的 Claim 数量得到的差值,才是真正的可调度余量。

  • kube_pod_resource_claim_info:关联 Pod 与 Claim 的关系。当出现 Pod 已消失但 Claim 仍为 allocated 状态时,自动触发告警并上报到清理队列。

建议为每个 GPU 节点部署 nvidia_gpu_exporter 的 sidecar,将 Claim 维度的显存占用直接打标成项目标签(例如 team=ml-training),而不是只展示 GPU 序号。这样当在凌晨收到 GPU 利用率告警时,可以直接定位是哪个业务的 Pod 占着卡不干活,不用再从一堆 Pod IP 里排查。

3. 常见错误处理

生产里踩坑最多的几种情况,本质上都是因为 DRA 的 alpha 特性与现有组件之间的预期不一致。

错误一:ResourceClaim 一直处于 Pending,调度器日志报“no driver registered”
原因几乎肯定是 kube-apiserver 开启了 DynamicResourceAllocation 门控,但 kubelet 没有同步开启,或者节点上的 DRA driver(如 NVIDIA GPU DRA driver)未安装或版本不匹配。解决方法:检查所有节点的 kubelet 启动参数,确保 --feature-gates=DynamicResourceAllocation=true;同时确认 GPU operator 版本支持当前 Kubernetes 版本,1.29 以下的 DRA 驱动与 1.29 以上不兼容。可以在任意节点执行 kubectl get resourceclasses 确认驱动程序是否已注册,如果为空则驱动未上线。

错误二:Pod 调度成功,但启动时卡在 ContainerCreating,kubelet 报“failed to prepare resource”
多数是因为 ResourceClaim 里的设备属性描述与实际节点可提供的资源不匹配。比如申请了 nvidia.com/gpu.memory >= 40Gi,但目标节点上的 A100 只有 40 GB 可分配部分已被 MIG 切割,剩余显存不足。解决办法:在 Claim 中不要只依赖单一属性,可以加上备选条件,比如允许小显存回退的规则,或者确保 MIG 分区计划与拓扑约束已提前在驱动层配置好。检查 kubectl describe resourceclaim 中的 status.conditions 会有详细错误信息。

错误三:多 Pod 共享同一 ResourceClaim 时,第二个 Pod 无法启动,提示“claim in use”
DRA 允许共享 Claim,但要求 Claim 的 sharePolicy 必须显式设置为 Shared,且驱动支持共享。默认创建的是独占模式。解决办法:在 Claim 的 spec 中添加 sharePolicy: Shared,但要清楚 NVIDIA GPU 驱动层面只有特定场景(如使用时分复用或 MPS)才能真正安全共享算力,否则多个容器并行访问同一张卡会导致 CUDA 上下文冲突。更稳妥的做法是为共享任务专门创建显存受限的 MIG 分区,每个容器各绑到一个分区。

排查这类问题的通用思路是沿着 Pod → ResourceClaim → ResourceClass → Driver 这条链路一层层检查 conditions,而不是只看 Pod 的 events。DRA 把资源分配的复杂状态回写到了 Claim 对象里,kubectl get resourceclaim -o yaml 往往比 describe pod 更能揭示根本原因。

六、未来方向:通用GPU调度

动态资源分配(DRA)在 Kubernetes 1.29 中仍以 Alpha 特性门控存在,但我们已经能从控制面与生态快速迭代中看到一个清晰的方向:从“设备计数”演进为“属性感知+动态拓扑”的通用 GPU 调度层。这一层不再只是把 GPU 当作可枚举的整卡资源,而是将显存、NVLink 拓扑、MIG 实例甚至功耗墙都纳入调度决策,真正把 GPU 集群从“硬件池”变成面向混合负载的算力工厂。想要抵达这个目标,下面三个技术路径正在彼此叠加,构成未来一两年内的落地骨架。

1. Kueue 批调度与 DRA 的职责解耦

当前不少团队把 GPU 闲置率高的锅单纯甩给调度器,实际是两个问题绞在了一起:作业排队时的公平性、以及资源分配时的灵活性。DRA 解决的是后者——让 Pod 可以根据设备属性动态匹配物理 GPU,而批调度组件(特别是 Kueue)负责在前置队列层面决定谁先跑、能跑几个副本、如何防止饿死。

在社区已经验证的实践中,这套组合的典型数据是:在 64 张 A100 的集群上运行混合训练与推理负载,单纯开启 DRA 可以让碎片利用率从 32% 提升到 58%,但出现明显的队头阻塞,部分高优先作业等待时间超过 40 分钟。接入 Kueue 并配置 Cohort 队列和公平共享规则后,平均排队延迟压到 9 分钟以内,GPU 整体分配率(含实际使用)稳定在 68%。这个案例说明,DRA 本身并不能理解作业的优先级与资源预留需求,它只是一个强大的“分配执行器”。Kueue 这样的批调度框架承担准入控制、配额管理和跨命名空间公平性,才能真正把动态资源从“能分”变成“分得好”。

未来半年内,预计 Kueue 社区会增强对 ResourceClaim 生命周期感知,比如在 Flavor 中指定 ResourceClaim 模板,并在作业挂起时预先“软锁定”一组资源但不实际分配,等到所有切片的 GPU 都可获得时才一次绑定。这种分阶段预留的能力,将进一步减少因部分 GPU 就绪导致的大规模训练作业反复失败重启。

2. 多租户共享的粒度与安全边界

多租户共享 GPU 的呼声很高,但落地中常见的问题不是技术做不到,而是租户间隔离不够彻底。DRA 为共享带来了新的可能:ResourceClaim 可以指定为“Shared”模式,允许多个 Pod 在同一张 GPU 上分配不同的显存段或时间片。然而,直接使用 DRA 实现共享会踩到一个坑——内核驱动和 CUDA 上下文并没有因此被完全隔离,一旦某个租户出现显存泄漏或内核崩溃,仍然可能波及其他租户。

现阶段可行的方案是将 DRA 与 NVIDIA MIG 控制器结合,由 MIG 完成硬件级隔离,由 DRA 暴露这些 MIG 实例为独立的 ResourceClaim。这样做可以做到显存与缓存的严格分区,甚至不同租户跑不同的 CUDA 版本。实际数据:某云厂商在内部实验集群里,将一张 A100-80G 通过 MIG 切成 3g.20gb 实例 4 个,利用 DRA 按租户配额下发 ResourceClaim,实现了 4 个互相隔离的推理服务共享一张卡,相比单纯的 MPS 共享,延迟抖动从 12% 降低到 2% 以内。

而真正的“细粒度共享”还需要应用层配合。业内正在探索通过 Coordinated Admission 让应用程序显式声明所需的最小显存与弹性算力,比如一个推理任务声明“至少 5GB 显存,愿意接受算力被压缩到 30%”,调度器结合 DRA 的动态属性可以将其塞入剩余碎片中,同时配合 GPU Operator 动态调节算力上限。这条路仍需驱动层和调度层之间更强的协同,但方向已经明确。

3. 动态 MIG 重配置:从静态切分到弹性重构

当前 MIG(多实例 GPU)的最大短板是重配置需要清空整张 GPU,这在线上的代价非常高。而动态 MIG 技术试图实现不中断服务的实例重划分,这正是 DRA 能真正释放威力的场景。设想这样一种调度:高峰时,集群自动将多张空闲的 A100 从 1g.5gb 切为 2g.10gb,交付给紧急的微调任务;低峰时再聚合回细粒度 MIG 实例,供大量小推理任务共享。

在 NVIDIA 的路线图中,动态 MIG 重构依赖 GPU 系统处理器(GSP)与新的驱动架构,预计在 Hopper 架构后续通过固件更新和驱动支持逐步成熟。Kubernetes 侧,DRA 的结构化参数正好可以表示这种可变拓扑——它不需要事先在节点上写好 MIG 配置,而是由 DRA 控制器在分配瞬间根据 ResourceClaim 里的需求去调 MIG 分区。这本质上把“选设备”变成了“塑造设备”。

不过必须保持冷静:动态 MIG 目前还有诸多限制,例如重配置的时延在秒级到分钟级不等,对正在运行的负载需要快照或迁移支持。行业里已经有实验性方案通过结合 DRA 和 KubeVirt 的虚拟机 GPU 透传实现准实时重配置,但生产就绪程度很低。未来一到两年,更可能的路径是夜间窗口或低峰定时执行重配置,由 Kueue 的 CronJob 风格任务触发,再配合 DRA 更新 ResourceClass,实现准静态的弹性重构,而不是真正的实时在线变更。


常见问题 FAQ

Q:DRA 能解决我集群里的 GPU 碎片化问题吗?
A:能部分解决。DRA 可以按属性分配不同的 GPU,避免因为“只要一张卡但整个节点被锁死”的情况。但彻底消除碎片仍需批调度(如 Kueue)配合队列重排序,以及任务本身的弹性伸缩能力。没有应用层的配合,单靠调度层做不到零碎片。

Q:我们现在用的 Device Plugin 方案,有必要迁移到 DRA 吗?
A:短期不必须。Device Plugin 对于整卡独占场景成熟稳定,性能开销极小。DRA 适合需要 GPU 共享、跨厂商设备、复杂拓扑或动态切分的场景。建议先在非核心业务上并行运行,积累运维经验,等社区达到 Beta 且有更多生产案例后再决定全量迁移。

Q:开启 DRA 后,我怎么知道哪些 ResourceClaim 没有被使用而可以回收?
A:通过 kubectl get resourceclaims -o json 查看 status 字段,判断是否被 Pod 引用。推荐部署一个集群级监控,比如用 DCGM 结合 Prometheus 指标显示实际 GPU 利用率,结合 Claim 分配记录就可以发现“已分配但利用率持续为零”的情况,再用简单的清理控制器定期回收这些死 Claim。需注意,强行删除已绑定的 Claim 会导致 Pod 异常,必须确认 Pod 已终止。

Q:动态 MIG 现在能用吗?
A:不推荐直接用于生产。动态 MIG 需要特定的 GPU 型号和驱动版本,且在 Kubernetes 侧尚无标准的控制器能可靠地协调重配置与调度。现阶段最好把 MIG 分区当作静态配置,通过 DRA 暴露这些静态分区来实现多租户共享,等 NVIDIA 和 K8s 社区共同推动的重配置协议成熟后再评估。

标签

微信咨询二维码
微信咨询 获取代理价(更低折扣)
更低报价 更低折扣 代金券申请
咨询热线:15026612550