mGPU 技术揭秘:mGPU 节点资源管理方案

技术

picture.image

上一篇文章中,我们详细介绍了 mGPU 的核心技术之一:

基于 Kubernetes 的 GPU 共享调度方案 。本文是 mGPU 系列文章的第三篇, 将重点介绍节点资源管理方案。

来源 | 火山引擎云原生团队

为了解决独占式地使用 GPU 资源导致资源利用率低且成本高这个问题,火山引擎推出了 mGPU 方案,实现了 GPU 在不同容器间的共享、GPU 显存和算力的强隔离,帮助客户在共享使用 GPU 的同时,保证业务性能与资源不受干扰。

而要实现 GPU 共享的能力,除了上篇文章提到的需要

GPU 共享调度方案的支持外,节点层面的工作也是不可或缺的。

技术方案

资源上报

为了实现 1% 算力粒度和 1 MiB 显存粒度的 GPU 资源调度,我们将每块 GPU 的总算力虚拟化成了 100 个算力资源 mgpu-core,将每 MiB 的显存虚拟化成一个显存资源 mgpu-memory,并使用 DevicePlugin 进行资源的上报。

例如,一个节点上有 4 个 V100 GPU,每个 V100 的显存为 32 GiB,则 DevicePlugin 上报的 mGPU 资源总量如下:


          
apiVersion: v1
          
kind: Node
          
metadata:
          
  name: 10.xx.yy.zz
          
spec:
          
  ...
          
status:
          
  allocatable:
          
    vke.volcengine.com/mgpu-core: "400" # 节点可分配的 GPU 算力,单位为百分比 
          
    vke.volcengine.com/mgpu-memory: "130040" # 节点可分配的 GPU 显存,单位为 MiB
          
  capacity:
          
    vke.volcengine.com/mgpu-core: "400" # 节点总的 GPU 算力,单位为百分比 
          
    vke.volcengine.com/mgpu-memory: "130040" # 节点总的 GPU 显存,单位为 MiB
          
  ...
      

资源分配

当使用 mGPU 资源的 Pod 被

GPU 共享调度插件调度成功后,调度结果将被填充到 Pod 的 Annotation 字段中:


          
apiVersion: v1
          
kind: Pod
          
metadata:
          
  annotations:
          
    vke.volcengine.com/assumed: "true" # 标识 Pod 被调度成功
          
    vke.volcengine.com/gpu-index-container-app: "3" # 容器调度结果,表示名称为 app 的容器被调度到序号为 3 的 GPU 上
          
  name: test-mgpu
          
  namespace: default
          
spec:
          
  containers:
          
  - name: test
          
    resources:
          
      limits:
          
        vke.volcengine.com/mgpu-core: "30"
          
        vke.volcengine.com/mgpu-memory: "1024"
          
      requests:
          
        vke.volcengine.com/mgpu-core: "30"
          
        vke.volcengine.com/mgpu-memory: "1024"
          
  nodeName: 10.xx.yy.zz # Pod 调度结果,表示该 Pod 被调度到 10.xx.yy.zz 节点
          
...
      

该 Pod 申请了 30% 的 GPU 算力,以及 1024Mi 的显存,被调度到了序号为 3 的 GPU 上,而如何将该调度结果应用到底层的 mGPU driver 就成为了问题的关键。

理想情况下,调度器做出调度决策,将调度结果打到 Pod 的 Annotation 上后,mGPU Device Plugin 会在 Allocate 或 PreStartContainer 读取到该值,并打到容器的 NVIDIA_VISIBLE_DEVICES 环境变量,传递给内核中的 mGPU driver。

但实际的情况是,当前的 DevicePlugin API 无法做到上述流程,关于这点,我们可以看看当前 DevicePlugin API 的接口:


          
// DevicePluginServer is the server API for DevicePlugin service.
          
type DevicePluginServer interface {
          
   // Allocate is called during container creation so that the Device
          
   // Plugin can run device specific operations and instruct Kubelet
          
   // of the steps to make the Device available in the container
          
   Allocate(context.Context, *AllocateRequest) (*AllocateResponse, error)
          
   // PreStartContainer is called, if indicated by Device Plugin during registeration phase,
          
   // before each container start. Device plugin can run device specific operations
          
   // such as resetting the device before making devices available to the container
          
   PreStartContainer(context.Context, *PreStartContainerRequest) (*PreStartContainerResponse, error)
          
}
          

          
type AllocateRequest struct {
          
   ContainerRequests    []*ContainerAllocateRequest `protobuf:"bytes,1,rep,name=container_requests,json=containerRequests,proto3" json:"container_requests,omitempty"`
          
   XXX_NoUnkeyedLiteral struct{}                    `json:"-"`
          
   XXX_sizecache        int32                       `json:"-"`
          
}
          

          
type ContainerAllocateRequest struct {
          
   DevicesIDs           []string `protobuf:"bytes,1,rep,name=devices_ids,json=devicesIds,proto3" json:"devices_ids,omitempty"`
          
   XXX_NoUnkeyedLiteral struct{} `json:"-"`
          
   XXX_sizecache        int32    `json:"-"`
          
}
          

          
type PreStartContainerRequest struct {
          
   DevicesIDs           []string `protobuf:"bytes,1,rep,name=devices_ids,json=devicesIds,proto3" json:"devices_ids,omitempty"`
          
   XXX_NoUnkeyedLiteral struct{} `json:"-"`
          
   XXX_sizecache        int32    `json:"-"`
          
}
      

可以看到,在 DevicePlugin API 的 Allocate 和 PreStartContainer 的参数中,没有任何可以定位到 Pod 和 Container 的信息,只有 DeviceIDs 数组列表。因此,在 kubelet 调用 DevicePlugin 的这两个接口时,研发人员无法知道本次 Allocate 是针对哪个 Pod 的,也就无法拿到 Pod 上的环境变量。同时在 PreStartContainer 阶段,也无法再为 Container 传递环境变量。

为了解决这个问题,火山引擎云原生团队采取了如下方案:

  • 在 Allocate 阶段 ,将分配的算力、显存的大小以及 DevicesIDs 数组的哈希值保存在容器的环境变量中。
  • 在 PreStartContainer 阶段 ,kubelet 的 Pod Resource API 中就可以拿到每个 Pod/Container 申请的 mGPU Resource 资源,此时我们根据 PreStartContainerRequest 里的 DevicesIDs 找到与之匹配的 Pod 和 Container,从 Annotation 中拿到分配好的 GPU 卡,在节点上持久化 DevicesIDs 和 GPU 卡之间的映射关系。
  • mGPU Driver 在启动容器前 ,先从容器环境变量中得到算力、显存的大小以及 DevicesIDs 的相关信息,根据节点上持久化的 DevicesIDs 和 GPU 卡之间的映射关系,就能得到调度器的 GPU 卡分配结果,从而将这些资源分配给容器。

资源监控

在早期的 GPU 监控中,我们会使用一些 NVML 工具来对 GPU 卡的基本信息进行采集,并持久化到监控系统的数据存储层,比如通过 nvidia-smi 这样的命令也是可以获取到 GPU 的基本信息的。

但随着整个 AI 市场的发展和成熟,对于 GPU 的监控也越来越需要一套标准化的工具体系,例如 NVIDIA 提供的 dcgm-exporter 套件。 而在 mGPU 共享场景下,我们还需要做到 Pod/容器/mGPU 级别的资源监控。

NVIDIA 官方实现了一个 DCGM-Exporter 组件,可以通过 DCGM 收集节点上 GPU 的各项指标,并通过 HTTP 接口暴露出去,常见指标如下:

picture.image

在原生的 GPU 独占场景,GPU 的利用率情况即代表的容器的利用率情况。而在我们的共享 GPU 场景下,每张 GPU 是会被分配给多个容器的,因此除了上述的 GPU 卡指标外,我们还希望能够获取到 每个 Pod/容器的 GPU 使用率、内存使用量

当然,NVIDIA 肯定没有直接提供相关的接口获取这些信息,我们需要通过其他技术手段的配合来达到这个目的。

众所周知,通过 NVML 接口可以拿到每个进程在每张卡上的 GPU 使用率、显存使用量,如果我们能拿到容器和进程之间的匹配关系,就可以通过对容器内的每个进程的 GPU 使用量进行求和,从而得到整个容器的 GPU 使用量。幸运的是,通过配合使用 Cgroup,这个目标可以被实现。具体方式如下:

picture.image

  • 通过 Cgroup fs 目录树结构,在组件内构造出 PodsUID → ContainersUID → Pids 的三层树结构,从而可以得到每个 Pod、每个 Container 中包含的进程。
  • 通过 NVML 接口,获取每张卡上每个进程使用的 GPU 资源,包括 GPU 利用率、显存利用率、显存使用量等。
  • 通过 APIServer Watch 本节点的 Pods,从 Pod 信息可以获取 Pod/Container 的 GPU 分配量,Name 和 UID 的映射关系,定时遍历每个 Pod,统计求和每个 Pod/Container 中所有进程的 GPU 利用率、显存利用率、显存使用量等指标并记录在内存中。
  • 通过 HTTP 接口将所有收集的指标暴露。
  • 采集到 Prometheus 监控。

picture.image

以上就是火山引擎 mGPU 节点资源管理方案的概述,欢迎企业用户扫描以下二维码开通服务并体验。

高成本的 AI 算力资源,亟需技术手段帮助企业降本增效,火山引擎 mGPU 立足云原生异构计算领域,创新式提供 GPU 资源强隔离能力,强化 GPU 资源细粒度感知和调度,释放 AI 算力,持续给企业带来巨大商业价值。

picture.image

扫码立即咨询

  • END -

相关链接

[1] 火山引擎 mGPU: www.volcengine.com/docs/6460/132501

[2] 火山引擎 VKE: www.volcengine.com/product/vke

[3] mGPU 技术揭秘 :新一代 Kubernetes GPU 共享调度方案

[4] 大模型时代,企业如何榨干每一块 GPU?

活动回放

在【字节跳动云原生】公众号菜单[技术沙龙]选择第三期活动,或在后台回复“第三期”,可直接查看回放链接和讲师 PPT!

picture.image

61
0
0
0
关于作者
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论