GPU虚拟化实践

一、背景

随着AI技术的发展,贝壳内部也有越来越多的场景使用到了机器学习算法提供服务,如每天上班的人脸识别、家装现场的鱼眼监控、经纪人的语音识别以及交易过程中的图片和文本识别等。机器学习模型从开发到应用的基本流程如图1所示,基本可以分为数据准备,模型开发,模型应用等三个大阶段,其中模型开发的训练和模型应用的推理阶段涉及大量的矩阵运算,特别是深度学习模型。因此在这些场景中会使用对矩阵运算具有天生优势的GPU设备来加速。

但是并不是所有阶段都需要大量GPU算力支持,如在算法工程师调试模型阶段,GPU主要用于验证模型是否合理,验证之后的训练才需要大量的算力;在模型推理预测时只有前向传播计算。这些场景下如果独占一张GPU卡会导致资源利用率很低,同时,线上服务又具备波峰和波谷的情况,会进一步降低资源利用率,造成资源浪费,增大AI服务成本。

image-20220410212701786.png

二、GPU虚拟化探索与实践

在上述背景下,需要一种GPU共享的方案,使得多任务或多用户可以共享一张卡的资源,从而提高GPU的利用率,节省公司AI服务成本。目标:

  • 多任务或多用户共享一张GPU卡;
  • 每个任务的GPU资源相互隔离。

在贝壳我们调研并实践了三种方案:阿里gpushare腾讯TKE gpu-manager、以及nvidia官方的Multi-Instance GPU(MIG)。其中gpushare和gpu-manager是结合k8s在软件层面实现的GPU共享,MIG是nvidia从硬件层面上实现的物理隔离。

由于gpushare和gpu-manager的实现都是依赖了k8s的device-plugin机制,所以在介绍gpushare和gpu-manager之前,先简单介绍一下k8s-device-plugin的概念。

1、k8s-device-plugin简介

k8s在v1.10版本引入feature state beta功能的device plugin机制框架,通过该框架可以将系统硬件资源发布到Kubelet。该框架的出现可以让我们在不修改任务k8s组件的情况下引入并使用 GPU、FPGA、高性能 NIC、InfiniBand 等第三方设备资源。 device-plugin本质上是一个grpc服务,其工作时序如下图所示,它主要做几个事情: a622d98fe68c4434b6f13c934793e46d.png

  • 向kubelet注册资源和设备。通过/var/lib/kubelet/device-plugins/kubelet.sock 向 Kubelet 注册,Kubelet 会将这些设备暴露到 Node 状态中,方便后续调度器使用。
  • 当设备发生变化时,向kubelet更新。
  • 接受kubelet申请第三方资源,并以参数的形式返回给kubelet。

如nvidia官方提供的k8s-device-plugin可以将GPU资源注册为nvidia.com/gpu,在使用GPU资源时,yaml中下申请即可。

resources:
  requests:
    nvidia.com/gpu: 1
  limits:
    nvidia.com/gpu: 1
复制代码

2、阿里gpushare

2.1 方案介绍

该方案设计框架如图所示,主要包含两个核心模块: image-20220418143546086.png

  • GPU Share Device Plugin
    • 利用Device Plugin机制,将GPU的显存注册为aliyun.com/gpu-mem,GPU的数量注册为aliyun.com/gpu-count,如一个单机两16G V100的节点,注册之后节点可分配资源为aliyun.com/gpu-mem=31260,aliyun.com/gpu-count=2。(此处显存是以Mi为粒度,也可Gi为粒度)
    • 在节点上被Kubelet调用负责GPU卡的分配,依赖scheduler Extender分配结果执行。
  • GPU Share Scheduler Extender: 它是一个k8s的调度器扩展,主要作用是:
    • 在默认调度器完成所有过滤(Filter)后,会调用该扩展来判断节点上单个GPU卡是否能够提供足够的gpu-mem(因为默认调度器只能判断单节点是否满足,但是不能判断单卡是否满足);
    • 在Bind阶段将GPU的分配结果通过annotation记录到Pod Spec以供后续Filter检查分配结果。
2.2 测试与实践:
  • 测试 同一个任务运行两个实例: job1:
    resources:
      limits:
        aliyun.com/gpu-mem: 8065
    复制代码
    job2:
    resources:
      limits:
        aliyun.com/gpu-mem: 8065
    复制代码
    其运行结果如下,从图中可以看出job1和job2调度到了gpu0上,实现了GPU的共享。

image2020-5-6_13-58-19-1650266550296.png

  • 实践

    该方案目前应用到线上推理服务,其结构图如下:

image-20220421210034806.png

2.3 缺陷:
  • 没有暴露每个容器GPU资源利用率的指标;
  • 只是在调度上实现了GPU显存粒度的虚拟化,没有实现资源隔离。

3、腾讯gpu-manager

3.1 方案介绍

该方案和sharegpu基本逻辑类似,也是基于k8s的device-plugin向k8s注册GPU资源,但其对每个容器的资源进行了隔离,包括显存和算力,其基本结构如图所示。

image-20220418153547774.png

gpu-manager主要包含3个模块:

  • device-plugin模块将GPU的显存注册为tencent.com/vcuda-memory,算力注册为tencent.com/vcuda-core,一张卡的算力tencent.com/vcuda-core=100,显存1G对应tencent.com/vcuda-memory=4。如一个单机两16G V100的节点,注册之后节点可分配资源为tencent.com/vcuda-memory=32*4=128,tencent.com/vcuda-core=100。

  • gpu-admission模块针对多个pod同时申请相同资源时出现排队情况做了优化,会按照申请时间分配。

  • vcuda-controller模块是cuda标准库的一个封装,它会拦截应用的资源申请并判断资源申请是否合理,如果申请不合理会返回申请失败,该模块也是gpu-manager实现资源隔离的核心。其显存和算力隔离原理图如下。

    其中显存隔离是属于硬隔离,容器实际使用量不能超出限制值;算力隔离属于软隔离,其实际使用量会在限制值上下波动,但是平均值基本满足限制条件。

    • 显存隔离

image-20220421201619172.png - 算力隔离

image-20220421201627958.png

3.2 测试与实践
  • 测试 同一个训练任务分别用如下3个配置运行: job1:
    resources:
      limits:
        tencent.com/vcuda-core: 100
        tencent.com/vcuda-memory: 64  
    复制代码
    job2:
    resources:
      limits:
        tencent.com/vcuda-core: 40
        tencent.com/vcuda-memory: 24 
    复制代码
    job3:
    resources:
      limits:
        tencent.com/vcuda-core: 30
        tencent.com/vcuda-memory: 24 
    复制代码
    运行结果如下:job1独占gpu1,job2和job3共享gpu0,实现了gpu共享。

image-20220416153342777.png 下面是分别是三个任务的gpu利用率监控,独占一张卡的job1平均利用率在60%左右,job2和job3的平均利用率在30%和40%左右,可以看出实现了资源隔离。但是由于其对算力的隔离是软隔离,所以瞬时利用率会上下波动。

image-20220416152031694.png

image-20220416152103916.png

  • 实践 基于gpu-manager实现了GPU虚拟开发机,其实现如下图所示。该项目在节省资源的同时为用户提供GPU资源的支持。
3.3 缺陷
  • 由于该方案是依赖cuda库函数,对少部分cuda版本支持不足。

3、nvidia mig

该方案是nvidia官方在硬件层面实现的GPU资源隔离,每个GPU实例都具有独立的片上端口(对应独立的系统内存路径)、L2缓存、存储控制器、DRMA地址总线。

3.1 方案介绍

基本概念:

GPU Engine:引擎

GPU Memory Slice:一个内存片占用总内存的1/8

GPU SM Slice:流式处理器片,一个流式处理器占总处理器的1/7

GPU Slice:=GPU Memory Slice+GPU SM Slice

GPU Instance:=GPU Slices+GPU Engines

Compute Instance:GPU instance可以分成讴歌Compute Instance,就是它的子集,也就是一个GI又能分成多个CI,但是多个CI是共享这个GI的engine和memory,SM是隔离的。

  • GPU实例结构图,其是由M个GPU Engine和N个GPU Slice组成。

image-20220419200241187.png

  • Compute实例结构图,它是在GPU实例上创建的,所以一个GPU实例又可以创建多个Compute实例。注意:同一个GPU实例创建的Compute实例的内存是共享的。

image-20220419200415831.png

3.2 测试与实践
  • 创建mig实例:
    # 开启mig功能
    nvidia-smi -i 0 -mig 1
    # 创建gpu实例,—C 表示同时创建compute实例,2g表示两个计算引擎,10gb表示10G显存。
    nvidia-smi mig -cgi 2g.10gb -C
    复制代码
  • 测试 job1(单卡):
    resources:
      limits:
        nvidia.com/gpu: 1 
    复制代码
    job2(mig):
    resources:
      limits:
        nvidia.com/mig-2g.10gb: 1
    复制代码
    如下是两个训练任务的GPU利用率监控,能够看出MIG实现了资源隔离,并且在隔离效果上比gpu-manager要好:

image-20220419205323317.png

image-20220419205253370.png MIG方案目前初步应用在GPU资源需求较小的训练任务中,还为大规模实践。

3.3 缺陷
  • MIG目前支持A100和A30显卡;
  • MIG实例的资源规格不能随意划分,对A100最多只能划分成7个1g.5gb实例,A30最多只能划分成4个1g.6gb实例。

三、总结与规划

总体上来说三个方案各有优缺点,由于应用的场景不同,目前我们主要在推理和GPU虚拟开发机中分别使用了gpushare和gpu-manager。而nvidia的硬件隔离方案限制A100和A30显卡且资源划分尺寸较大,目前应用范围较小。

未来考虑结合软件隔离和硬件隔离,在MIG的基础上实现进一步的资源任意划分。

猜你喜欢

转载自juejin.im/post/7097871172770988063