根据 Gartner 对全球 CIO 的调查结果显示,人工智能将成为 2019 年组织革命的颠覆性力量。对于人工智能来讲,算力即正义,成本即能力,利用 Docker 和 Kubernetes 表明云原生技术为 AI 提供了一种新的工做模式,将 GPU 机器放到统一的资源池进行调度和管理,这避免了GPU 资源利用率低下和人工管理的成本。所以,全球主要的容器集群服务厂商 Kubernetes 都提供了 Nvidia GPU 容器集群调度能力,可是一般都是将一个 GPU 卡分配给一个容器。这虽然能够实现比较好的隔离性,确保使用 GPU 的应用不会被其余应用影响;对于深度学习模型训练的场景也很是适合,可是,针对模型开发和模型预测的场景仍是会显得比较浪费。基于此,你们有了共享 GPU 的集群调度需求。git
共享 GPU 的集群调度就是可以让更多的模型开发和预测服务共享同一个 GPU 卡,进而提升集群中 Nvidia GPU 的利用率。而这就须要提供 GPU 资源的划分,而这里 GPU 资源划分的维度指的就是 GPU 显存和 Cuda Kernel 线程的划分。一般在集群级别谈支持共享 GPU 是如下两件事情:github
1.调度api
2.隔离,咱们这里主要讨论的是调度,隔离的方案目前须要用户经过应用限制(好比使用 Tensorflow 的per_process_gpu_memory_fraction 来控制),将来会提供基于 Nvidia 的 MPS 的可选项, 也会考虑 GPU 的方案。架构
而对于细粒度的 GPU 卡调度,目前 Kubernetes 社区并无很好的方案,这是因为 Kubernetes 对于 GPU 这类扩展资源的定义仅仅支持整数粒度的加加减减,没法支持复杂资源的分配。好比用户但愿使用 Pod A 占用半张 GPU卡,这在目前 Kubernetes 的架构设计中没法实现资源分配的记录和调用。这里挑战是多卡 GPU 共享是实际矢量资源问题,而 Extened Resource 是标量资源的描述。app
针对此问题,咱们设计了一个 Out Of Tree 的共享 GPU 调度方案,该方案依赖于 Kubernetes 的现有的工做机制:ide
这个 GPU 共享调度扩展的好处是:利用 Kubernetes 的扩展和插件机制实现,对于 API Server,Scheduler,Controller Manager 以及 Kubelet 等核心组件没有侵入性。这就方便了使用者能够在不一样 Kubernetes 版本上应用这个方案,无需 rebase 代码和从新构建 Kubernetes 二进制包。学习
gpu_options.per_process_gpu_memory_fraction
控制应用的显存使用量。那咱们要解决的问题就先简化到以显存为调度标尺,而且把显存使用的大小以参数的方式传递给容器内部。前提:测试
而咱们的工做首先是定义了两个新的 Extended Resource: 第一个是 gpu-mem, 对应的是 GPU 显存;第二个是 gpu-count,对应的是 GPU 卡数。 经过两个标量资源描述矢量资源, 而且结合这一资源,提供支持共享 GPU 的工做机制。下面是基本的架构图:ui
核心功能模块:人工智能
具体流程:
1. 资源上报
GPU Share Device Plugin 利用 nvml 库查询到 GPU 卡的数量和每张 GPU 卡的显存, 经过ListAndWatch()
将节点的 GPU 总显存(数量 显存)做为另外 Extended Resource 汇报给 Kubelet; Kubelet 进一步汇报给 Kubernetes API Server。 举例说明,若是节点含有两块 GPU 卡,而且每块卡包含 16276MiB,从用户的角度来看:该节点的 GPU 资源为 16276 2 = 32552; 同时也会将节点上的 GPU 卡数量 2 做为另一个 Extended Resource 上报。
2. 扩展调度
GPU Share Scheduler Extender 能够在分配 gpu-mem 给 Pod 的同时将分配信息以 annotation 的形式保留在 Pod spec 中,而且在过滤时刻根据此信息判断每张卡是否包含足够可用的 gpu-mem 分配。
2.1 Kubernetes 默认调度器在进行完全部过滤(filter)行为后会经过 http 方式调用 GPU Share Scheduler Extender的filter 方法, 这是因为默认调度器计算 Extended Resource 时,只能判断资源总量是否有知足需求的空闲资源,没法具体判断单张卡上是否知足需求;因此就须要由 GPU Share Scheduler Extender 检查单张卡上是否含有可用资源。
如下图为例, 在由 3 个包含两块 GPU 卡的节点组成的 Kubernetes 集群中,当用户申请gpu-mem=8138
时,默认调度器会扫描全部节点,发现 N1 所剩的资源为 (16276 * 2 - 16276 -12207 = 4069 )不知足资源需求,N1 节点被过滤掉。
而 N2 和 N3 节点所剩资源都为 8138MiB,从总体调度的角度看,都符合默认调度器的条件;此时默认调度器会委托 GPU Share Scheduler Extender 进行二次过滤,在二次过滤中,GPU Share Scheduler Extender 须要判断单张卡是否知足调度需求,在查看 N2 节点时发现该节点虽然有 8138MiB 可用资源,可是落到每张卡上看,GPU0 和分别 GPU1 只有 4069MiB 的可用资源,没法知足单卡 8138MiB 的诉求。而 N3 节点虽然也是总共有 8138MiB 可用资源,可是这些可用资源都属于 GPU0,知足单卡可调度的需求。由此,经过 GPU Share Scheduler Extender 的筛选就能够实现精准的条件筛选。
2.2 当调度器找到知足条件的节点,就会委托 GPU Share Scheduler Extender 的 bind 方法进行节点和 Pod 的绑定,这里 Extender 须要作的是两件事情
ALIYUN_COM_GPU_MEM_IDX
保存到 Pod 的 annotation 中;同时也保存该 Pod 申请的 GPU Memory 做为ALIYUN_COM_GPU_MEM_POD
和ALIYUN_COM_GPU_MEM_ASSUME_TIME
保存至 Pod 的 annotation 中,而且在此时进行 Pod 和所选节点的绑定。注意:这时还会保存ALIYUN_COM_GPU_MEM_ASSIGNED
的 Pod annotation,它被初始化为“false”。它表示该 Pod 在调度时刻被指定到了某块 GPU 卡,可是并无真正在节点上建立该 Pod。ALIYUN_COM_GPU_MEM_ASSUME_TIME
表明了指定
时间。
若是此时发现分配节点上没有 GPU 资源符合条件,此时不进行绑定,直接不报错退出,默认调度器会在 assume 超时后从新调度。
如下图为例,当 GPU Share Scheduler Extender 要把 gpu-mem:8138 的 Pod 和通过筛选出来的节点 N1 绑定,首先会比较不一样 GPU 的可用资源,分别为 GPU0(12207),GPU1(8138),GPU2(4069),GPU3(16276),其中 GPU2 所剩资源不知足需求,被舍弃掉;而另外三个知足条件的 GPU 中, GPU1 偏偏是符合空闲资源知足条件但同时又是所剩资源最少的 GPU 卡,所以 GPU1 被选出。
3. 节点上运行
当 Pod 和节点绑定的事件被 Kubelet 接收到后,Kubelet 就会在节点上建立真正的 Pod 实体,在这个过程当中, Kubelet 会调用 GPU Share Device Plugin 的Allocate
方法, Allocate
方法的参数是 Pod 申请的 gpu-mem。而在Allocate
方法中,会根据 GPU Share Scheduler Extender 的调度决策运行对应的 Pod
ALIYUN_COM_GPU_MEM_ASSIGNED
为false
的 GPU Share PodALIYUN_COM_GPU_MEM_POD
的数量与 Allocate 申请数量一致的 Pod。若是有多个符合这种条件的 Pod,就会选择其中ALIYUN_COM_GPU_MEM_ASSUME_TIME
最先的 Pod。ALIYUN_COM_GPU_MEM_ASSIGNED
设置为true
,而且将 Pod annotation 中的 GPU 信息转化为环境变量返回给 Kubelet 用以真正的建立 Pod。目前项目已经开源到 github.com 上
gpushare-scheduler-extender
gpushare-device-plugin
请参照部署文档
aliyun.com/gpu-mem
的应用apiVersion: apps/v1 kind: Deployment metadata: name: binpack-1 labels: app: binpack-1 spec: replicas: 1 selector: # define how the deployment finds the pods it manages matchLabels: app: binpack-1 template: # define the pods specifications metadata: labels: app: binpack-1 spec: containers: - name: binpack-1 image: cheyang/gpu-player:v2 resources: limits: # MiB aliyun.com/gpu-mem: 1024
请参照使用文档
请参照如何构建
1: 部署多个 GPU Share 的 Pod,发现他们以 binpack 的方式被放置到同一个 GPU 卡上
视频地址:https://cloud.video.taobao.com//play/u/2987821887/p/2/e/6/t/1/214292079721.mp4
2: 避免错误调度申请资源超过单个 GPU 可用资源的 Pod
视频地址:https://cloud.video.taobao.com//play/u/2987821887/p/2/e/6/t/1/214235285109.mp4
本文为云栖社区原创内容,未经容许不得转载。