一、环境说明

这次测试环境是一套单节点 Kubernetes 集群,节点上有一张 NVIDIA GPU,显存 8G 左右。部署目标是使用 HAMi 对单张 GPU 做 vGPU 共享,让多个 Pod 可以共享同一张物理 GPU。

HAMi 原名 k8s-vGPU-scheduler,现在是 Kubernetes 上的异构算力虚拟化中间件,主要用于 GPU、NPU 等异构设备的共享、隔离和调度。对于 NVIDIA GPU 来说,HAMi 可以让 Pod 按显存、GPU core 等维度申请资源,从而避免一个小任务独占整张 GPU。HAMi 官方文档中也说明,它支持多种异构 AI 计算设备,并且支持多个容器或工作负载共享设备资源。

本文主要测试以下内容:

1. 部署前检查 NVIDIA Driver 和 NVIDIA Container Toolkit
2. 使用 Helm 部署 HAMi
3. 验证 HAMi 组件是否正常
4. 测试一张 8G GPU 被两个 Pod 共享
5. 测试 nvidia.com/gpumem 显存切分
6. 测试 nvidia.com/gpumem-percentage 显存百分比切分
7. 测试 nvidia.com/gpucores 算力比例限制
8. 测试指定 GPU 型号
9. 测试指定 GPU UUID
10. 说明 nvidia.com/gpu: 2 这个容易误解的点

二、部署 HAMi 前需要准备什么

在 Kubernetes 上使用 NVIDIA GPU,大体要先解决三件事:

1. 宿主机能识别 GPU
2. 容器能使用 GPU
3. Kubernetes 能调度 GPU 资源

HAMi 官方 Helm 部署文档中提到,部署前需要在 GPU 节点上准备 NVIDIA Driver 和 NVIDIA Container Toolkit,并且假设 nvidia-container-runtime 已经配置为默认的底层运行时。也就是说,HAMi 本身不是用来安装显卡驱动的,驱动和容器运行时环境需要提前准备好。

这里有一点需要注意:

在 HAMi 场景下,通常不需要再单独部署 NVIDIA 官方 k8s-device-plugin。

原因是 HAMi 安装后会部署自己的 hami-device-plugin,由它负责把 HAMi 管理的 GPU 资源注册给 kubelet。HAMi 官方 FAQ 也说明,HAMi Device Plugin、Volcano vGPU Device Plugin、NVIDIA 官方 Device Plugin 都会管理 GPU 资源,同一个节点上建议只使用一种 GPU 管理插件,避免资源冲突。

如果你的集群里已经通过 GPU Operator 部署了 NVIDIA 官方 device-plugin,需要注意不要让两个 device-plugin 同时管理同一种 nvidia.com/gpu 资源,否则很容易出现资源显示、调度行为和实际设备分配对不上的问题。


三、检查宿主机和容器 GPU 环境

3.1 检查宿主机 GPU 是否正常

先在宿主机上执行:

nvidia-smi

正常情况下应该能看到显卡信息,例如:

NVIDIA GeForce RTX 3070 Ti Laptop GPU
Memory-Usage: 0MiB / 8192MiB

查看 GPU 型号:

nvidia-smi --query-gpu=name --format=csv,noheader

查看 GPU UUID:

nvidia-smi --query-gpu=index,name,uuid,memory.total --format=csv,noheader

示例输出:

0, NVIDIA GeForce RTX 3070 Ti Laptop GPU, GPU-998864a7-78d9-dbab-e122-4c37842ce7cf, 8192 MiB

后面测试 nvidia.com/use-gputypenvidia.com/use-gpuuuid 时会用到这两个值。


3.2 检查 Docker 容器是否能使用 GPU

如果你的机器上使用 Docker,可以先用 Docker 跑一个容器测试 GPU 是否能被容器识别。

docker run --rm --gpus all nvidia/cuda:11.0.3-base-ubuntu18.04 nvidia-smi

如果这里不能正常输出 GPU 信息,就不要急着安装 HAMi,应该先处理 NVIDIA Driver、NVIDIA Container Toolkit 或 Docker runtime 配置。


3.3 注意 Docker 和 containerd 的区别

这里要特别注意:

Docker 测试成功,不代表 Kubernetes Pod 一定能使用 GPU。

原因是现在很多 Kubernetes 集群默认使用的是 containerd,而不是 Docker。HAMi 官方部署文档里 Docker 和 containerd 的 NVIDIA runtime 配置是分开的:如果 Kubernetes 使用 Docker,需要把 nvidia-container-runtime 配成 Docker 的默认 runtime;如果 Kubernetes 使用 containerd,需要在 containerd 配置中把 NVIDIA runtime 配成默认 runtime。

可以通过下面命令查看节点使用的容器运行时:

kubectl get node -o wide

如果显示的是 containerd,可以使用 NVIDIA 官方推荐的方式配置:

sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd

NVIDIA 官方文档也说明,Kubernetes 使用 containerd 时,可以通过 nvidia-ctk runtime configure --runtime=containerd 配置,然后重启 containerd。

如果是 Docker,则可以使用:

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

本文后面的 Pod YAML 没有显式配置:

runtimeClassName: nvidia

前提是 NVIDIA runtime 已经被配置为默认容器运行时。如果你的集群不是默认使用 NVIDIA runtime,而是通过 RuntimeClass 暴露 nvidia runtime,那么需要根据实际环境给 Pod 增加:

runtimeClassName: nvidia

四、安装 HAMi

4.1 给 GPU 节点打标签

HAMi 默认只管理带有 gpu=on 标签的 GPU 节点。官方文档说明,需要给 GPU 节点打 gpu=on 标签,没有这个标签的节点不能被 HAMi scheduler 管理。先查看节点名称:

kubectl get node

给节点打标签:

kubectl label nodes master-01 gpu=on --overwrite

确认标签:

kubectl get node master-01 --show-labels | grep gpu=on

如果你发现 hami-scheduler 正常,但是 hami-device-plugin 没有起来,优先检查这个标签是否存在。


4.2 添加 HAMi Helm 仓库

先查看 Kubernetes Server 版本:

kubectl version

添加 HAMi Helm 仓库:

helm repo add hami-charts https://project-hami.github.io/HAMi/
helm repo update

4.3 安装 HAMi

安装时需要设置 kube-scheduler 镜像版本和 Kubernetes Server 版本匹配。HAMi 官方安装文档中也明确写到,scheduler.kubeScheduler.imageTag 需要和 Kubernetes Server 版本保持一致。

例如 Kubernetes Server 版本是 v1.29.0,安装命令如下:

helm install hami hami-charts/hami \
  --set scheduler.kubeScheduler.imageTag=v1.29.0 \
  -n kube-system

这里的 v1.29.0 要替换成你自己集群的 Kubernetes Server 版本。

如果你的版本是 v1.27.9,那就写成:

helm install hami hami-charts/hami \
  --set scheduler.kubeScheduler.imageTag=v1.27.9 \
  -n kube-system

五、验证 HAMi 组件是否正常

执行:

kubectl -n kube-system get pod | grep -E "hami|vgpu"

新版本一般能看到类似:

hami-device-plugin-xxxxx   2/2   Running
hami-scheduler-xxxxx       2/2   Running

老版本可能叫:

vgpu-device-plugin-xxxxx   Running
vgpu-scheduler-xxxxx       Running

HAMi 官方文档中也说明,如果 hami-device-pluginhami-scheduler 都是 Running,说明安装成功。查看节点资源:

kubectl describe node master-01

重点看 CapacityAllocatable

Capacity:
  nvidia.com/gpu: 10

Allocatable:
  nvidia.com/gpu: 10

这里有个容易误解的点:

nvidia.com/gpu: 10

这不是说机器上有 10 张物理 GPU,而是 HAMi 把一张物理 GPU 按默认配置注册成了多个可分配的 vGPU 份额。HAMi 配置中 nvidia.deviceSplitCount 默认值是 10,含义是单张 GPU 默认最多允许分配给 10 个任务。所以在单卡 8G 的机器上看到 nvidia.com/gpu: 10 是正常现象。


六、HAMi 常用资源字段说明

HAMi 在 Pod 里主要通过下面几个资源字段控制 GPU 申请:

resources:
  limits:
    nvidia.com/gpu: 1
    nvidia.com/gpumem: 3000
    nvidia.com/gpucores: 30

字段含义如下:

nvidia.com/gpu
表示申请 GPU/vGPU 设备分配实例。安装 HAMi 后,节点上看到的 nvidia.com/gpu 数量通常来自 HAMi 的 deviceSplitCount 配置。例如单卡默认可能显示为 10,但这个 10 不是 10 张物理 GPU,也不表示单个 Pod 可以从同一张物理 GPU 上申请多个 vGPU。

nvidia.com/gpumem
表示申请多少 GPU 显存。官方示例中 nvidia.com/gpumem: 3000 表示每个 vGPU 申请 3G 左右显存。

nvidia.com/gpumem-percentage
表示按百分比申请显存。例如 10 表示申请这张 GPU 约 10% 的显存。

nvidia.com/gpucores
表示申请 GPU core 的比例。例如 30 可以理解为申请约 30% 的 GPU 算力。

这里注意一点:

nvidia.com/gpumem 和 nvidia.com/gpumem-percentage 不要同时写在同一个容器里。

前者表示按固定显存大小申请,后者表示按百分比申请,二者选择一种即可。HAMi 官方显存示例中也明确说明,nvidia.com/gpumem 不能和 nvidia.com/gpumem-percentage 一起使用。

Kubernetes 里 GPU 这类扩展资源一般写在 limits 下。如果只写 limits,Kubernetes 会默认把 limit 当作 request 使用。Kubernetes 官方文档也说明,GPU 可以只写 limits;如果同时写 requests 和 limits,两者必须相等;不能只写 GPU requests 而不写 limits。


七、容易误解的点:nvidia.com/gpu: 2 不是从一张卡里拿 2 份 vGPU

前面在查看节点资源时,可以看到类似下面的结果:

Capacity:
  nvidia.com/gpu: 10

Allocatable:
  nvidia.com/gpu: 10

如果是单卡环境,这里很容易产生一个误解:

既然节点上显示 nvidia.com/gpu: 10,
是不是说明这一张物理 GPU 被切成了 10 份,
所以单个 Pod 写 nvidia.com/gpu: 2,
就是从这一张物理 GPU 里拿 2 份 vGPU?

这个理解是不对的。

HAMi 官方 FAQ 对这个问题有明确说明:deviceSplitCount: 10 表示一张物理 GPU 最多可以同时服务 10 个任务,但它并不表示一个任务可以从同一张物理 GPU 上申请多个 vGPU。当一个任务申请 nvidia.com/gpu: 2 时,HAMi 会把它解释为需要两个物理 GPU,而不是从同一张 GPU 上申请两个 vGPU。也就是说,下面这种写法:

resources:
  limits:
    nvidia.com/gpu: 2

可以简单理解为:

这个 Pod 需要 2 个 GPU/vGPU 设备分配实例,并且这 2 个实例不能来自同一张物理 GPU。
对于我的单卡环境来说,可以简单理解为这个 Pod 需要 2 张物理 GPU,所以无法调度成功。

而不是:

这个 Pod 需要从 1 张 GPU 里拿 2 个 vGPU 份额。

所以在单节点、单张 8G GPU 环境里,如果创建下面这个 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: hami-gpu-2-test
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 2
          nvidia.com/gpumem: 2000
          nvidia.com/gpucores: 30

这个 Pod 无法调度成功。因为当前节点只有一张物理 GPU,而 Pod 申请的是 2 个 GPU/vGPU 设备分配实例。创建后查看 Pod:

kubectl get pod hami-gpu-2-test

一般会看到它处于 Pending 状态:

NAME              READY   STATUS    RESTARTS   AGE
hami-gpu-2-test   0/1     Pending   0          1m

继续查看事件:

kubectl describe pod hami-gpu-2-test

可以看到调度失败相关信息。这里要把两个概念分清楚:

deviceSplitCount
表示一张物理 GPU 最多允许多少个任务共享。

nvidia.com/gpu
表示这个 Pod 申请几个 GPU/vGPU 设备分配实例。
当单个 Pod 写成 nvidia.com/gpu: 2 时,HAMi 会按需要 2 个 GPU/vGPU 设备实例处理。
在单卡环境下,它无法从同一张物理 GPU 上拿到两个 vGPU。

HAMi FAQ 里也说明,vGPU 的设计目标是让多个任务共享同一张 GPU,而不是让一个任务在同一张 GPU 上绑定多个 vGPU。deviceSplitCount: 10 只是允许最多 10 个任务共享该 GPU,并不会增加真实的物理 GPU 资源。

所以,如果目标是:

一张 8G GPU 同时跑两个 Pod

正确做法不是创建一个 Pod 并写:

nvidia.com/gpu: 2

而是创建两个 Pod,每个 Pod 都申请:

resources:
  limits:
    nvidia.com/gpu: 1
    nvidia.com/gpumem: 3000
    nvidia.com/gpucores: 30

也就是:

Pod A 申请 1 个 vGPU,3000MB 显存,30% GPU core。
Pod B 申请 1 个 vGPU,3000MB 显存,30% GPU core。

这样两个 Pod 才能共享同一张物理 GPU。


八、测试 1:运行一个基础 HAMi GPU Pod

先验证 HAMi 调度链路和容器内 GPU 可见性。

apiVersion: v1
kind: Pod
metadata:
  name: hami-basic
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 2000
          nvidia.com/gpucores: 30

进入容器验证:

kubectl exec -it hami-basic -- nvidia-smi

如果正常,会看到类似下面的输出:

[HAMI-core Msg(...): Initializing.....]

Memory-Usage:
0MiB / 2000MiB

重点看容器里显示的总显存是否变成了你申请的值。例如这里申请的是 2000,那么容器内看到的显存上限应该接近 2000MiB


九、测试 2:一张 8G 卡切给两个 Pod,每个 Pod 申请 3G 显存

测试目的:

验证两个 Pod 是否可以共享同一张 8G 物理 GPU。
每个 Pod 申请 3000MB 显存。
两个 Pod 总共申请 6000MB,小于 8G,所以理论上可以同时 Running。

YAML 如下:

apiVersion: v1
kind: Pod
metadata:
  name: hami-gpumem-3000-1
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 3000
          nvidia.com/gpucores: 30
---
apiVersion: v1
kind: Pod
metadata:
  name: hami-gpumem-3000-2
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 3000
          nvidia.com/gpucores: 30

分别进入两个 Pod:

kubectl exec -it hami-gpumem-3000-1 -- nvidia-smi
kubectl exec -it hami-gpumem-3000-2 -- nvidia-smi

预期结果:

两个 Pod 都能 Running。
两个 Pod 内执行 nvidia-smi 都能看到 GPU。
每个 Pod 内看到的 GPU 总显存大约是 3000MiB。

如果这两个 Pod 都能正常运行,说明 HAMi 已经可以把一张物理 GPU 按显存维度切给多个 Pod 使用。


十、测试 3:按显存百分比切分

除了直接写 nvidia.com/gpumem,HAMi 也支持通过 nvidia.com/gpumem-percentage 按百分比申请显存。例如 8G 显存的 GPU,申请 10%:

apiVersion: v1
kind: Pod
metadata:
  name: hami-gpumem-percent-10
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem-percentage: 10
          nvidia.com/gpucores: 10

验证:

kubectl exec -it hami-gpumem-percent-10 -- nvidia-smi

预期结果:

容器内看到的总显存约等于整卡显存的 10%。
如果物理卡是 8192MiB,那么 10% 大约就是 819MiB。

这里建议 Pod 名字和百分比保持一致。比如资源里写的是 nvidia.com/gpumem-percentage: 10,Pod 名称就叫 hami-gpumem-percent-10,避免后面排查时看乱。


十一、测试 4:按 GPU core 百分比限制

nvidia.com/gpucores 可以理解为 HAMi 里用于申请 GPU 算力比例的字段。比如:

nvidia.com/gpucores: 30

可以简单理解为申请约 30% GPU core 算力。这里的 30 不是指 30 个物理 CUDA Core,而是 HAMi 定义的 GPU core 使用比例。测试 YAML:

apiVersion: v1
kind: Pod
metadata:
  name: hami-gpucores-30
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      env:
        - name: GPU_CORE_UTILIZATION_POLICY
          value: "force"
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 2000
          nvidia.com/gpucores: 30

查看:

kubectl get pod hami-gpucores-30
kubectl exec -it hami-gpucores-30 -- nvidia-smi

这里要注意:

nvidia-smi 只能证明容器能看到 GPU,以及显存视图是否被限制。
如果要验证 gpucores 的算力限制,需要运行 CUDA 压测程序观察 GPU-Util,而不是只看 nvidia-smi。

HAMi 官方文档说明,nvidia.com/gpucores 是通过 time-slice 方式实现的,所以通过 nvidia-smi 查询 core utilization 时可能会有波动,不一定稳定卡在某一个固定数值。


十二、GPU_CORE_UTILIZATION_POLICY=force 是什么

在测试 HAMi 的 GPU core 限制时,经常会看到下面这个环境变量:

env:
  - name: GPU_CORE_UTILIZATION_POLICY
    value: "force"

这个配置不是 Kubernetes 原生字段,而是 HAMi 容器侧配置项,主要给容器里的 HAMi-core 使用。HAMi 官方配置文档把 GPU_CORE_UTILIZATION_POLICY 放在 Container Configs: Env 下面,也就是说它属于容器运行时侧的环境变量,不是 Kubernetes 原生资源字段。

要理解这个参数,关键是要把 HAMi 的两个阶段分开看:

第一阶段:调度阶段
HAMi scheduler 根据 Pod 里申请的 nvidia.com/gpu、nvidia.com/gpumem、nvidia.com/gpucores 判断当前节点、当前 GPU 是否还有足够资源。

第二阶段:容器运行阶段
Pod 启动以后,容器里的 CUDA/NVML 调用会受到 HAMi-core 的影响,从而让容器看到被分配后的 GPU 资源视图,并对显存、GPU core 等资源进行运行时控制。

先看 Pod 资源申请:

resources:
  limits:
    nvidia.com/gpu: 1
    nvidia.com/gpumem: 2000
    nvidia.com/gpucores: 30

这几个字段首先会进入 HAMi 的调度逻辑。它表达的是:

这个 Pod 需要 1 个 GPU/vGPU 设备分配实例;
这个 Pod 需要 2000MB 左右显存;
这个 Pod 需要 30% GPU core 配额。

所以在调度阶段,HAMi scheduler 会根据这些资源申请去判断:

这张 GPU 的剩余显存是否足够;
这张 GPU 的剩余 core 配额是否足够;
这个 Pod 是否可以放到这张 GPU 上。

举个例子,假设一张 GPU 上已经有一个 Pod 申请了:

nvidia.com/gpumem: 3000
nvidia.com/gpucores: 70

这时候再来一个 Pod 申请:

nvidia.com/gpumem: 3000
nvidia.com/gpucores: 40

即使显存还够,也可能因为 GPU core 配额不够而无法调度。这个阶段的重点是:

nvidia.com/gpucores 用于调度层面的资源计算。
HAMi scheduler 会根据它判断这张 GPU 还能不能继续放这个 Pod。

但是,调度成功不代表容器运行时一定会强制把 GPU 利用率压到 30% 以下。因为调度器只负责“把 Pod 放到哪里”,Pod 真正运行起来以后,就进入了 HAMi-core 的运行时控制阶段。

也就是说:

nvidia.com/gpucores: 30
首先表示:调度时我申请 30% GPU core 配额。

GPU_CORE_UTILIZATION_POLICY=force
表示:容器运行起来以后,HAMi-core 也要强制按照 nvidia.com/gpucores 的值去限制 GPU core utilization。

这才是 GPU_CORE_UTILIZATION_POLICY=force 的重点。

12.1 不加 GPU_CORE_UTILIZATION_POLICY=force 时

如果只写:

resources:
  limits:
    nvidia.com/gpu: 1
    nvidia.com/gpumem: 2000
    nvidia.com/gpucores: 30

而不写:

env:
  - name: GPU_CORE_UTILIZATION_POLICY
    value: "force"

那么 nvidia.com/gpucores: 30 仍然会参与 HAMi 的调度计算。HAMi scheduler 仍然会认为这个 Pod 申请了 30% GPU core,并用这个值判断当前 GPU 是否还有足够的 core 配额。

但是容器真正运行 CUDA 程序时,是否强制把 GPU core utilization 限制在 30% 以下,就取决于 HAMi-core 的默认策略。

不加 GPU_CORE_UTILIZATION_POLICY=force 时,
nvidia.com/gpucores 主要体现为调度层面的 core 配额申请。

HAMi scheduler 会用它判断资源够不够,
但容器运行阶段不一定适合用来强验证 core 限制效果。

12.2 加上 GPU_CORE_UTILIZATION_POLICY=force 时

如果写成:

env:
  - name: GPU_CORE_UTILIZATION_POLICY
    value: "force"
resources:
  limits:
    nvidia.com/gpu: 1
    nvidia.com/gpumem: 2000
    nvidia.com/gpucores: 30

它的含义就是:

调度阶段:
HAMi scheduler 按照 30% GPU core 计算这个 Pod 是否可以调度。

运行阶段:
Pod 启动后,HAMi-core 也会根据 nvidia.com/gpucores 的值,对容器内 GPU core utilization 做限制。

HAMi 官方配置文档中对 force 的说明是:限制 core utilization 低于 nvidia.com/gpucores

所以如果只是为了让 HAMi scheduler 做资源计算,写 nvidia.com/gpucores 就可以。

但如果是为了测试运行时 GPU core 限制效果,建议同时加上:

env:
  - name: GPU_CORE_UTILIZATION_POLICY
    value: "force"

这样测试目标更明确:

既验证调度层面的 GPU core 配额计算;
也验证容器运行时 HAMi-core 对 GPU core utilization 的限制。

12.3 default、force、disable 三种策略怎么理解

HAMi 官方配置中,GPU_CORE_UTILIZATION_POLICY 有三个常见取值:

default
默认策略。

force
强制限制 core utilization,使其低于 nvidia.com/gpucores。

disable
任务执行期间忽略 nvidia.com/gpucores 设置的利用率限制。

可以这样理解:

策略 调度阶段 容器运行阶段
default nvidia.com/gpucores 仍然参与调度计算 使用 HAMi-core 默认策略
force nvidia.com/gpucores 参与调度计算 HAMi-core 强制限制 core utilization
disable nvidia.com/gpucores 仍然可能参与调度计算 运行时忽略 nvidia.com/gpucores 的利用率限制

这里最容易混淆的是 disable。如果写成:

env:
  - name: GPU_CORE_UTILIZATION_POLICY
    value: "disable"

它主要影响的是容器运行时阶段,意思是运行任务时忽略 nvidia.com/gpucores 设置的利用率限制。但是不要简单理解成:

disable 后调度阶段也完全不看 nvidia.com/gpucores。

调度阶段是否能放下这个 Pod,仍然取决于 HAMi scheduler 对 Pod 资源申请的计算。GPU_CORE_UTILIZATION_POLICY 这个环境变量重点影响的是容器运行时 HAMi-core 的行为。


十三、测试 5:指定 GPU 型号

HAMi 支持通过 Pod annotation 指定要使用的 GPU 型号。官方配置文档中 nvidia.com/use-gputype 的含义是:如果设置了这个 annotation,那么 Pod 分配到的设备类型必须是这里指定的类型。先查看本机 GPU 型号:

nvidia-smi --query-gpu=name --format=csv,noheader

示例:

NVIDIA GeForce RTX 3070 Ti Laptop GPU

然后写 YAML:

apiVersion: v1
kind: Pod
metadata:
  name: hami-use-gputype-3070
  annotations:
    nvidia.com/use-gputype: "NVIDIA GeForce RTX 3070 Ti Laptop GPU"
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 2000
          nvidia.com/gpucores: 30

查看:

kubectl get pod hami-use-gputype-3070
kubectl exec -it hami-use-gputype-3070 -- nvidia-smi

预期结果:

如果 annotation 里的 GPU 型号和 nvidia-smi 查出来的型号一致,Pod 可以 Running。

十四、测试 6:指定 GPU UUID

HAMi 也支持通过 nvidia.com/use-gpuuuid 指定 GPU UUID。官方配置文档中说明,如果设置了 nvidia.com/use-gpuuuid,Pod 分配到的设备必须属于指定 UUID 列表。先查看 UUID:

nvidia-smi --query-gpu=index,name,uuid,memory.total --format=csv,noheader

示例:

0, NVIDIA GeForce RTX 3070 Ti Laptop GPU, GPU-998864a7-78d9-dbab-e122-4c37842ce7cf, 8192 MiB

把 UUID 写进 Pod annotation:

apiVersion: v1
kind: Pod
metadata:
  name: hami-use-gpuuuid
  annotations:
    nvidia.com/use-gpuuuid: "GPU-998864a7-78d9-dbab-e122-4c37842ce7cf"
spec:
  restartPolicy: Never
  containers:
    - name: cuda
      image: nvidia/cuda:11.0.3-base-ubuntu18.04
      command: ["bash", "-lc", "nvidia-smi && sleep 3600"]
      resources:
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 2000
          nvidia.com/gpucores: 30

验证:

kubectl get pod hami-use-gpuuuid
kubectl exec -it hami-use-gpuuuid -- nvidia-smi

在单卡环境里,指定正确 UUID 的意义主要是验证 annotation 是否生效。如果是多卡机器,这个功能更有用,可以指定任务只能跑到某一张或某几张 GPU 上。


十五、总结

这次在单节点 Kubernetes 环境中部署了 HAMi,并基于一张 8G NVIDIA GPU 做了几个基础测试。

整体来看,HAMi 的核心作用就是让 GPU 不再只能以整卡方式分配给一个 Pod,而是可以按显存、core、设备型号、设备 UUID 等维度进行更细粒度的调度和限制。

本次测试中需要重点记住几件事:

1. 部署 HAMi 前,宿主机必须先能正常执行 nvidia-smi。
2. 容器运行时必须配置 NVIDIA Container Toolkit。
3. HAMi 节点需要打 gpu=on 标签。
4. scheduler.kubeScheduler.imageTag 要和 Kubernetes Server 版本匹配。
5. 单卡环境下看到 nvidia.com/gpu: 10 是 HAMi 默认切分数量,不是 10 张物理卡。
6. nvidia.com/gpumem 可以按固定显存大小申请 GPU。
7. nvidia.com/gpumem-percentage 可以按百分比申请显存。
8. nvidia.com/gpumem 和 nvidia.com/gpumem-percentage 不要同时使用。
9. nvidia.com/gpucores 表示申请 GPU core 比例,不是 CUDA Core 数量。
10. GPU_CORE_UTILIZATION_POLICY=force 更适合用来测试 GPU core 运行时限制效果。
11. nvidia.com/use-gputype 和 nvidia.com/use-gpuuuid 可以用来做指定型号或指定卡调度。
12. 单个 Pod 写 nvidia.com/gpu: 2,不是从一张卡里拿 2 份 vGPU;在单卡环境里会无法调度。

对于单卡实验环境来说,HAMi 的价值很明显:一张 8G GPU 可以被多个测试 Pod 共享,不再因为一个小任务就独占整张卡。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐