实验 4: 使用动态资源分配进行 GPU 切片
HAMi DRA 驱动尚处于快速发展阶段。本实验安装的是已在 Tesla T4 集群上实际验证过的 DaemonSet 清单(驱动版本 projecthami/k8s-dra-driver:v0.1.0)。驱动仓库此后新增了同一 v0.1.0 驱动的 Helm Chart(位于仓库 chart/hami-dra-driver,包含适用于 GPU Operator 集群的 nvidiaDriverRoot 值);该路径验证完成后,本实验将切换为使用 Chart。可消耗容量(Consumable Capacity)特性目前仍需要通过 Kubernetes Feature Gate 启用。
在 实验 3 中,你使用 HAMi 的扩展资源(nvidia.com/gpumem、nvidia.com/gpucores)对 GPU 进行了切片。本实验通过**动态资源分配(Dynamic Resource Allocation,DRA)**实现相同的效果——这是在 v1.34 中正式发布(GA)的 Kubernetes 原生设备 API。Pod 不再使用不透明的资源名称,而是通过 ResourceClaim 以结构化的、经过 Schema 验证的容量请求来申请设备。
为什么 DRA 很重要
| 扩展资源(实验 3) | DRA(本实验) | |
|---|---|---|
| API | 资源 limits 中的 nvidia.com/gpumem: 4000 | ResourceClaim 的 capacity.requests: {memory: 4Gi, cores: 30} |
| 调度 | HAMi 调度器扩展 + Webhook | 原生 kube-scheduler DRA 插件 |
| 设备清单 | 设备插件写入的节点注解 | 带类型化属性的 ResourceSlice API 对象 |
| 设备选择 | 诸如 nvidia.com/use-gputype 的注解 | 基于设备属 性的 CEL 表达式 |
| 验证 | 无(准入时接受任何数值) | 由 API Server 强制执行的 requestPolicy(含 min/max/step) |
HAMi DRA 驱动实现了 DRA Consumable Capacity 特性:多个 Pod 从同一设备抽取容量,由调度器负责记账。
前提条件
- 已完成 实验 1 的集群,Kubernetes v1.34 或更高版本,已安装 HAMi 和 GPU Operator
- 来自
tutorials/labs/examples/04-hami-dra/的清单文件
实验概览
步骤 1: 启用 DRAConsumableCapacity Feature Gate
DRA 本身在 v1.34 中已 GA,但可消耗容量(多个 Pod 从同一设备的容量池中抽取)需要在控制面组件和 kubelet 上启用 DRAConsumableCapacity Feature Gate。以 root 身份运行 enable-dra-feature-gates.sh:
for f in kube-apiserver kube-scheduler kube-controller-manager; do
sed -i "/ - $f/a\\ - --feature-gates=DRAConsumableCapacity=true" /etc/kubernetes/manifests/$f.yaml
done
cat >> /var/lib/kubelet/config.yaml <<EOF
featureGates:
DRAConsumableCapacity: true
EOF
systemctl restart kubelet
编辑
/etc/kubernetes/manifests/下的静态 Pod 清单会使 kubelet 自动重启该组件。API Server 会中断约 20 秒;请等待kubectl get nodes再次响应。
验证 DRA API 组可用:
kubectl api-resources --api-group=resource.k8s.io
NAME SHORTNAMES APIVERSION NAMESPACED KIND
deviceclasses resource.k8s.io/v1 false DeviceClass
resourceclaims resource.k8s.io/v1 true ResourceClaim
resourceclaimtemplates resource.k8s.io/v1 true ResourceClaimTemplate
resourceslices resource.k8s.io/v1 false ResourceSlice
步骤 2: 配置容器运行时
DRA 驱动通过卷挂载而非 NVIDIA_VISIBLE_DEVICES 环境变量来选择设备,这需要一个额外的 NVIDIA 容器运行时选项。如果由 GPU Operator 管理 toolkit,通过 Helm 设置(Operator 会重写运行时配置并自动重启 containerd):
RELEASE=$(helm list -n gpu-operator -o json | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['name'])")
helm upgrade ${RELEASE} nvidia/gpu-operator -n gpu-operator --reuse-values \
--set-json 'toolkit.env=[{"name":"ACCEPT_NVIDIA_VISIBLE_DEVICES_AS_VOLUME_MOUNTS","value":"true"},{"name":"CONTAINERD_SET_AS_DEFAULT","value":"true"}]' \
--version=v25.3.0
toolkit Pod 重启后验证:
grep default_runtime_name /etc/containerd/config.toml
grep accept-nvidia-visible-devices-as-volume-mounts /usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml
default_runtime_name = "nvidia"
accept-nvidia-visible-devices-as-volume-mounts = true
步骤 3: 安装 HAMi DRA 驱动
驱动以 kubelet 插件 DaemonSet 方式运行。两个清单文件:先 RBAC,再 DaemonSet。
kubectl apply -f rbac.yaml
kubectl apply -f ds-gpu-operator.yaml
kubectl get pods -n hami-dra-driver
NAME READY STATUS RESTARTS AGE
hami-dra-driver-kubelet-plugin-r4jtt 1/1 Running 0 31s
ds-gpu-operator.yaml是上游 DaemonSet 的微调版本:NVIDIA_DRIVER_ROOT和driver-roothostPath 指向/run/nvidia/driver,因为 GPU Operator 将驱动放在容器内而非宿主机上。如果你的驱动直接安装在宿主机上,请使用未修改的上游ds.yaml。
步骤 4: 查看 ResourceSlice
在 DRA 世界中,驱动通过 ResourceSlice 对象而非节点注解来发布设备。查看驱动发布的内容:
kubectl get resourceslices -o jsonpath='{.items[0].spec.devices[0].capacity}' | python3 -m json.tool
{
"cores": {
"requestPolicy": {
"default": "100",
"validRange": { "max": "100", "min": "0", "step": "1" }
},
"value": "100"
},
"memory": {
"requestPolicy": {
"default": "15Gi",
"validRange": { "max": "15Gi", "min": "1Mi", "step": "1Mi" }
},
"value": "15Gi"
}
}
T4 被发布为一个拥有两种可消耗容量的设备:
cores(0-100,步长 1)和memory(最高 15Gi,步长 1Mi)。requestPolicy由调度器强制执行,这是扩展资源从未具备的能力。设备还携带类型化属性(productName: Tesla T4、architecture: Turing、cudaComputeCapability: 7.5.0、UUID 等),Claim 可以通过 CEL 表达式进行选择,此外还有allowMultipleAllocations: true,即可消耗容量开关。
步骤 5: 通过 ResourceClaim 分配 GPU 切片
setup.yaml 创建一个 DeviceClass(通过 CEL 选择 HAMi GPU)、一个 test-dra 命名空间和若干 Claim。关键部分:
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
namespace: test-dra
name: single-gpu-0
spec:
devices:
requests:
- name: single-gpu
exactly:
deviceClassName: hami-core-gpu.project-hami.io
capacity:
requests:
cores: 30
memory: "4Gi"
pod-0.yaml 通过引用 Claim 来替代请求 nvidia.com/* 资源:
resources:
claims:
- name: single-gpu
resourceClaims:
- name: single-gpu
resourceClaimName: single-gpu-0
部署并验证:
kubectl apply -f setup.yaml
kubectl create -f pod-0.yaml
kubectl get pod pod-0 -n test-dra
kubectl get resourceclaim single-gpu-0 -n test-dra -o jsonpath='{.status.allocation.devices.results[0]}' | python3 -m json.tool
{
"consumedCapacity": {
"cores": "30",
"memory": "4Gi"
},
"device": "hami-gpu-0",
"driver": "hami-core-gpu.project-hami.io",
"pool": "hami-workshop",
"request": "single-gpu",
"shareID": "225b5df7-3753-45b1-9043-81c00616b384"
}
Claim 已分配到设备
hami-gpu-0