HAMi-scheduler:异构 AI 计算虚拟化中间件

发布时间: 更新时间: 总字数:4832 阅读时间:10m 作者: IP上海 分享 网址

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) 的方式与之协同工作。其架构主要包含以下几个关键组件:

  1. Mutating Admission Webhook: 这是一个准入控制器。当一个 Pod 被创建时,该 Webhook 会进行拦截。它会检查 Pod 的资源请求中是否包含 Hami 定义的资源类型(如hami.io/gpu-memory)。如果包含,Webhook 会自动修改 Pod 的 YAML 文件,将其schedulerName字段指定为hami-scheduler,从而将这个 Pod 的调度权交给了 Hami。
  2. Hami-scheduler Extender: 这是调度的核心逻辑所在。当 Hami-scheduler 接手一个 Pod 的调度任务后,它会执行一系列的过滤(Filter)评分(Score)操作。它会根据节点上物理 GPU 的实际使用情况、vGPU 的分配情况以及 Pod 请求的资源量,计算出哪些节点是可行的,并为这些可行节点打分,最终选择得分最高的节点进行绑定。Scheduler Extender 外置调度器可以影响到三个阶段:
    1. Filter:调度框架将调用 Filter 函数,过滤掉不适合被调度的节点。
    2. Priority:调度框架将调用 Priority 函数,为每个节点计算一个优先级,优先级越高,节点越适合被调度。
    3. Bind:调度框架将调用 Bind 函数,将 Pod 绑定到一个节点上。
    4. Filter、Priority、Bind 三个阶段分别对应三个 HTTP 接口,三个接口都接收 POST 请求,各自的请求、响应结构定义在这里:#kubernetes/kube-scheduler/extender/v1/types.go
  3. 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
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 中进行定义,你可以通过以下的方式进行更新:

  1. 直接热更新 ConfigMap:成功部署 HAMi 后,你可以通过 kubectl edit 指令手动热更新 hami-scheduler 中的数据,如以下指令所示:

    kubectl edit configmap hami-scheduler-device -n <namespace>
    

    更新完毕后,需要重启 hami-Scheduler 和对应节点上的 hami-device-plugin

  2. 修改 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 类型

容器配置(在容器的环境变量中指定)

  • GPU_CORE_UTILIZATION_POLICY

    当前这个参数可以在 helm install 的时候指定,然后自动注入到容器环境变量中, 通过 --set devices.nvidia.gpuCorePolicy=force

    字符串类型,“default”,“force”,“disable”

    • 默认为"default” 代表容器算力限制策略,“default” 为默认
    • “force” 为强制限制算力,一般用于测试算力限制的功能
    • “disable” 为忽略算力限制
  • CUDA_DISABLE_CONTROL

    布尔类型,“true”,“false”

    • 默认为 false
    • 若设置为 true,则代表屏蔽掉容器层的资源隔离机制,需要注意的是,这个参数只有在容器创建时指定才会生效,一般用于调试

节点配置

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

其他

参考

  1. https://project-hami.io/
  2. https://github.com/Project-HAMi/HAMi
  3. https://github.com/Project-HAMi/HAMi/blob/master/README_cn.md
  4. https://www.lixueduan.com/posts/kubernetes/29-hami-analyze-1-device-plugin-nvidia/
  5. https://www.lixueduan.com/posts/kubernetes/31-hami-analyze-2-webhook/
  6. https://www.lixueduan.com/posts/kubernetes/32-hami-analyze-3-scheduler/
本文总阅读量 次 本站总访问量 次 本站总访客数
Home Archives Categories Tags Statistics