AI 训练迭代分两个阶段:
第一,样本集的生成,任务输入是两个:一是来自于七牛对象存储的原始数据,主要是一些图片、音视频流富媒体数据;二是 ava 平台本身有一个打标系统,可以对原始数据进行标签,通过样本生成器生成样本集,存储到容器云平台的存储当中,这是一个分布式的网络存储。
第二,一旦你的样本集生成完成以后会自动触发或者人工触发一个训练任务进行一个训练,读取整个平台由算法工程师事前准备的算法模型、训练参数到你的训练任务当中进行 。
训练,最后将你的训练任务输出到存储,最后上传到对象存储当中去的整个过程。
第一,使用 Kubernetes 做平台之前,训练流程上需要算法工程师通过脚本、控制训练任务的触发以及训练任务要存储到什么地方,同时训练任务可能因为一些硬件错误导致失败,失败需要人工介入。
第二,资源规划方面,GPU 集群是很多人共享的,其中 GPU 资源需要人为协调,耗费掉很多精力。
第三,训练任务完成以后并没有把占用的 GPU 释放掉,造成一定的资源浪费。
第四,存储,训练任务的存储往往是非常大的样本集,需要容量非常大的网络存储支撑,在此之前我是用的是 NFS,服务可用性没有办法达到需求,水平扩展以及性能也没有办法满足训练任务的要求。
第一,k8s 支持 GPU 调度的,我们积极将整个实践过程当中取得的成果回馈到社区;
第二,k8s 支持多种 Workload 的调度方式,适应不同的业务场景,JOB 与训练任务两者切合度非常高。
k8s 和现在开源社区结合非常好,包括监控日志方案社区已经取得了相当成果,在搭建平台的时候这个部分省了很多人力。
第一,数据规模可以支持非常大,最大样本集可以达到一个样本集 10T 数据,需要读取数据服务一定需要有一个网络共享来支撑,这样在物理机发生故障时,pod 在别处被重启后仍然能访问之前的数据;
第三,读写控制,Kubernetes 一个独占的读写和多个 Pod 同时读取的模型,适用于训练模型的整个流程,包括之前样本集生成有一个样本生成,一旦完成以后可以进入只读模式,多个任务同时读取进行并发训练。CEPH 使用过程中我们把积极地改进回馈给社区,比如说 ImageFormat2 的支持,还有 k8s 对 CEPH 调度需要有一个 Provisioner 去支持的,现在整个社区演进方向希望将这些存储 Provisioner 全部变成独立部署的形式,便于它的升级扩展。
GPU资源规划采用Node Label+Node Selector,对训练任务进行调度,我们的 GPU 卡可能有不同的型号,对不同训练任务会有型号上的偏好,这个时候可以为每一台机器上装的具体型号的显卡帮助它打上一个标签,之后进行训练任务调度的时候可以使用 Node Selector 将这个任务调度上去。
关于资源方面的,Kubernetes 提供了比较好的 Limits+request 资源分配模型,Limits 表示这个 Pod 最多使用多少资源,Request 是说要将这个任务调度起来最少需要多少资源,目前对于 GPU 这样的模型没有办法很好的工作,我们缺少一个有效的机制监控 GPU 使用多少,限制对 GPU 使用,对于 CPU 和 Memory 可以有效的使用这样的模型,进行合理超卖,提资源的利用率。
关于 Nvidia GPU Driver,训练任务需要在 Pod 当中使用具体显卡的驱动,每一台机器安装不
同型号的显卡驱动版本也是不一样的,但是我们 Pod 并不关心这个版本,只是调度到这台机器上就需要这台机器上对应型号显卡的驱动,我就可以通过 k8s 的 Hostpath 方式挂载到 Pod上去,打包镜像的时候完全不需要关心 GPU 驱动这个事情。
- 基于 Prometheus Node Exporter
- 获取 CPU、内存、磁盘、网络维度信息
容器监控
- kubelet 内嵌 cadvisor
监控注册
- Prometheus 从 kubernetes apiserver 获取需监控的资源
GPU 监控
- GPU 使用率
- 现存使用率
- GPU核心使用率
关于日志方案我们采用了由七牛自主研发的分片的 Elastic Search 自研的 Sharding 集群,承载了目前所有七牛的业务数据以及包括外部客户的数据,把 Elastic Search 运维的工作完全交付给七牛 pandora 日志存储分析平台。
4、一次踩坑经历
因为这样一个原因导致了故障的发生:我们有一个容器 A 已经运行起来了,是只读的方式挂载了一个存储,接下来 Node Exporter 要进行监控采集,为了获取某些监控数据,会以 Make-private 挂载整个宿主机根目录,刚才提到已经有容器 A 将 RBD Volume 挂在起来了,势必之后的挂载也将这个带到了 Promethus 的 Node Exporter,导致了 A 容器运行完成以后将容器销毁了,卸载 RBD Volume 成功,但是 RBD umap 失败,主要是因为 Node-Exporter 仍在在 RO 挂载。我们也没有发现这个问题。
整个故障原因就是 Node-exporter 挂载根目录方式比较危险,导致了之后挂载 ceph 卷失败。挂载卷失败这原本不是一个太严重的问题,也是因为我们之前部署上的问题导致了 ceph 服务端和客户端版本不一致,致使获取文件信息失败,本身 k8s 对这种情况处理方式简单粗暴,种种原因放在一起导致了整个事故的发生。反思这次事故,主要有几个点:
第一,部署流程需要固化,因为我们每次部署都要需要人手工操作,一些配置文件都是当时修改出来的,要把整个部署流程固化下来,在部署完成以后要检查相应的版本。
我们接下来的作也把我们现在关于 Kubernetes 运维经验产品化以后变成七牛公有云的平台,会在 11 月份上线 preview 版本。