大家好,欢迎大家来参加本次的分享我是Vivo的吴紫阳我今天的分享主题是Vivo AI计算平台的K8s实践那么今天的分享会分为以下四个部分首先我们来看一下背景那么右边这幅图是OpenAI在20年做的一个调查是关于行业内这段规模的深度学习训练所需要计算量那么我们可以看到这个曲线在12年之前它是满足蜂蜜定律的也就是说计算量每两年会增长一倍但在12年之后随着深度学习技术的突飞猛进这个曲线发生了变化现在每年会增长10倍这是一个很夸张的数字那么我们Vivo AI从17年以来开始建设我们的算力到现在规模已经达到了几百p flows那么也有几千张卡的一个规模量那面对这样海量的算力其实是有一系列的工程挑战的主要有三个方面第一就是算力的应用性它指我们能否低门砍的快速的使用大量的算力这里有个案例就比如说在早期的时候我们训练都是跑在单机上的但是单机由于配置是有硬件上线的那么其实它最终会存在一个瓶颈要突破这个性能瓶颈的话呢就必须通过分布式训练来使用多机的算力但是这工程门砍其实是比较高的而第二点就是算力的灵活调度就是说我们怎么样把海量的算力分配给各个业务来满足他们各个业务的算力需求这里有个例子是当前比较火的领域是预训练大模型而它的训练其实需要大量的算力可能需要几百张卡那么在资源时使用比较饱和的情况下怎么来满足这个需求呢而第三点就是算力的高效利用它使得是我们把算力分配给应用或者业务之后它能否把这个资源使用好那比如说在广告推荐场景下的一个训练和推理它其实都有一定的性能的优化的空间的那我们是通过建设了AI计算平台来解决以上的这些挑战接下来给大家介绍的就是我们平台的一个盖览我们AI计算平台主要分成三大模块第一模块是AI训练平台它的核心能力是分布式训练和交互式调试那它面向的用户是我们的算法工程师AI推理平台的核心能力是推理框架和推理加速它面向的是我们的后端开发工程师第三个模块是AI容器平台它有资源调度算力超卖的核心能力它主要是对上负能给上层的各个平台同时它也对接了公司的CICD部署平台这样子我们的在线业务也好理线训练也好都是以容器化的方式跑在我们的KBS进训里面那么刚才提到的算力用性其实就是通过我们训练平台的分布式训练和交互式调试的能力让我们的算法工程师能够快速地使用好大量的算力而算力的灵活调度方面刚才提到我们会基于容器来做资源调度通过容器我们能够以更稀利度和更加灵活的单位来划分这个资源同时我们会把在线和离线的资源放在同一个资源池里面这样子能够灵活的在在离线之间来调配这些资源最后我们还建设了混合运的能力来满足临时的大量算力的一个需求而第三大点就是算力的高效利用的挑战方面我们在两个方面做了建设在资源分配上我们通过弹性伸缩和算力插迈让资源的分配能够更加有效按需来分配而在资源使用方面就是我们当资源分配给你了之后我们通过训练和推理加速训练容错等来支持业务让业务能更好的把资源利用好那以下一页展示的就是我们平台的技术选型可以看到在各个方向上我们都选择了对应的一些开源的项目那么这些项目或者说是技术在整个行业内也被拐放的使用比如说在容器边台上我们选择了 Kubernetes那么 Kubernetes 它是行业内容器编排的事实标准也是各个 AI 平台它所选择的一个容器底座而在技术选型上我们主要会考虑一下三个方面第一点就是这个技术或者项目的一个是否能满足需求第二就是它的可闻护性第三就是它的可扩展性那么第一点我们展开来讲的话就是说怎么判断这个项目能够满足当前的需求或者是甚至未来的需求呢当前的需求的话我们主要就是要了解这个StreamMood的功能而且并对这个项目做一些实践的调研并且和作验证而未来的需求的话主要会看两个方面第一个就是项目的可扩展性那这点我们放在后面再讲第二点呢就是我们会看这个项目它社区的活跃度它社区是否有清晰的路线图因为一个活跃的社区的话它才能支撑这个项目未来持续的迭代发展那么第二大点刚才提到的就是一个可闻护性那么可闻护性的话我们会考察目前这个项目的成熟度如果是还是处于比较早期的项目的话一般来说它的稳定性会比较弱健壮性也比较差那么处于稳比较成熟的一个阶段的一个项目的话用起来可能就更加放心同时我们还会考虑这个项目的设计以及它的工程质量一个设计比较好工程质量比较高的项目的话回顾起来也更加方便简单第三点也是刚才提到的一个社区活跃度的就是说假如这个社区是比较活跃的话那么发生问题的话地板来说在社区都能找到解决的方案那么第三大点就刚才提到的可扩展性的话会有两个层面一个层面是功能上的可扩展性也就是说我们常说的Extensible那么就是说我们能否很好的很便捷地对这个项目进行点这个层面要讲的就是规模上的可扩展性一般叫做Scalability也就是说随着我们应用的规模的扩大之后这个项目是否还能满足需求是否还能保持健壮和稳定性那么以上这些因素就是我们在做技术血型上会考虑的点那么接下来就给大家展开讲讲我们在KBIS上的一些实践的一个经验首先我们介绍的是个P调度的能力像我们的分布式训练其实主要采用的是Rainwater Reduce的一个算法它是个同步的模式那么就意味着说我当我一个任务有多个Walker的时候需要这些Walker都已经跑起来了任务还才会启动比如说我们有个任务是包含了8个Walker每个Walker其实就是一个POD那需要当这8个POD都原生的Coopers Scheduler其实存在一些问题主要有两点第一就是它只支持单体的调度这什么意思呢就是说它有多个POD的时候下它为一个POD来进行调度那么在刚才的场景下就会存在一个问题可能我8个POD可能只有前面6个POD被调度了到第7个POD调度的时候没有资源了那这时候我这个任务是没法跑起来的但它却白白占了6个POD的资源还有另外一个问题就是调度顺序的问题像我算法工程师提交任务可能提交了A任务之后再提交B任务那么我们这个会预期说A任务先跑起来但实际上却不是这样子有可能会B先跑起来这是为什么呢这是因为Coopers Scheduler原生调度器里面有一个BIFF的一个机制这个机制就是到说当你这个POD调度失败几次之后它会把你放到一个BIFF的队列里面然后过段时间再从你这个POD把拿到了一个调度队里面重新做调度那么这就会导致说刚才出现的一个问题那为了解决以上的问题呢我们就引入了社区的Coopers Batch它是一个P调度器它在Vocano、Coopers Flow等项目里面也被应用那么它的P调度能力指的是说它能把多个POD当成一个整体来做调度从而解决上面的一个单体调度的问题同时它还有一些高级特性比如说它支持多队列、配合管理也支持抢占还在任务优先级方面它支持的DIF算法这个算法主要是保证说当我有多种类型资源的时候它能够把多种类型的资源都使用的比较好不会出现一个情邪举个例子说当我有CPU和内存的时候不会说我CPU都用的比较满的内存就比较浪费的情况那么右边这幅图是整个Batch的一个架构图它主要包含四个模块第一个模块是Action模块主要就是说调度的步骤目前是包含了四个步骤第二个部分是一个Session模块是一个绘画在每一个调度周期的时候它都会New一个绘画来维护这里面的一些数据结构第三个部分是Client就是插件它原生已经有了一些定义好的插件同时我们也可以通过实现一个预定义的一些函数来加一个新的插件从而扩展这个调度期的能力第四个模块是CashCash里面维护了一些数据结构主要是反映了当前集群的资源使用情况那么这个资源这数据结构的数据跟实际情况一致是十分重要的否则调度就会出错那么接下来我们讲一讲我们平台是怎么使用HoverBatch的就我们平台首先它只使用了它的核心特性P调度的能力而对于它一些高级特性其实我们是不会使用的我们会在上层的平台重新实现那么同时我们也会把调度优先级的算法做了简化只基于任务的优先级和时间来做一个排序那么之所以这么做呢主要是希望降低整个调度的一个复杂度和维护成本因为其实整个调度期的逻辑已经是特别复杂了如果我们还要把一些高级特性给利用上的话出了问题其实特别不好排查也不好维护可能这个调度期的代码就只有我一个能看得懂团队其他人都没法维护那么通过把这个化分成不同的模块像它只负责最核心的批调度能力而把一些其他的能力在其他在上层平台实现的话能够有效的降低这个调度期的一个复杂度那么实际在我们应用中也踩过一些坑比如说我们出现cash和实际的资源不一致的情况导致整个pod调度上去之后包了uninspected admission的一个错误那么同时我们也遇到一些计算逻辑有误导致这个调度期crash或者有时候出现有资源没有pub没被调度的一个情况那么由此可见这个调度期其实是比较复杂的那么所以说我们的建议就是说大家不要从零开始写一个新的调度期而可以使用scheduling famo来做因为其实像couple batch的话他作者是华为的一个资深架构师同时他也是couple netis调度兴趣小组的一个前负责人他本身是对调度期的原理和实现都特别深入的情况下才有能力从零开始写一个新的调度期但哪怕这样子其实他也有些conda case没有完全覆盖到那么所以说社区也意识到这个问题就是对调度期的扩展其实是比较难的原先的机制也比较单一所以在新版本里面引入了一种叫scheduling famo的一个机制那么通过这个机制我们就能对调度期做很好的扩展那么这里就建议如果大家有扩展需求的话就尽量基于scheduling famo来做而接下来介绍的是一个任务管理的一个特性那么我们是通过tf operator来做的那他是一个couple flow的一个子项目现在这个项目已经重新命名为training operator了这主要是把各种各样不同任务的operator都集成在一起了像以前的话像tf我针对tensor flow可能要有个operator我针对pilot需要有个operator针对mpi要有个operator这样子就会有很多的operator但实际上这些operator的作用都大同小异所以在近期的话社区就把这些operator合成一个新的operator来来做一个简化那么这个tf operator他做的作用呢就是能够管理训练任务他资源的生命周期比如说在提交一个任务之后他会自动去创建对应的资源像pull out service然后当任务结束之后他能自动把这个资源给回收回来同时他会根据pull的状态来做一些处理设置任务的状态比如说当任务pull都被调度起来之后他就会把这个任务的状态设置为running当我这个pull退出了并且退出马是正常的情况下他会把这个任务状态设置为成功那么下面这是一个简单的原理图跟我们普通的controller其实大同小异他会坚定tap drop和pull的一个资源的变动然后通过informer来获取这些变动之后把这个变动放到一个队列里然后最终把这个队列的一个任务拿通过拿出来然后通过sim tap drop这个函数来做处理而接下来讲的是我们平台怎么来使用tap operator首先我们不会把tap drop这个crd直接暴露给用户因为这里面设计的细节比较多所以我们会做一个封装暴露出更加简单利用的一个接口让用户来做配置当用户配置完之后我们会转换成tap drop提交给k8s同时我们也会用这个tap operator来管理不同类型的任务像一开始的话tap operator可能设计是针对tensor flow的但其实我们同时会用它来管理renewal reduce这种类型的一个任务也就是说比如说可能对于renewal reduce这种类型的任务我们就不会设置ps角色而只有work角色了那么这也是为了降低整个复杂度而不用使用多个operator而在我们也做了一些定制的优化比如说我们发现可能有些节点故障的时候这个任务的pod不会被删除导致任务就卡死了所以说我们发现假如有故障节点的pod的话我们会把它删掉这样它会重建来保证任务能继续跑下去同时我们发觉假如在集群规模比较大用户量大的很多的情况下可能存在很多tap drop的那么这些tap drop存在就会导致tap operator的性能变差了我提交一个新的tap drop的时候这个对应的pod要可能几分钟甚至十几分钟之后才能被创建所以这里我们对这种情况做了一个性能的优化同时我们发觉可能当集群规模大的时候我pod就会被GC了tap operator来不及处理pod就不建了这就会导致任务的状态变异常了它可能会从哪怕这个任务已经正常退出了它发觉pod不建了它就会重新创建pod那么针对这种情况我们也提交了一个e-shape并且提供了一个设计就是我们需要对pod和tap drop都加上finalizer来避免pod被集群给回收那么接下来介绍的是一个弹性伸缩的一个能力首先kbs它原生已经支持了一些弹性伸缩的能力它能够基于cpu和内存的利用率来做弹性伸缩但它的实力只能速溶到e那么在实际的业务场景中我们其实有更多的需求比如说我们希望利用更多的指标包括gpu的利用率业务的qps还有对定时来做伸缩容而且在某些场景下我们是希望实力是能够缩到0的比如说在我们的在线场景里的预发环境上我们可能希望说在能够把这些应用的实力缩到0否则假如每个应用都实力都1都占着一张gpu卡的话就会造成大量资源的浪费了那么针对这些需求我们1.0的方案会引入多个开源组件比如说定时的话我们用了com HPA而其他指标的话我们是通过pro metros adapter然后这些组件其实会比较多然后可为互信和可扑展信都相对比较差当要新实现一个新的特性的时候又必须引入一个新的组件那么所以在2.0的方案里面我们做了重构引入了kata框架kata是微软和阿里合作的一个项目它全程是kubernetes event driven autoscaler那么它的架构会很简单并且扩展性特别好右边就是它的一个架构图我们可以看到它通过mash adapter会把外部的一些事件源注册到k8s里面那么k8s原生的hpa就能基于这些注册的一些指标来做弹性伸缩而当这个wattload也就是它应用的实力需要从0破到1或者1缩到0的时候是由kata的controller来负责的那么同时kata本身已经内置了很多插件它能够支持对一些消息队列或者数据库事件的一个弹性基于这些事件来做弹性伸缩它也支持了定时伸缩的一个插件而且它也能很方便的来新实现一个插件重了实现新的特性那么在实际的落地场景中我们遇到一些挑战那么比如说我们整个弹性伸缩其实它是对指标的基于指标的所以它对指标的及时性和准确性的要求特别高那么原来我们使用的prompto4它是个单点的系统那么因此我们对整个监控做了改造引入了sand knows把它改造成一个高可用的监控系统同时我们对一些指标一场也做了一些兜底的异常处理而第二个就是说扩缩容的时候其实我们需要保证应用是稳定的那么这块我们跟中间界团队一起合作建设了一个流量无损的机制让这个业务应用发生扩缩容的时候它流量也能够不损失第三点就是说当我们晚上有很多业务它会把资源做缩容缩容之后这个资源怎么前置资源到底怎么使用呢我们会把它自动挪给在线离线的资源池来使用那么离线资源池用着之后呢当白天需要的时候我们会把离线这些节点上的一个任务被驱除掉还给在线来使用那么在扩缩的时候怎么来保证有资源呢其实这需要我们对资源容量来做精细化的管理这也是我们正在建设的一块工作因为比如说像kbis原生的一个resource quota其实只能只是一级的只能针对nose base那实际场景中其实我们需要有多级的限额的管理同时需要有分级的一个机制所以说这块需要有重新制定义的坏规的整个机制那么右边这是一个效果图我们可以看到我们的整个实力数和我们的那个kbis基本上是保持变化的趋势是保持一致的那么最后讲的是我们一个混合运能力的建设像我们混合运主要是希望能够通过快速低成本的利用供应的算力和高级特性来给业务来使用那么当我们建设这个特性的时候主要是为了满足以下两种场景的需求第一种就是我们一个算力的一个临时的需求比如说在节假日活动的时候我们业务需要临时很多算力还有刚才提到的像预训练模型它只是可能临时需要跑一个训练但却需要大量的资源那对于这种临时的大量的算力的需求我们可以通过混合运来补充我们算力的不足那第二个场景就是说当我们的机房的基础设施有些高级特性不具备但是公有云上却有这些基础能力比如说RDMA的网络或者高性能的存储那么当有业务有需求的时候我们就能通过混合运让业务能先用到这些高级的特性而在实际落地的中我们方案的选型主要有三个第一个就是我们会把云主机当作物理机一样加到我们的私有集群里面这个方案的优点就是它很简单而且不改变当前资源的申请流程缺点就是因为我们申请云主机需要向我们的基础平台申请需要有审批的流程所以说它并不是完全自动化的而且没有办法实现分钟级的扩容方案二的话就是我们通过在公网云上能够申请一套单独的集群来使用这个优点就是这个集群我们不用维护这个集群同时这个集群向公网云上集群一般有一些高级的特性比如说它能够做一个资源池的自动扩容但是它缺点就是它的成本会比较高并且是我们需要去兼容云上云下两套集群的差异同时做跨集群的一个调度而且它这个资源申请的流程也不是符合公司当前规范的那么第三个就是通过引入Virtual Kubernetes这个项目这个Virtual Kubernetes项目目前已经对接了很多各个公网云上的一个ECI的服务它能够把容器的实力直接当成一个节点加到集群来使用那么它优点就是说这个项目目前比较成熟而且通过它能够快速实现那个集群算力的扩容但同时它的实现成本也比较高因为这里我们又引进了一个新的一个项目同时这种资源的使用申请方式也是不符合公司流程的那么最终经过全云之后我们选择了方案1主要是因为方案1的实现成本低不改变当前资源申请流程可以快速落地那么这里我们得到的一个思考就是说我们落地一个方案的时候除了考虑技术层面的一个东西之外还要考虑流程当往往特别是这个流程假如涉及跨部门以及涉及整个公司的流程的话要改变这个流程其实需要的周期就会比较长那为了快速落地的话我们永远会选一些对流程改动不大的一个方案那么以下这个就是我们一个整体的架构图我们可以看到我们既有IDC机房又有RDU然后我们的控制平面就会放到我们的IDC机房里面它包含API Server、IDCD集群等那工作平面里面既包含了IDC的物理机也包含了RDU上的云储机那么我们IDC和RDU会通过专线拉通同时我们在IDC里面采用的是Calico BGP的网络而在RDU上会采用特位的网络那么通过这样子之后我们的POD不管是在集群内外或者是在机房和公共云上都能够互通那么最后就给大家讲讲我们未来的展望也是针对刚才提到三个点在算力应用器上面我们希望建设一个容量拖管的能力这样我们的业务方就不需要关注到底我的业务需要使用多少容量了第二点就是我们希望打造一个训练的SDK这个SDK里面集成了一些比较好的库能够提升性能比如说英伟达的APIS和大力那么在算力灵活调度上面我们之前使用的是Cooper Badge但是现在我们希望基于Schedule Network来做重扣同时支持TOP感知的一个调度这样子能够满足最佳TOP能够提升训练的性能而在算力高效方面我们希望通过数据的编排加速来提升算力的使用性同时我们希望建设一个弹性训练的模式那么今天的分享就到这里像我们平台在InfoQ有个专题叫做Vivo AI 计算平台的搭建实战有兴趣的同学可以上去看看我们分享的一些内容谢谢大家