如何用 K8S 高效完成 AI 训练任务
作者:陆龙文
才云科技
2017-12-19 07:08


陆龙文 / 「七牛云」资深后端工程师
嘉宾介绍:
「七牛云」资深后端研发,专注容器云平台的产品化实践和探索,目前致力于 kubernetes 与七牛自有业务的整合落地。

「七牛云」的 AI 部门属于容器云部门的客户,针对于 AI 训练这样一个特殊的训练场景,具体落实到 Kubernetes 的实践上具体实施工作怎样做的,究竟会带给「七牛云」怎样的好处,以及从中碰到一些什么样的问题呢?


AI 训练的业务情况

「七牛云」本身有一个深度学习的平台,这是一个端到端的深度学习平台,包括从对原始富媒体数据的打标,到制成一个可以被训练任务读取的样本集,到训练任务的触发以及训练成果的存储,包括对于最后训练出来模型的评估,评估完成以后最后将这个模型打包成你的线上业务,通过 API 形式对外提供服务一整套流程的平台。 AI 训练是这个平台中的一个部分。


AI 训练迭代是怎样的一个事情?


AI训练迭代分两个阶段:

  • 第一,样本集的生成,任务输入是两个:一是来自于「七牛云」对象存储的原始数据,主要是一些图片、音视频流富媒体数据;二是 AVA 平台本身有一个打标系统,可以对原始数据进行标签,通过样本生成器生成样本集,存储到容器云平台的存储当中,这是一个分布式的网络存储。
  • 第二,一旦你的样本集生成完成以后会自动触发或者人工触发一个训练任务进行一个训练,读取整个平台由算法工程师事前准备的算法模型、训练参数到你的训练任务当中进行训练,最后将你的训练任务输出到存储,最后上传到对象存储当中去的整个过程。

Kubernetes 的优势

AP 训练的痛点

我们这边遇到的痛点是什么?


  • 第一,使用 Kubernetes 做平台之前,训练流程上需要算法工程师通过脚本、控制训练任务的触发以及训练任务要存储到什么地方,同时训练任务可能因为一些硬件错误导致失败,失败需要人工介入。
  • 第二,资源规划方面,GPU 集群是很多人共享的,其中 GPU 资源需要人为协调,耗费掉很多精力。
  • 第三,训练任务完成以后并没有把占用的 GPU 释放掉,造成一定的资源浪费。
  • 第四,存储,训练任务的存储往往是非常大的样本集,需要容量非常大的网络存储支撑,在此之前我用的是 NFS,服务可用性没有办法达到需求,水平扩展以及性能也没有办法满足训练任务的要求。

Kubernetes 的优势

Kubernetes 主要有三个优势:


  • 第一,Kubernetes 支持 GPU 调度的,我们积极将整个实践过程当中取得的成果回馈到社区;
  • 第二,Kubernetes 支持多种 Workload 的调度方式,适应不同的业务场景,JOB 与训练任务两者切合度非常高。
  • 第三,Kubernetes 和现在开源社区结合非常好,包括监控日志方案社区已经取得了相当成果,在搭建平台的时候这个部分省了很多人力。

基于 Kubernetes 的 AI 训练

生成样本集

整个调度仅仅利用 Kubernetes 调度没有办法很好的满足业务需求,我们对样本集的生成有 Sample Job Controller 做整个样本集生成任务的调度,生成任务从对象存储和 Mongo 数据库中读取输入数据,产出一个样本集输入到 CEPH 存储。

启动训练任务

以上任务完成以后会触发一个 Training Job Controller 的训练任务,这个训练任务从刚刚的样本集里读取数据,同时配合算法模型和训练参数,对于算法模型的权重进行计算,最后训练完成以后再将新的算法模型输出到 CEPH 存储当中,如果评估下来比较好的话可以上传到对象存储当中,CEPH 这部分存储资源可以释放掉了。


使用 CEPH 存储

使用的 CEPH 存储训练 AI 训练任务场景主要有三个好处:

第一,数据规模可以支持非常大,最大样本集可以达到一个样本集 10T 数据,需要读取数据服务一定需要有一个网络共享来支撑,这样在物理机发生故障时,Pod 在别处被重启后仍然能访问之前的数据;

  • 第二,CEPH 存储是分布式存储,水平扩展性非常良好,训练级规模上升以后可以很快速的进行水平扩展;
  • 第三,读写控制,Kubernetes 一个独占的读写和多个 Pod 同时读取的模型,适用于训练模型的整个流程,包括之前样本集生成有一个样本生成,一旦完成以后可以进入只读模式,多个任务同时读取进行并发训练。

CEPH on Kubernetes 的改进
CEPH 使用过程中我们把积极地改进回馈给社区,比如说 Image Format 2 的支持,还有 Kubernetes 对 CEPH 调度需要有一个 Provisioner 去支持的,现在整个社区演进方向希望将这些存储 Provisioner 全部变成独立部署的形式,便于它的升级扩展。


GPU 资源规划与分配

GPU 资源规划采用 Node Label+Node Selector,对训练任务进行调度,我们的 GPU 卡可能有不同的型号,对不同训练任务会有型号上的偏好,这个时候可以为每一台机器上装的具体型号的显卡帮助它打上一个标签,之后进行训练任务调度的时候可以使用 Node Selector 将这个任务调度上去。

关于资源方面的,Kubernetes 提供了比较好的 Limits + Request 资源分配模型,Limits 表示这个 Pod 最多使用多少资源,Request 是说要将这个任务调度起来最少需要多少资源,目前对于 GPU 这样的模型没有办法很好的工作,我们缺少一个有效的机制监控 GPU 使用多少,限制对 GPU 使用,对于 CPU 和 Memory 可以有效的使用这样的模型,进行合理超卖,提资源的利用率。


Nbidia GPU Driver

关于 Nvidia GPU Driver,训练任务需要在 Pod 当中使用具体显卡的驱动,每一台机器安装不同型号的显卡驱动版本也是不一样的,但是我们 Pod 并不关心这个版本,只是调度到这台机器上就需要这台机器上对应型号显卡的驱动,我就可以通过 Kubernetes 的 Hostpath 方式挂载到 Pod 上去,打包镜像的时候完全不需要关心 GPU 驱动这个事情。


监控方案和日志方案

关于监控和日志方案采用的是 Prometheus,它本身提供的 Prometheus Node Exporter 可以很好的帮助我们关注整个集群里物理机结构的信息,Kubelet 里已经集成 Cadvisor 帮助我们提供容器内部的监控信息,我们还在上面做了一个改进就是将 GPU 的监控信息添加到监控方案当中去,并且贡献给社区。


关于日志方案我们采用了由「七牛云」自主研发的分片的 Elastic Search 自研的 Sharding 集群,承载了目前所有「七牛云」的业务数据以及包括外部客户的数据,把 Elastic Search运维的工作完全交付给「七牛云」Pandora 日志存储分析平台。


一次“踩坑”经历

接下来分享一下我们在运维当中碰到的问题,最后造成很严重后果的事故。

容器挂载 CEPH 过程

首先介绍要一下 Kubernetes 使用 CEPH 存储是怎样一个过程?


我们知道 CEPH 存储是通过 CEPH 的 RBD 命令,将 CEPH 的 Image Attach 到你的宿主机成为一个块设备,Kubernetes 将这个设备 Mount 到 Kubelet 的 Pulgins 文件夹下面,再次通过 Mount Rbind 的方式绑定到对应需要的 Pod 的目录下,这是 CEPH Image 绑定到 Pod 的过程。Mount Rbind 是挂载命令,本身是有三种模式:Shared、Slave、Private,Kubernetes 在使用的时候是 1.6,仅仅支持 Private 模式。


故障过程

因为这样一个原因导致了故障的发生:我们有一个容器 A 已经运行起来了,是只读的方式挂载了一个存储,接下来 Node Exporter 要进行监控采集,为了获取某些监控数