HAMi(前身为 k8s-vGPU-scheduler)是一个面向 Kubernetes 的异构设备管理中间件。它可以管理不同类型的异构设备(如 GPU、NPU、MLU、DCU 等),实现异构设备在 Pod 之间的共享,并基于设备拓扑和调度策略做出更优的调度决策。
介绍
核心功能与优势包括:
-
设备虚拟化与共享: 这是 Hami-scheduler 最核心的功能。它允许将单个物理 GPU 虚拟化成多个 vGPU,并根据 Pod 的实际需求(如显存大小、计算核心比例)进行分配。这意味着多个 Pod 可以共享同一张物理 GPU,从而极大地提高了 GPU 的利用率,特别适用于推理服务、开发测试等对 GPU 资源需求不高的场景。
-
资源隔离: 在共享的同时,Hami-scheduler 也提供了硬性的资源隔离机制。它通过拦截 CUDA API 调用等技术手段,确保每个 Pod 只能使用其被分配的 vGPU 资源,避免了邻居噪声
问题,即一个 Pod 的异常行为影响到共享同一物理设备的其他 Pod。
-
异构设备统一管理: Hami 意为Heterogeneous AI Middleware
(异构 AI 中间件)。顾名思义,它不仅支持 NVIDIA 的 GPU,还致力于支持来自不同厂商的多种异构计算设备(如 NPU、MLU 等),提供一个统一的管理和调度界面。
-
灵活的调度策略: Hami-scheduler 提供了比默认调度器更丰富的调度策略,以应对复杂的 AI 工作负载场景。用户可以通过 Pod 的注解(Annotations)来指定调度策略,例如:
binpack
(装箱策略): 尽可能将 Pod 调度到已使用 GPU 的节点或已分配 vGPU 的物理 GPU 上,以整合资源,让部分 GPU 保持空闲以备大型任务使用。
spread
(分散策略): 尽可能将 Pod 分散到不同的节点或物理 GPU 上,以提高容灾能力和负载均衡。
-
无感知的应用体验: 对于应用开发者而言,使用 Hami-scheduler 管理的 vGPU 资源几乎是无感知的。开发者无需修改应用程序代码,只需在 Pod 的资源请求中声明所需的 vGPU 资源(如hami.io/gpu-memory
),即可像使用普通 GPU 一样进行开发和部署。
-
Architecture
Hami-scheduler 是如何工作的?
Hami-scheduler 并非完全取代了 Kubernetes 的默认调度器,而是以调度器扩展(Scheduler Extender) 的方式与之协同工作。其架构主要包含以下几个关键组件:
- Mutating Admission Webhook: 这是一个准入控制器。当一个 Pod 被创建时,该 Webhook 会进行拦截。它会检查 Pod 的资源请求中是否包含 Hami 定义的资源类型(如
hami.io/gpu-memory
)。如果包含,Webhook 会自动修改 Pod 的 YAML 文件,将其schedulerName
字段指定为hami-scheduler
,从而将这个 Pod 的调度权交给了 Hami。
- Hami-scheduler Extender: 这是调度的核心逻辑所在。当 Hami-scheduler 接手一个 Pod 的调度任务后,它会执行一系列的
过滤(Filter)
和评分(Score)
操作。它会根据节点上物理 GPU 的实际使用情况、vGPU 的分配情况以及 Pod 请求的资源量,计算出哪些节点是可行
的,并为这些可行节点打分,最终选择得分最高的节点进行绑定。Scheduler Extender 外置调度器可以影响到三个阶段:
- Filter:调度框架将调用 Filter 函数,过滤掉不适合被调度的节点。
- Priority:调度框架将调用 Priority 函数,为每个节点计算一个优先级,优先级越高,节点越适合被调度。
- Bind:调度框架将调用 Bind 函数,将 Pod 绑定到一个节点上。
- Filter、Priority、Bind 三个阶段分别对应三个 HTTP 接口,三个接口都接收 POST 请求,各自的请求、响应结构定义在这里:#kubernetes/kube-scheduler/extender/v1/types.go
- Device Plugin: Hami 为每种支持的异构设备提供了设备插件。这些插件负责发现节点上的物理设备、上报设备信息(如总显存、健康状况)给 Kubernetes,并在 Pod 被调度到节点后,负责具体的资源分配和隔离操作。
简而言之,整个流程是:Webhook标记
需要特殊调度的 Pod -> Hami-scheduler决策
Pod 的最佳节点 -> Device Plugin 在节点上执行
资源的分配与隔离。
hami-scheduler 包含了 kube-scheduler,参考
调度流程介绍
适用场景
Hami-scheduler 在众多场景下都能显著提升资源利用率和管理效率:
- AI 推理服务集群: 推理任务通常对 GPU 的计算核心需求不高,但对显存有一定要求。通过 vGPU 共享,可以在单张 GPU 卡上部署多个推理服务实例。
- 研发与测试环境: 在多用户、多任务的研发环境中,开发者可以申请小份额的 vGPU 进行代码调试和模型验证,避免了为每个开发者都分配一张完整 GPU 卡的巨大成本。
- 云计算平台: 云服务提供商可以利用 Hami-scheduler 提供更小颗粒度、更具成本效益的 GPU 云主机或容器服务,降低用户使用 AI 算力的门槛。
- 混合 AI 工作负载: 在同时存在模型训练和推理任务的集群中,Hami-scheduler 可以灵活调度,将需要整卡的训练任务与可以共享的推理任务进行合理地混合部署。
部署
- 通过添加标签
gpu = on
标签,标记 GPU 节点以与 HAMI 进行计划。没有此标签,节点将无法由 hami 调度程序管理
kubectl label nodes {nodeid} gpu=on
helm repo add hami-charts https://project-hami.github.io/HAMi/
# 或
wget xxx/hami.tar.gz
tar -xzf hami.tar.gz
helm install hami hami-charts/hami -n kube-system
# 查看
kubectl get pods -n kube-system
配置
设备配置
注意:
以下所有配置均在名称为 hami-scheduler-device
的 ConfigMap 中进行定义,你可以通过以下的方式进行更新:
-
直接热更新 ConfigMap:成功部署 HAMi 后,你可以通过 kubectl edit
指令手动热更新 hami-scheduler 中的数据,如以下指令所示:
kubectl edit configmap hami-scheduler-device -n <namespace>
更新完毕后,需要重启 hami-Scheduler 和对应节点上的 hami-device-plugin
-
修改 Helm Chart: 在 device-configmap.yaml 更新对应的字段,并进行部署
nvidia.deviceSplitCount
:
整数类型,预设值是 10。GPU 的分割数,每一张 GPU 都不能分配超过其配置数目的任务。若其配置为 N 的话,每个 GPU 上最多可以同时存在 N 个任务。
nvidia.deviceMemoryScaling
:
浮点数类型,预设值是 1。NVIDIA 装置显存使用比例,可以大于 1(启用虚拟显存,实验功能)。对于有 M 显存大小的 NVIDIA GPU,
如果我们配置nvidia.deviceMemoryScaling
参数为 S,在部署了我们装置插件的 Kubenetes 集群中,这张 GPU 分出的 vGPU 将总共包含 S * M
显存。
nvidia.migStrategy
:
字符串类型,目前支持 “none“ 与 “mixed“ 两种工作方式,前者忽略 MIG 设备,后者使用专门的资源名称指定 MIG 设备,使用详情请参考 mix_example.yaml,默认为 “none”
nvidia.disablecorelimit
:
字符串类型,“true” 为关闭算力限制,“false” 为启动算力限制,默认为 “false”
nvidia.defaultMem
:
整数类型,预设值为 0,表示不配置显存时使用的默认显存大小,单位为 MB。当值为 0 时,代表使用全部的显存。
nvidia.defaultCores
:
整数类型 (0-100),默认为 0,表示默认为每个任务预留的百分比算力。若设置为 0,则代表任务可能会被分配到任一满足显存需求的 GPU 中,若设置为 100,代表该任务独享整张显卡
nvidia.defaultGPUNum
:
整数类型,默认为 1,如果配置为 0,则配置不会生效。当用户在 Pod 资源中没有设置 nvidia.com/gpu 这个 key 时,webhook 会检查 nvidia.com/gpumem、
resource-mem-percentage、nvidia.com/gpucores 这三个 key 中的任何一个 key 有值,webhook 都会添加 nvidia.com/gpu 键和此默认值到 resources limit 中。
nvidia.resourceCountName
:
字符串类型,申请 vgpu 个数的资源名,默认:“nvidia.com/gpu”
nvidia.resourceMemoryName
:
字符串类型,申请 vgpu 显存大小资源名,默认:“nvidia.com/gpumem”
nvidia.resourceMemoryPercentageName
:
字符串类型,申请 vgpu 显存比例资源名,默认:“nvidia.com/gpumem-percentage”
nvidia.resourceCoreName
:
字符串类型,申请 vgpu 算力资源名,默认:“nvidia.com/gpucores”
nvidia.resourcePriorityName
:
字符串类型,表示申请任务的任务优先级,默认:“nvidia.com/priority”
Pod 配置(在注解中指定)
-
nvidia.com/use-gpuuuid
:
字符串类型,如: “GPU-AAA,GPU-BBB”
如果设置,该任务申请的设备只能是字符串中定义的设备之一。
-
nvidia.com/nouse-gpuuuid
:
字符串类型,如: “GPU-AAA,GPU-BBB”
如果设置,该任务不能使用字符串中定义的任何设备
-
nvidia.com/nouse-gputype
:
字符串类型,如: “Tesla V100-PCIE-32GB,NVIDIA A10”
如果设置,该任务不能使用字符串中定义的任何设备型号
-
nvidia.com/use-gputype
:
字符串类型,如: “Tesla V100-PCIE-32GB,NVIDIA A10”
如果设置,该任务申请的设备只能使用字符串中定义的设备型号。
-
hami.io/gpu-scheduler-policy
:
字符串类型,“binpack” 或 “spread”
- spread:,调度器会尽量将任务均匀地分配在不同 GPU 中
- binpack: 调度器会尽量将任务分配在已分配的 GPU 中,从而减少碎片
-
hami.io/node-scheduler-policy
:
字符串类型,“binpack” 或 “spread”
- spread: 调度器会尽量将任务均匀地分配到不同节点上
- binpack: 调度器会尽量将任务分配在已分配任务的节点上,从而减少碎片
-
nvidia.com/vgpu-mode
:
字符串类型,“hami-core” 或 “mig”
该任务希望使用的 vgpu 类型
容器配置(在容器的环境变量中指定)
节点配置
HAMi 允许为每个节点的 device plugin 配置不同的行为,只需要修改 configmap,如下:
kubectl -n <namespace> edit cm hami-device-plugin
name
: 要配置的节点名称。
operatingmode
: 节点的运行模式,可以是 “hami-core” 或者 “mig”, 默认: “hami-core”。
devicememoryscaling
: 节点内存的超配率。
devicecorescaling
: 节点算力的超配率。
devicesplitcount
: 每个设备允许被分配的任务数。
filterdevices
: 节点上不被 HAMi 管理的设备。
uuid
: 所要排除设备的 UUID。
index
: 所要排除设备的索引。
- 一个设备只要在
uuid
或者 index
列表中,就不会被 HAMi 管理。
使用
Allocate device memory
resources:
limits:
nvidia.com/gpu: 1 # requesting 1 GPU
# nvidia.com/gpumem: 3000 # Each GPU contains 3000m device memory
nvidia.com/gpumem-percentage: 50 # Each GPU contains 50% device memory
limits:
nvidia.com/gpu: 1 # 请求1个vGPUs
nvidia.com/gpumem: 21000 # 每个vGPU申请21G显存 (可选,整数类型)
nvidia.com/gpucores: 30 # 每个vGPU的算力为30%实际显卡的算力核心 (可选,整数类型)
- Nvidia GPU Parameters 参考
nvidia.com/gpu
: 请求的 vGPU 数量,例如 1
nvidia.com/gpucores
表示每张卡占用的 GPU 算力,值范围为 0-100;如果配置为 0,则认为不强制隔离;配置为 100,则认为独占整张卡
nvidia.com/gpumem
表示每张卡占用的 GPU 显存,值单位为 MB,最小值为 1,最大值为整卡的显存值
nvidia.com/gpumem-percentage
显存请求百分比,例如 50 表示请求 50% 的显存
nvidia.com/priority
优先级,0 表示高优先级,1 表示低优先级,默认为 1
GPU 选择
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
annotations:
nvidia.com/use-gpuuuid: "GPU-afcde51c-80b8-11f0-89e8-df5232a0764e,GPU-b6461b44-80b8-11f0-bbbb-1763fe84f675"
# nvidia.com/nouse-gpuuuid: "GPU-afcde51c-80b8-11f0-89e8-df5232a0764e"
spec:
containers:
- name: ubuntu-container
image: ubuntu:24.04
command: ["bash", "-c", "sleep inf"]
resources:
limits:
nvidia.com/gpu: 1
其他