大家好我们这边就先开始因为快点开始快点结束不耽误大家吃午饭我是来自于火山引擎的工程师我叫钱磊然后主要在火山引擎做容器服务平台给用户提供一些托管的Kubernetes对然后最近一年我主要在做集群弹性方向上的工作主要是利用Class to Auto Scaler去帮用户集群做自动扩缩容来提高节点的使用率节省成本今天很高兴能跟大家分享一下我们作为云上的Kubernetes平台在帮助客户实现资源弹性的过程中碰到的一些经历还有一些挑战我会先和大家介绍一下什么是Class to Auto Scaler以及它内部的一些流程和实现方式帮助大家更好的理解它的工作机制然后我会简单介绍一下我们客户的使用场景然后会把重心放在碰到的问题和解决方案中最后会给大家一些建议避免踩到类似的坑OK我们先了解一下什么是Class to Auto Scaler这个单词比较长我后面就全部简称CA了然后CA这个项目其实关键点我已经在这里面标红了这一段话摘字CA项目的Rhythmia然后里面有几个高量的部分第一个是说它的一个能力是自动调整集群的大小简单讲就是做扩缩容第二个是说它扩容是因为集群中的资源不足才会去做扩缩然后第三个是它的一个缩容判断缩容是第一个要节点的使用率去低于预值设置的使用率超过了低于用户设置的那个值第二个是说这个节点上它的已经存在的这些Port能够调度到其他节点上面去在这个条件下它才会去做缩容这张图展示了在用户视角下CA扩容的一个情况当集群中出现Pending Port的时候没有节点能让这些Port调度上去因为资源已经不够了所以CA就会去触发扩容往集群里面加新的节点让Port调上去然后在节点的使用率比较低比如图中低于50%的时候CA就会把这个节点去给它删掉然后节点上的Port就会被重新被驱逐然后重新调度到其他节点上面去这样一来集群中总的工作负载情况不会有什么大的变化但是因为节点数减少了剩余的节点数减少以后整体的集群的使用率就提高了对用户来说这相当于是一个降本增效的一个效果这里面给大家讲一下大致的一个工作流程集中细节会比较多然后我这边大家解化了就这么三个流程第一个CA会是先从集群中获取相关的数据比如说节点还有集群的状态需要调度的这些panning port以及做一些必要的轻易工作比如说去轻易创建失败的节点过滤还没有ready的GPU节点等等然后第二部分是做扩容接着再做缩容最后结束以后再等待一段时间以后再从头开始所以CA是一个定期重复执行的一个过程然后在缩容阶段CA会先找到集群中无法调度的panning port然后试着把这些panning port和集群里的节点池做一个匹配看看每个节点池上都能满足哪些panning port的调度要求有的节点池它可能满足不了panning port的调度要求那节点池即使做了缩容其实也没有什么用不能解决panning port的调度问题然后这些节点池就被排除掉了那有的节点池它能解决一部分的panning port那就会把这些节点池保留下来那对于这些保留下来的节点池CA又会去计算说我这个节点池需要缩容多少个节点才能满足这些panning port的资源用量然后接着CA再从这些节点池里面按照用户设置的一个缩容策略比如说随机选择或者按照优先级或者最少浪费这样的一个缩容策略去选择一个最合适的节点池然后去做缩容那缩容它实际上本上做的事就是通过接口去告诉厂商我这个节点池需要新增多少个节点那后续具体的这个节点的创建加入集群这些动作都是由厂商来执行的然后在缩容阶段CA会先找到使用率低于预值的这个节点看看这些节点上面是不是还有一些port的那如果没有port了就认为这是一个空的节点那空节点会被优先的批量的删除掉然后删除完空节点以后那剩下的都是上面跑的一些业务port的节点CA会再判断这些非空的有业务port的节点上面这些业务port的是不是可以调度到其他节点上去那如果CA模拟出来说可以调度到其他节点那CA也会把这个非空的节点的信息告诉厂商让厂商去把这个非空节点给大家删除掉这大概就是一个CA的整一个过程当然中间还有很多细节省略了但是给大家梳理完应该有这么几个关键点大家可以理解到第一个就是CA中的逻辑它是定期在运行的第二个是在整个扩容的流程中在整个流程中它其实有扩容和缩容两个阶段这两个阶段会比较独立扩容需要计算新增的节点数量按照扩容策略去选择节点池然后缩容就只看节点的使用率和上面的POD是否可以被重新调度然后这次分享的客户他们有自己的一个任务分发平台他们主要是做PG算的场景的那他们不同的这个计算任务会通过他们自己的这个任务平台下发到Kubernetes集群里面每PG算任务对应着一堆的POD那不同的任务所需要的资源不太一样那CPU用量有大有小有的会使用GPU不同的任务他们对应的这个POD的数量也不太一样有的小的可能几百个多的可能几千个剩上万个对然后分职的时候整个集群甚至到过两万POD这么一个级别一般业务高峰会在晚上从凌晨开始跑然后一直跑到第二天早上那这些POD的镜像也非常的大这个在弹性场景下也是一个不可忽略的业务特点然后在这样的业务场景下面为了节省成本客户很自然的就会去使用CA这个组件期望在他们的计算任务下发以后节点只能够自动去扩容加入一些新的节点资源让POD掉上去那在计算任务跑完以后节点空闲下来CA又会去把这个节点删掉避免资源浪费为了提高装箱率减少资源的碎片那客户也会去设置POD的Resource Request尽量和节点的节点池的规格一致让每个POD都尽量能独占一个节点然后客户在上量的过程中其实并不是一番纷顺的我们碰到的第一个问题就是用户在大规模扩容的过程中会出现大量的扩容失败那CA触发节点池扩容以后它出现的一个现象就是一部分节点能够创建成功调度了部分的计算POD上去那另外一部分节点它就会创建失败然后这些创建失败的节点在随后的CA的运行过程中又会被删掉然后因为还有这些部分的计算POD处于偏见状态又去触发CA的扩容然后又有失败就这么周而复始那用户在提侃上使用体验就非常的差那第一个是说因为看到了非常多的这个失败的扩容记录对于我们厂商这个信任度是有下降的那第二个是增加了不必要的这个成本因为这些创建失败的节点他们虽然没有加入集群被客户使用计算能力没有用起来但是这个节点对应了背后的云服务器它是实在在被创建出来的那从云服务器的创建到删除这一段时间里面那云服务器还是在继续收费的所以用户花了钱资源又没有用上对用户来说其实增加了一些无畏的成本然后我们调查发现节点扩容失败主要是因为云服务器在初始化Kubernetes组建加入集群的过程中是有一些超时他们很长时间都不能加入到集群里面超过了我们预设的一个四分钟的超时限制所以我们会认为这是一个异常的节点异常的节点随后又被CA给大家清理删除掉那我们当时也就很好奇为什么云服务器不能加入到集群里面来那我们后面发现是因为云盘它的写入速度特别慢对 然后这个云盘速度特别慢主要是因为我们给云盘服务带去了比较大的一个压力压力主要来自于两方面第一个方面来自于云服务器自身在启动初始化Kubernetes组建的时候比如安装系统的软件包或者从OSS上面去拉取Kubernetes的安装包做解压这样的动作它其实是有一定的磁盘写入这样的操作的一个节点可能还好当几百个节点你同时拉起来都处于这个阶段的时候其实会对EBS服务就是云盘服务弹性快存储服务那边会有一些压力但这个压力不是特别的大然后第二个主要的压力来源是说云服务器上就加入节点加入集群以后迫得调度上去了开始拉镜像在正常的规模下其实这不会是一个特别大的问题但是当有几百个节点然后上面的镜像都十几个句在同时拉的时候不管是对网络带宽还是对磁盘写入包括对后面的云盘的服务都会造成一个比较大的压力云盘服务压力大了以后就会影响到新创建的这部分ECS它们的一个写入速度所以这里面是有一些资源增强的情况存在的然后为了解决这个问题我们的想法是非常直观的是对同时扩容的节点数做一个限制虽然社区中当前社区CA并没有对同时扩容的节点有什么限制但是在规模比较大的情况下我们认为除了我们碰到云盘写入压力这个瓶颈以外可能也会存在其他的瓶颈所以对扩容的节点数做一个限制是一个比较合理的做法我们根据云盘的吞吐能力计算了一个可以被接受的同时的扩容节点数比如说限制是100这样的话用户他看到的就是100一批100一批地去扩容这些节点的成功率都非常的高基本上都能扩容成功虽然扩容的批次增加了但是它的扩容成功率也对于提高整体的云盘的写入流量也更加的平滑整体的扩容速度也比之前提升了很多然后我们碰到的第二个问题主要是极致的性能问题这里面又可以分两个部分来讲第一个是扩容的性能我们客户对扩容的端到端的速度要求可能会比较高比如要求要在五分钟之内拉起500个节点然后这些POD都能跑起来这是一个非常有挑战的事情因为在客户视角下他们计算任务的启动延迟大概有五个阶段第一个是他们的下发任务急准中出现因资源不足而导致的pending POD第二个阶段是CA感知到这些pending POD以后去触发节点值的扩容这个阶段一般是秒急的如果是使用了GPU的POD它可能会延迟到30秒这里CA它不立马扩容要等几秒的原因是因为如果出现了最新的pending POD创建的时间离现在比较近可能后面还会有新的pending POD被创建出来比如deployment了副本数我们从0改到1000可能就要几秒到几十秒的过程去做POD创建的动作所以CA它宁愿多等一会等所有的POD都创建完了才去做扩容第三个阶段是CA它感知它做了扩容的动作告诉厂商厂商这边要去创建云服务器把云服务器注册到集群里面来这个阶段一般是分钟级别的在我们这儿是大概在两分钟左右不同的厂商时间可能会不太一样第四个阶段就是节点加入到集群以后pending POD被调到节点上如果pending POD的数量还有集群规模不是非常的大POD的调度条件也没有那么复杂所以相对整个过程来说这个阶段是可以忽略不计的最后就是节点上的POD被调上去了开始拉镜像开始启动这里面这个耗时其实不太稳定的比如同时扩容的节点数量比较多容器精效比较大很可能就会受到厂商的限制比如说打满厂商从镜像仓库拉去的带宽线素或者刚刚提到了EBS的存储的线素问题然后在这张图里其实会有多个节点在拉镜像对一般除了用户自己的计算POD在拉镜像以外其实还会有一些系统组件的DEMON SET比如说网络组件还有一些日制组件这些其实也会在拉镜像然后这些镜像的拉取也会大量的同时的从镜像仓库拉很容易打到网络瓶颈这个刚刚提过也会给云盘服务带来压力如果500个节点同时扩容每个节点上都在增强网络带宽或者次盘写入带宽是没有办法达到刚刚说的要在五分钟内扩出500个节点并且都让POD的Running这么一个比较极致的性能要求的然后在这种性能要求下面我们采用了自定义系统镜像的方案这个意思就是说这个里面的自定义系统镜像是指云服务器的系统镜像我们先在云服务器里面把容器镜像多克一面纸给它拉下来然后把云服务器导出为一个自定义系统镜像把业务的容器镜像固化到系统里面去这样在后续节点式扩容的时候我们用这个自定义镜像去创建云服务器云服务器加入到节点以后这个容器镜像它已经在这个节点上了就不需要再重复去做拉取破的就可以基本上做到秒极启动把我们刚刚讲的最后一个阶段的耗时给它基本上就砍掉了这个方案其实也有一些弊端比如说我们在整一个容器镜像固化到系统镜像以后因为容器镜像它经常在发生的变化所以这个自定义系统镜像它也经常需要重新制作这个会非常的麻烦如果镜像更新比较频繁这个自定镜像的制作也要跟着一起很频繁的变所以我们也可以把这个容器镜像做一下拆分把这个数据量比较大的又不怎么更新的这些静态数据打包到基础的这个Docker image里面做一个Base镜像然后再把这个Base镜像放到自定义的这个操作系统镜像里面去这样也能减化说节点在启动以后拉取的这个镜像的数据量的大小然后使用了这个方案之前我们扩容500个节点在单批次运行单批次扩容70个节点的情况下面每个节点一个破的每个破的10G这样的镜像大小我们整个从C扩容到破的最终Running起来大概是需要22分钟的用了这个镜像因为不需要拉取容器的用了这个方案以后因为不需要拉取容器镜像了所以刚刚说的云盘的服务压力就没有了我们就直接把扩容的并发限制给它放开从0直接到500去扩从破的下发到最终Running这个过程可以在4分30秒内完成并且云盘它整体的写入流量从分值的比较高的14Gb下到了6Gb减少了非常多的数据写入这个方案其实在做快速扩容或者说对扩容的端道端耗时非常敏感的业务在这种场景下是一个可行的方案然后刚刚提到的是一个极致的一个扩容场景那我们还有一个说是在缩容的情况因为我们的客户它的计算人物是不太一样的所以会触发不同的节点池的扩容比如说用户它可能会先来一波GPU的计算人物触发了节点池A的扩容那节点池A它是GPU节点在计算人物A快结束的时候可能又会下发新的CPU的计算人物触发CPU节点池的扩容那按照客户的预期节点池A的这些GPU节点在结束以后因为上面没有GPU的这些扩的了所以节点的使用率很低可以在一段时间有很快就被缩容掉但实际上节点池A的缩容会被推迟较长的一段时间那在这段时间里面这些资源其实是空闲浪费的那当时我们也很好奇说为什么这个节点池A会被延迟到这么后面然后后来调查发现说CPU内部的这个缩容流程里面其实是有一个冷却时间表示扩容多久以后是不能对这个节点做缩容的这个值是由用户设定的那这个值是一个集群级别的冷却时间每次节点池的不管哪个节点是扩容这个计时器都会被重置那么在大规模多节点池的扩容的情况下为了保证刚刚提到那个成功率我们对节点池做了分批扩容比如一千个节点我们需要分十批去扩每次扩的时候都会对这个冷却时间做一个重置所以节点池A在节点池B的十字扩容中都无法被缩容掉这个是一个比较严重的浪费问题那当前这个问题其实是已经有了一些解决方案但弹码还是处于一个草稿阶段这个解决思路就是说把这个冷却时间这个计时器改成了一个节点池级别的每个节点池它就只针对自己的扩容过程做倒计时不受其他节点池的干扰我们在身上环境上对这个方案做了一些验证确实很好解决很好地解决了我们的问题那在计算任务结束后这个节点池A很快就会被缩容那缩容时间的缩短其实就是客户成本的降低在这个方案使用之前那集群整体的装箱率大概是在80%其实最低的时候也到过60%然后用了这个方案以后装箱类就直接提升到了97%一个比较高的位置然后刚刚讲了稳定性还有一些性能问题我们再来看一下我们碰到的规模带来的问题那用户是用自己的任务分发平台去下发POD到Kubernetes集群的那有时候并不能很好的控制任务的下发速度分值的时候集群中会有2万多个POD那其中PendingPOD的数量甚至达到了18000那在这么大规模下我们Ca直接就降死了那上面这个图中是集群中POD的数量的一个情况那下面是我们的Ca的日制分布情况我们可以看到在POD的数量凸增的这段时间里面那Ca基本上是没有什么日制输出的集群中的这些节点也没有做缩容Ca的客户的计算人物被大量的堆积组设然后我们调查发现Ca的卡住主要是在调度预测这个阶段就是在这个阶段Ca它会计算每个节点值需要扩大多少个节点才能满足这些PODPendingPOD的一个资源用量的情况那为了复线这个问题我们做了一些压侧希望能找到这个耗时的主要原因方便针对用户的场景做一些优化那一开始我们想到的是就是PendingPOD的这个数量为此我们设施了两个不同规格的节点池然后往集群中去下发大量的PendingPOD那这些PendingPOD通过资源用量调度到其中的一个节点池上我们发现随着集群中PendingPOD的数量的增长这个单个节点池的整体的计算耗时是不断上升的在2.2万POD的时候单个节点池的计算耗时会到400秒左右大概也就是在7分钟而从POD的视角来看单个POD的平均耗时也是线性增长虽然PendingPOD的数量规模达到了但是实际上CA将死的时间就是当时我们客户的情况远比我们这个测出来的400秒要多所以我们为了接近客户的真实的使用方式将POD的这个调度逻辑做了修改从之前的资源用量去选择节点池改为了使用节点的弹性节点的清和性那我们发现在不使用节点清和性的情况下整体的耗时跟第一次压测是一样的然后如果的使用节点清和性PendingPOD的数量在1.8万的时候那整一个调度预测的耗时就达到了700秒右侧这张图中蓝色的这条曲线也说明单个POD的平均计算时间是比之前不使用节点清和性的场景增长得更加的快那整条线上升的速度更快协律会更高然后除此以外我们继续控制变量在之前的单个节点上是只跑一个POD的那改为了单个节点上去跑8个POD的这样跑完以后预期添加到集群中的节点数量是之前的1%同时整一个计算耗时也比相比之前的曲线也更加平接近躺平了那从上面三次压测过程中我们可以得出一些结论就是PendingPOD的数量越多那需要计算的耗时越久并且每个PendingPOD的耗时随着总数的增加而增加的第二个是使用了节点清和性的PendingPOD在做调度预测的时候耗时也会更久然后第三个是预估节点数量越多预测调度预测的耗时也会越久第四个是可被调度的节点数数量越多调度预测越久那这是我们压测的结论那从技术理解上是怎么去理解这个现象的然后CA在估算节点时需要扩容多少个节点的时候内部它其实是有一个快照的这个快照包含了集群中真实的POD还有NO的情况一开始这个快照里面只包含了集群中的节点和节点上的POD那如果集群中有多个节点池CA会先对每一个节点池做一下计算看一下哪些PendingPOD可以调度到节点池上面就这张图里对因为如果节点池它不能满足PendingPOD的调度要求即使扩容了其实也没有用然后在这张图里面集群中一共有8个PendingPOD那节点池A它能满足所有的PendingPOD的调度要求而节点池B只能满足6个这个过程的PendingPOD跟节点池匹配的这个过程它的复杂度大概是个O的N的平方跟POD的数量还有节点池的数量有关系当然也跟POD自身的调度条件有关系调度条件越复杂做这个耗水会更久一点那做完这一步之后CAA会再根据节点池和节点池上的这些PendingPOD去计算需要扩多少个节点比如这里节点池A再能满足8个PendingPOD的调度条件下CAA会先把这些PendingPOD和快招里的节点做一轮调度模拟跑一下Schedule for a more-curly的Pre-Fuelter和Fuelter这两个阶段看看能不能正常通过那如果能通过其实就说明这些PendingPOD是可以调度到快招的节点上的那如果不能通过CAA就会根据节点池A的规格信息构建出一个虚拟的假的Node放到快招里面去然后再做刚刚的调度模拟此时这些PendingPOD是可以顺利地调度到这些新的FakingNode上面的那CAA就一直重复这个过程直到它这里面所有的PendingPOD都能加入到快招那此时快招里面新增了多少个虚拟节点其实就代表了节点池A在后面需要多扩容几个节点然后这里面这个节点池B也是类似的只不过在我们的例子里面节点池B的规模会比节点池A小所以我们通过刚刚的分析整个过程的复杂度是接近于O的N的三次的跟PendingPOD的数量还有快招中的节点的数量还有节点池的数量是比较相关的那这跟我们压侧的结论也是一样的就是PendingPOD的数量越多节点池越多预估的节点的数量越多调度条件越复杂你整体的扩容耗时就越久那这个问题其实在社区在去年是已经解决了然后解决的方式主要有两个第一个是限制节点数量的上限就是减少快招中节点的数量这个其实跟我们刚刚提到的观点是比较类似的就是如果我们不对扩容的节点数量做限制这个其实是对稳定性要求比较高不是不太稳妥的第二个是对单个节点池整体的计算耗时做一个限制比如这里是不能超过10秒那如果超过了10秒我们就截断这个过程后续的计算我们就不进行了那当然这个特性是在比较高的版本才支持那如果大家使用的C还是在1.25版本以下的那可能就没有办法解这个问题对然后基于刚刚的那几些问题还有解决方案我给了大家一些建议那如果业务对扩容的延迟比较敏感希望能更快地让Port去启动可以考虑将一些静态的较大的容器镜像打包进云服务器的系统镜像里去加速扩容然后第二个是稍微控制下集群中的Panning Port的数量来确保集群和扩容的稳定性因为Panning Port的数量过多单一资源的数量过多会对集群本身带来比较大的压力然后对扩容的稳定性也并不是非常的好然后第三个是说对于不需要弹性的节点池就直接关闭掉避免C在这些节点池上的一些算力的浪费这就是我带给大家的建议好这就是我全部的分享内容好那边同学有问题你好我这边有几个问题第一个的话就是C它是弹服务器满弹斗的然后其实火山还有其他的产品就比如说VCI直接弹Port就这两个产品我怎么样去做选择呢这其实是一个方案选择的上的一个讨论就是你既可以用ECS去增加你的弹性也可以用弹性容器实力去做你的弹性但弹性实力它其实是在价格上包括在整体的资源量上可能是并没有ECS充分的在我们火山引擎是这么一个情况所以如果你用火山引擎的弹性容器实力去弹比如说GPU可能是规格是库存是不太够的你想弹可能弹不上去但如果你用ECS去弹就完全没有这个问题现在这是在我们这弹性是两条两个方向会有对应的解决方案给出来行OK刚才讲到库存我想问一下就是CAA这个方案GPU的库存是大概是什么情况会不会出现那种资源不足这个肯定会有的因为在现在浪潮下GPU资源是比较昂贵比较稀缺的CAA它确实会弹ECS的云浮气弹出来以后是弹不出来因为库存不足直接就见不出来在我们这边的场景你直接就失败然后这个节点是会被禁用一段时间比如说五分钟以后它再尝试去弹这里面就是多个客户之间对GPU ECS的一个真强这里面可能需要一些销售的协调来做这个底层的库存的去分配但这个已经不在CAA弹性的范畴里面了OK然后的话就是说就这块价格就是说相对整足CAA它弹出来的资源价格的大概也会差多少我理解你这边是说我同样的计算能力比如说一盒在ECS上和在VCR上两者的一个对吧对然后这里面不单单是价格问题其实还有一个主要装箱率的考虑因为你用ECS去弹你肯定会有些资源的碎片或者是浪费你并不能把整个ECS的资源给它用起来那VCR上也会有那这个装箱率的概念我们现在产品上还在设计后面可能会透露出来但是具体哪个高要看具体的业务场景还有你颇德的规格以及分发的一个情况好谢谢我这样没问题好没有问题那我这边就先这样好谢谢大家