使用 Dynamic Resource Allocation (DRA) 进行 GPU 切片
传统的 device-plugin 模型会分配整张 GPU:Pod 请求 nvidia.com/gpu: 1,即使只是一个 LoRA 微调、从未使用超过几 GiB 显存,也会得到整块卡。在一张 24 GB 的 A30 上,这相当于作业实际所需内存的最多 20 倍,而这些资源都处于闲置状态。
Dynamic Resource Allocation (DRA) —— 一个 resource.k8s.io/v1 API,并已在 Kubernetes 1.34 中达到 GA —— 允许 Pod 改为请求 GPU 的一个切片。借助 Alauda Build of NVIDIA DRA Driver for GPUs,这个切片可以是:
- 一个 MIG 分区 —— 由硬件隔离的卡片一部分(拥有独立的 SM 和 VRAM,由 GPU 强制隔离),或者
- 一个 time-sliced / MPS-shared 的完整 GPU —— 多个 Pod 共享一张卡,但没有显存隔离。
本指南通过 ResourceClaimTemplate 请求一个 MIG 切片,然后在其中使用 Kubeflow Trainer v2 运行一次监督微调。所有资源都位于 assets/dra/,并且仓库中的 e2e harness 通过 c15_dra_gpu_slice.sh 用例对整条流程进行了端到端验证。
目录
device plugin 与 DRA 的对比前提条件一个切片请求中的三个对象第 1 步 —— 确认 driver 正在发布切片第 2 步 —— 对切片做冒烟测试第 3 步 —— 使用 Kubeflow Trainer v2 在切片内进行微调选择切片大小 / 选择 profile没有 MIG?通过 time-slicing 共享整张 GPU将切片与 Kueue 配额结合使用将同一个 claim 接入其他 orchestrator将其转变为真实微调准备 GPU 节点和 DRA driver(管理员)在节点上准备 MIG profile故障排查device plugin 与 DRA 的对比
DRA 不会替代调度器或 Kueue —— 它改变的是设备如何被描述和声明。配额、优先级和抢占机制仍然有效(参见 将切片与 Kueue 配额结合使用)。
前提条件
DeviceClass 和 ResourceSlice 是集群作用域资源,由 driver/admin 拥有。ResourceClaimTemplate 和 ResourceClaim 是命名空间作用域资源,由你创建。ResourceClaimTemplate 必须与引用它的 Pod 位于同一个命名空间。
一个切片请求中的三个对象
DeviceClass(由 driver 创建)定义一种设备类型和一个基础选择器。Alauda Build of NVIDIA DRA Driver for GPUs 提供gpu.nvidia.com(完整 GPU)和mig.nvidia.com(MIG 切片)。ResourceClaimTemplate(由你创建)表示“给每个 Pod 分配一个来自该类、匹配此 CEL 选择器的设备”。调度器会基于它为每个 Pod 生成一个ResourceClaim。- Pod 在
spec.resourceClaims中引用该模板,而 container 通过resources.claims选择使用它。完全不会使用nvidia.com/*限制。
第 1 步 —— 确认 driver 正在发布切片
只要 driver 安装完成,DeviceClass 就会存在;但只有当 kubelet-plugin 为节点的 GPU 发布 ResourceSlice 后,Pod 才能被调度:
如果 get resourceslices 结果为空,说明 driver 已安装,但尚未在任何节点上发布设备——请先跳到 准备 GPU 节点和 DRA driver,然后再继续。
检查已发布的设备,以了解你的 CEL 选择器可以匹配哪些确切的属性键和值——不要假设 profile 字符串;请从你的集群中读取它们:
第 2 步 —— 对切片做冒烟测试
在把 DRA 接入训练作业之前,先使用 dra-smoke-pod.yaml 证明 driver 确实会分配一个切片。它会请求一个 MIG 切片并打印 container 看到的内容:
你应该能看到该切片降低后的显存上限——对于 1g.6gb,大约是 6 GiB,而不是整张卡的 24 GiB——这正是切片的意义所在:
从 control plane 检查分配情况:
第 3 步 —— 使用 Kubeflow Trainer v2 在切片内进行微调
dra-sft-trainingruntime.yaml 中的 TrainingRuntime 是一个普通的 Trainer v2 runtime,恰好包含两个 DRA 钩子,并且没有 nvidia.com/* 限制:
它的训练脚本是自包含的——会构建一个小型 causal-LM,为其包装一个 LoRA adapter,并在合成数据集上训练,因此这次运行不需要下载模型或数据集,并且大约一分钟即可完成。这使它非常适合在离线 GPU 节点上运行,也非常适合验证切片。你可以替换为真实的基础模型和数据集,将其变成生产级微调(参见 将其转变为真实微调);DRA 相关的接入方式不变。
应用 runtime 并提交作业:
跟踪它直到完成:
成功时如下所示:
peak_mem 明显低于切片的 total_mem,这证明微调完全运行在其分区内。再启动第二个 TrainJob,并让第一个继续运行:如果一张卡被划分为四个 1g.6gb 切片,则最多可以在一张物理 GPU 上并发且隔离运行四个微调——每个作业都看不到其他作业的显存。
选择切片大小 / 选择 profile
修改 mig-slice-resourceclaimtemplate.yaml 中 CEL 选择器里的 MIG profile:
常见 profile(请根据你的 ResourceSlice 确认确切字符串——几何布局因 GPU 而异):
选择能够轻松容纳你的模型 + LoRA + optimizer state + activations 的最小 profile。0.5–1.5B 模型的 LoRA 适合 1g.6gb;7B QLoRA 通常需要 2g.12gb 及以上。
没有 MIG?通过 time-slicing 共享整张 GPU
如果 GPU 不支持 MIG,或者你不想对其分区,可以通过一个不透明的 GpuConfig 使用 shared-gpu-resourceclaimtemplate.yaml 请求一个通过 time-slicing 共享的完整 GPU:
将 runtime 指向这个模板即可(resourceClaimTemplateName: shared-gpu-timeslice)——其余部分无需更改。e2e 用例会使用 DRA_SLICE_MODE=shared 来运行这条路径。
将切片与 Kueue 配额结合使用
DRA 关注的是设备的形态;Kueue 关注的是谁可以使用多少。Kueue 能理解 DRA DeviceClass,因此 ClusterQueue 可以像配额 nvidia.com/gpu 一样,为 mig.nvidia.com 设备设置配额——按切片预算接纳 TrainJob,并在更高优先级作业需要切片时抢占借用者。Preemptible TrainJobs with Kueue 指南中的 cohort 模式可原样使用;只需将 ResourceFlavor 中受管的资源替换为 DRA device class 即可。(Kueue 中的 DRA 支持仍在演进——在生产环境依赖之前,请先根据你的 Kueue 版本进行验证。)
将同一个 claim 接入其他 orchestrator
DRA 钩子位于 Pod spec 中,因此任何能够构造 Pod 的系统都可以请求一个切片——Trainer v2 只是其中一个调用者:
- Kubeflow Pipelines (KFP v2): 通过
kfp-kubernetes的平台配置(或 pod-spec patch),为某个 task 的 Pod 附加resourceClaims/resources.claims,让 pipeline component 运行在切片上。 - Volcano / 普通
Job/Pod: 这两个钩子与 冒烟测试 Pod 在字节级别上完全一致——设置spec.resourceClaims和 container 的resources.claims即可。
冒烟测试 Pod 就是这些场景的最小模板。
将其转变为真实微调
随附脚本训练的是一个合成模型,因此可以离线验证切片。若要在切片上微调真实模型,请保留这两个 DRA 钩子,并将 container 替换为真实 recipe——例如 使用 Kubeflow Trainer v2 进行微调 中的 LlamaFactory runtime,其中 dataset-initializer / model-initializer 步骤会将基础模型和数据集下载到共享 PVC 上。该 runtime 唯一需要修改的地方,就是删除 nvidia.com/gpualloc / gpucores / gpumem 限制,并添加 spec.resourceClaims + resources.claims。根据模型大小来配置 MIG profile(0.6B LoRA 适合 1g.6gb;7B QLoRA 需要 2g.12gb 及以上)。
准备 GPU 节点和 DRA driver(管理员)
Alauda Build of NVIDIA DRA Driver for GPUs 安装了一个 controller,以及一个受节点标签控制的 kubelet-plugin DaemonSet。只要 GPU 节点还没有带上该标签,plugin 就不会发布任何 ResourceSlice,而 DRA Pod 也会一直处于 Pending 状态。
对于完整 GPU / time-sliced 方式的 claim,这就是全部需要做的事情——此时 plugin 会为每块卡发布一个 gpu.nvidia.com 设备,你可以直接跳到 对切片做冒烟测试。MIG 切片则需要下面的额外准备。
在节点上准备 MIG profile
在 driver 能够分配之前,卡上必须先存在 MIG 切片。 Alauda Build of NVIDIA DRA Driver for GPUs 不会按需创建 MIG 分区——它只会暴露已经存在的 MIG 设备。即使 MIG 模式已启用但尚未创建任何实例,kubelet-plugin 也会进入 crash-loop,并报 invalid CDI Spec: no devices。因此,管理员需要在每个 GPU 节点上只创建一次 MIG profile(所有 nvidia-smi 命令都在节点宿主机上执行——可以通过特权 Pod 或 SSH):
profile 的ID、名称以及每个实例的显存都与硬件相关,而且 nvidia-smi mig -lgip 显示的名称可能与 driver 发布的 profile 属性不同——例如,A30 的一个 GPU-instance profile 在列表中显示为 2g.6gb,但在 DRA 中会暴露为 1g.6gb。不要直接照抄这个示例。 请先在第 2 步读取你卡的真实 profile,然后(在下面)读取发布出来的 profile 字符串,并让你的 ResourceClaimTemplate 的 CEL 选择器与之匹配。
使用 nvidia-smi mig -cgi 创建的 MIG 实例并不总是能在 GPU 重置或节点重启后保留下来。请在重启后重新创建,或者通过 driver 的 MIG-config 工具 / 启动时 unit 自动化创建流程。
最后,重启 kubelet-plugin Pod,使其重新枚举该卡并发布 MIG ResourceSlice,然后读取 CEL 选择器必须匹配的确切 profile 字符串:
一块物理 GPU 只能对应一个 device plugin。 NVIDIA DRA kubelet-plugin 和传统 device plugin(例如 HAMi 的 nvidia.com/gpualloc,或默认的 nvidia.com/gpu plugin)不能同时管理同一张卡——否则会造成重复分配。请为 DRA 专门保留一台节点(或特定 GPU),并确保其他 plugin 不会选择它。在另一 plugin 正在提供服务的卡上启用 MIG 模式,会干扰该 plugin 的工作负载。
故障排查
为了对整个流程进行可重复覆盖,c15_dra_gpu_slice.sh 用例会应用模板、runtime 和 TrainJob,并断言微调能够在切片内完成。如果集群中不存在 gpu.nvidia.com ResourceSlice,它会自动跳过,因此在仅使用 device-plugin 的集群上也会保持通过。