简介
数据中心日均 CPU 利用率 45% 的运行之道–阿里巴巴规模化混部技术演进
整体脉络:
- 粗放的资源评估,hpa。PS:用户配置Pod.limit超过峰值 ==> 即便按峰值配了恰当的 limit,仍然有很多时候用不到,前者用vpa 解决(这样一台机器可以塞更多在线应用),后者用混部解决。
- 节点分时复用
- 节点超卖(卖的资源是“已经卖出去的”的资源),然后又有一系列辅助动作保证Pod QoS。
- 识别出节点的弹性资源,上报,调度器用上 弹性资源。
- 节点不要成为热点(节点压力过大本身就会影响业务);在线任务调度延迟不受大的影响:不受离线影响(优化os 抢占策略),不要被其它在线影响。 如何识别干扰源,如何压制干扰源?离线的混部任务一般都是可重试的,因此驱逐可以作为一种兜底手段。也别让离线任务过的太惨(离线任务一直没资源跑就调离这个node)。
- 统一资源抽象,调度器支持全场景的资源类型 ==> 本质是对节点上不同pod的分类管理(时延敏感型、xx),跳出request和limit的概念限定之外,干脆提出一个资源池的概念。节点划分资源池,高优池支持绑核、cpu负载控制在50%以下,池间强隔离,不同池运行不同类型的任务,池资源大小随负载动态调整。PS: 如果一个系统可以做到workload 需要资源就立马给,不需要就立马剥夺,我们就不需要request和limit 了, 但是现实做不到,资源分配和回收有延迟,所以就只能分级处理,QoS越高的冗余越大。
PS: 理想状态:给到 pod 准确、实时的limit(更好的监控),且保证pod 之间不会互相影响(更好的隔离)。20年左右一位阿里云的大佬就提到,调度的理想境界就是,部署一个服务,你只要配好QoS(比如接口延迟不低于xx),剩下的全交给调度器解决。
统一调度的渊源
毕玄:在资源利用这个方向,除了 Serverless,有别的方案吗?就是混部,现在混部到 CPU 层面了,大数据和在线其实都是 CPU 层面的,所以在离线的统一调度是这个阶段我们可以做的,能把利用率提高的一个很落地的方案。之后最关键的是看 GPU、FPGA 这些能不能做好。
调度这个事,其实阿里最早很多高管都有这个梦想,博士成立阿里云的时候提出云的实际表现是统一存储和统一调度,他认为云的核心是可以把所有的机器像一台机器用,这就是统一调度。这个思想最早提出的应该是 Google。Google 很早有篇文章叫《The Datacenter as a Computer》,一个数据中心像一台机器,这效率肯定是最高的,事实上现在也还做不到。Borg 的思路是把 CPU 尽可能当一台机器用,但内存还不行,比如 A 机器现在空了 1G,B 机器有 2G,但你不能说用户现在有 3G 可以用,这个真做不到,因为内存有时延的问题,在单机时延非常低,一旦跨了网络,但网络已经是光速了,这是物理决定的,就很难。据说 Google 想探索这个方向,因为 Google 近两年把这篇文章改了一下,叫《The Datacenters as a computer》,跨地数据中心像一台机器,这简直颠覆了我们的想象。
毕玄:11年传闻 Google 认为自己的核心竞争力是什么?最早他就做搜索,他认为自己最重要的竞争力是,一我排序结果的准确度比多数公司好;二做同样的效果我付出的成本是你们的 1/10。这确实是,成本如果差这么远,商业上就没办法做下去了,这里面,Google 觉得调度系统 Borg 承担了很大角色,类似它的 Page Rank 算法,是整体竞争力的一部分。所以外部很少有 Borg 的信息,保密性做得非常好。后来我们知道一点是,2015 年 Google 发表 Borg 论文,那其实几年前就写好了,只是内部一直摁着不让发,觉得可能对业务的核心竞争力有影响。
多数公司的机器会分成很多个池子,最典型的是一个机器池子用来跑在线业务,另外一个用来做大数据的业务。大数据这个软件的核心设计思想就是尽量并行化,把一台机器的资源全部吃光,所以大数据特别吃资源,能把池子用得特别满。而在线业务,不是不想吃资源,它必须考虑的是什么?是稳定性,如果出问题了,我最好要有足够的冗余,加上一天还有很多不确定的高峰,所以我机器的余量一定是为高峰准备的,利用率就没有办法很高。因为对在线来讲,稳定性是最重要的,如果你不断伸缩,万一出问题了,可能得不偿失。除非你可以做到秒级以下的伸缩,那可以。
很多公司到了一定规模,在线机器增长其实还好,因为在线跟业务基本成正比,就是 QPS,比如说现在 100,明年你希望做到 200,那我就加机器;同时,因为每年的机器比上一年更好,所以从预算角度来讲,公司觉得是合理的,业务增长 20%,你机器增长比如 15%,那当然可以,至少没多出钱,能接受。但大数据就有问题了。通常大数据机器只要开始用了,每年的增速会越来越快,因为存储量一直在那,而大家想采集的数据细节会越来越多,这样才能更精准地画出特征,所以大数据的机器就会多;另外公司的经营一定会越来越难,以前增长比较容易获得,可以粗放式,但后面你肯定要精细化,但精细化对大数据的要求又越来越高,所以机器会越来越多。
这个时候,技术层面大家都很容易想到一个方案:既然在线这边这么混,大数据这么满,能不能合在一起,让大数据用在线?这就是当时 Borg 给大家的核心思路。Google 当年有一个高管跳槽到百度,他第一次看预算的时候发现还分大数据机器和在线机器就很疑惑,为什么还要分机器类型?他说我们从来不分。
从技术上讲,大数据跑到在线确实有很多问题。首先机器以前是分开的,最典型的就是通常在线机器是 1 块盘,大数据机器是 12 块盘,因为它跑的时候需要算非常大的数据,但在线以前数据量非常小,大数据上去跑的时候,存储不够,大家觉得没法搞。另外物理的基础设施条件也有要求。第一在线机器和大数据机器要在同一个城市。比如从 A 到 B 的网络我们叫城际带宽,如果不在同一个地方,意味着要走这个,但城际的网络带宽非常贵,你如果跑大数据就更不得了,要求非常高,这条路是不可能的,所以首先要的是大数据和在线搬到同一个机房。而且当时物理基础设施除了机房,还有网络的问题,以前我们的网络是千兆,对大数据来讲不够用的,它需要万兆以及更高。千兆就要搞各种限制,所以也很痛苦。除了基础设施,还有干扰的问题,因为大数据任务会吃光所有的资源,如果你在线也同时跑,会不会影响到在线业务的稳定性?如果被干扰了,导致你的响应时间下去了,就完蛋了,在线业务会不惜一切代价保稳定性,你想如果业务都挂了,省钱有什么意义,对不对?
以前机房分开也是出于成本考虑吧?中国西部城市比如说内蒙古,电非常充足,电费非常便宜,加上温度通常比较低,建机房就很好,很省钱。但问题是互联网的出口通常又在一线城市比如北京、上海这几个点,做在线业务是需要互联网出口的,我必须在这些城市或者附近。这就奠定了以前大数据、在线都在不同机房。
- 当时我们面临的第一个挑战就是这个,怎么说服高层阿里建一个大机房,让在线和离线搬到同一个地方去?但这对任何一家公司都是一个非常大的决定。建一个大机房是几十亿的投入,要找到一个地方电费便宜,也要在互联网出口附近,也要探讨清楚 ROI 到底是什么?因为开始肯定要增加投入,以前没有这些,现在砸好多钱,你到底能不能省回来?这就是又是那个论证问题。所以机房问题,团队也没办法,只能等公司决策,后来主要是因为云起来了,对我们来讲小机房效率不是很好。所以后来统一调度能做,也很难讲,可能是时机比较巧。
- 我们觉得 Google 能干成,至少这条路走下去应该没问题,不会走不通,只是要解决的问题肯定非常多而已。
- 但在阿里,必须说我们做这件事情难度比百度更大。百度是高层 Push 大家这样做,而且大数据团队有很强的动力,在阿里,我们有很强的动力,但我们是在线业务团队,不是大数据团队,这个事想做成,很多工作是要大数据团队做的,当年他们还有别的很多事情要干,觉得这不是我的重点。所以各种原因,尽管我们从 14 年开始做,但进展一直比较慢。
- 物理上的限制,是等 15 年建大机房了才有可能性了,然后网络要升级到万兆,上面升级到 25G,40G,100G,到了 16、17 年那个时候网络都具备了,也没有问题。剩下要解的核心问题就是大数据对在线的干扰,还有两边机器的磁盘不一样。网络升级上去之后我们可以走计算 - 存储分离。
- 但计算 - 存储分离内部当年也争论非常大。原因是大数据软件在一开始的核心设计思想,除了高度并行、充分使用资源,还有一个是“存储和计算一体化”,就是调度的时候会尽量让任务和存储在同一台机器上,这样算起来最快。但你现在告诉大数据团队不要放在一台机器上,这其实挑战了大数据的很多思想。所以内部争论非常巨大,但我们反正可以逼着你必须走这条路,比如说卡预算各种。
- 所以基本等 16 年阿里开始大力提统一调度,也有了正规军,很多问题才慢慢被解决,一是物理条件具备了,其实是 18 年才具备的,但大家在 16 年开始探讨,觉得这个方向可行,基础设施就去配套准备,所以大机房、网络都在那两年完成了。
- 到 16 年那个时候,对在离线混部方案的价值,大家的认知也统一了?我觉得很大的原因是预算上大数据对成本的压力已经非常大了,必须要控制。但控制的思路我们探讨了很久,觉得最好最完美的仍然是 Google。你想,在线有一大堆机器在手上,如果能把大数据跑上去,相当于不用花钱的,大家拍脑袋想都觉得能省好多钱,是个好方向。
- 现在就剩下干扰问题。就是阿里的操作系统团队。系统软件部在我一开始组建的时候,操作系统团队可能只有 10 个人左右,人很少,然后到 2018 年的时候大概有 100 人,主要就是为了解决干扰问题。Google 尽管在论文里提及一两句,但不会讲更多细节,他论文的风格一般是这样,只是告诉你我很牛,但要怎么做到这么牛不会讲。你只能自己实践。我们就必须靠大量人力去堆,在这个过程中肯定会出问题,但只要出了问题以后我有专业的人,可以把问题迅速解决掉就能做。所以我们其实是这样摔出来的,也没有什么。这因为一方面公司信任,另外一方面是我们的在线业务有回滚、容灾各种策略,也有异地多活可以切流量,相对来讲是在比较安全的情况下做尝试,所以我们也不太在乎,出问题了就把流量切走。
- 从 2016 年到 2018 年,我们大概跑到了 1 万台机器,相当于在线有 1 万台可以给离线用,那一年离线少采购了 5000 多台机器,1 台假设 10 万,所以一年省了 5 个亿。关键是不光这一年,下一年我只要继续扩大在线规模,就能继续省钱,到后面每年省的钱会越来越多,因为技术已经是成熟可以被复用的了。方案上、技术层面上肯定不会有太大问题,剩下全是工程,工程是很缓慢的,你就算技术走通了,工程要完全落地也要个周期。这可以用一个指标直接体现,公司所有服务器全天的平均利用率,像 Google 就只看这个指标。大部分公司应该都小于 10%,阿里 16 年开始做的时候是 8%,Google 发表论文的时候利用率大概是 50%,这意味着 Google 只用 1/5 的机器就可以做同样的业务,离它讲的核心竞争力非常接近。
- 中国公司更难是因为我们不是全球化的,大数据是很高,但晚上没有流量在线就是零,所以你平均一下就完蛋了,利用率就很低,但国外很多公司会好一点。以前我们问 Facebook 这个问题,因为 Facebook 没有学 Google 走统一调度。我们问为什么?Facebook 说因为我的在线业务全天流量都还挺高的,因为它是一个国际化的网站,全时区覆盖。那我们没有,中国公司这一点确实是个问题。
- 所以从那之后,利用率就成团队重点关注的指标了?阿里每年就在不断地推进利用率指标,我们甚至讲到连财务都理解了,财务挑战研发线的服务器成本,他不关注其他,只看利用率要拉上去。
- Google 这两年据说已经推到了 60%,我们以前认为 50% 是天花板,不可能再多。以前财务也问,你们说利用率可以低,但得告诉我多少是合理的。我们总不能说小于 10% 是合理的,这解释不过去,从技术上也得编个理由,但 50% 我们说是可以解读的。一是因为这是全天的平均,如果说 50%,意味着高峰肯定会比较高,平均一下已经很少了。第二是多数 CPU 的设计原理都是超线程,你看到两个核,物理上只有一个核,只是说软件层面具备跑出类似两个核的能力,但其实是不可能跑得出来的。所以我们说就打个折,当然这有点忽悠,但财务线非 IT 的人可以理解,觉得比较有道理(笑)。但是现在 Google 推到 60%,这套解释又说不过去了(笑)。
问题
一文看懂业界在离线混部技术而造成资源利用率不高的原因主要有如下几个:
- 粗放的资源评估/业务申请的资源配额超过实际使用量+服务的资源使用量存在波峰波谷:研发更关注如何快速稳定的迭代产品需求,所以在服务部署时,一般按照最大流量来估计服务所需资源。但在线服务大都具有明显的潮汐特征,导致大部分时间段资源利用率都很低(10% 以下)从而造成浪费。
- 集群资源整合度不高:服务器的资源占用常常呈现非均衡状态,例如在线服务尤其是调用主链路上的扇出节点业务,高峰期往往呈现出 CPU 和带宽吃紧,但内存绰绰有余的情况。这导致虽然内存有冗余,但依然无法聚合等比例的其它闲置资源去形成有意义的计算实体。
- 业务部署隔离:因为东西部机房成本差异较大和以及容量规划等问题,很多企业会将在线机房、离线机房完全隔离开,这样不同 AZ 甚至不同地域间的在离线作业完全无法融合,资源池也无法互通流转。
混部要解决的问题,分为单机层面和集群层面
- Noisy Neighbor Problem,
- 资源层面:CPU、Memory L1/L2/L3、network、blkio。如何合理使用 CPU 管理策略,提升容器性能?
- 硬件拓扑层面:NUMA、cpu cache、memory bandwidth、SMT
- 在线服务类/Latency Sensitive
- 容器混合部署时相互干扰
- 资源竞争引发应用响应时间出现抖动毛刺的现象
- 批处理任务
- 分级可靠地资源超卖,满足差异化的QoS需求
- 及时识别干扰源,避免影响LS应用
- 资源抽象:Kubernetes 原生 QoS 分级无法满足大规模生产环境的要求。QoS 等级在多个维度反映了服务对资源质量的要求,比如在资源隔离上的一些配置,以及在资源竞争比较激烈时,驱逐时的顺序等。各大厂实现都自定义了自己的QoS。 PS:卖的资源是“已经卖出去的”的资源。
Kubernetes解决Noisy Neighbors场景的探索 RDT机制 按照应用程序和线程的服务分类来限制和分配其使用的L1/L2/L3缓存。 Koordinator 最佳实践系列:精细化 CPU 编排
技术要求
多种工作负载的混部成为常态
Koordinator 0.6:企业级容器调度系统解决方案,引入 CPU 精细编排、资源预留与全新的重调度框架当企业的应用越来越多,为每一种类型的应用单独规划集群,在运维成本和资源成本上将不再可行。企业管理不同业务类型的方式逐步从切分集群到共享集群,从切分节点池到共享节点池这样的方式演进。
Koordinator,名字取自 coordinator,K for Kubernetes,发音相同。语意上契合项目要解决的问题,即协调编排 kubernetes 集群中不同类型的工作负载,使得他们以最优的布局、最佳的姿态在一个集群、一个节点上运行。
B站云原生混部技术实践 把离线pod往在线节点调,有一些有趣的实现:把多余的cpu mem看做扩展资源,看request 这个节点已经满了,但是看扩展cpu/mem 还有很多,由于没有申请原生资源类型,那么k8s会自动将这类pod归类为best effort类型,并使用agent 根据负载调整 best effort的额度。 k8s原生调度器的基本原理和问题
- 混部pod会占用原生的资源配额(例如cpu request),这会导致在线任务发布的时候没有可用资源;PS:把离线业务调到在线集群节点,得调的上去,调上去之后不能影响在线业务pod调度。
- 原生调度本质是静态调度,没有考虑机器实际负载,因此没法有效地将混部任务调度到实际负载有空闲的机器上。PS:得感知实际的机器负载。得调的上去,调上去之后要有措施防止出事。
字节跳动开源 Katalyst:在离线混部调度,成本优化升级提供资源超卖的能力,充分利用集群中 “已经售卖但未充分使用的资源” 部署更多低优业务,同时在系统侧完善 CPU、内存、磁盘、网络等多维度的资源隔离机制,并且智能预测、感知各类服务的负载变化,结合服务的分级机制,通过分钟级的指标感知和调控策略,保证服务的稳定性。
Koordinator 支持 K8s 与 YARN 混部基于 Hadoop YARN 开源版本,原则上不对 YARN 做侵入式改造。单机侧 同时运行kubelet(pod) 和 NodeManger(YARN Task within cgroup),koordlet 做一些协同。
可观测性体系
单机层面:操作系统级/资源隔离
容器的本质是一个受限制的进程,进程之间通过 namespace 做隔离,cgroups 做资源限制。在云原生时代,大部分业务资源都是基于容器来隔离和限制,但是在资源超售叠加混部场景下,CPU、内存等方面依然可能存在争抢。
- 例如在 CPU 方面,为了保证在线服务稳定性,普遍做法是进行绑核,将在线服务绑定在某个逻辑核心上避免其他业务占用。但是绑核对于有并行计算要求的服务并不友好,核数直接决定并行效率。
- 在内存方面,离线作业往往会读取大量文件数据,导致操作系统会做 page cache,而原生操作系统对 page cache 的管理是全局的,不是容器维度的。
百度混部实践:如何提高 Kubernetes 集群资源利用率?
历经 7 年双 11 实战,阿里巴巴是如何定义云原生混部调度优先级及服务质量的?在这些在线和离线的 Pod 之间,我们就需要用不同的调度优先级和服务质量等级,以满足在线和离线的实际运行需求。
- Kubelet 默认的 CPU 管理策略会通过 Linux 内核的 CFS 带宽控制器(CFS Bandwidth Controller)来控制容器 CPU 资源的使用上限。在多核节点下,进程在运行过程中经常会被迁移到其不同的核心,考虑到有些应用的性能对 CPU 上下文切换比较敏感,Kubelet 还提供了 static 策略,允许 Guaranteed 类型 Pod 独占 CPU 核心。
- 内核 CFS 调度是通过 cfs_period 和 cfs_quota 两个参数来管理容器 CPU 时间片消耗的,cfs_period 一般为固定值 100 ms,cfs_quota 对应容器的 CPU Limit。例如对于一个 CPU Limit = 2 的容器,其 cfs_quota 会被设置为 200ms,表示该容器在每 100ms 的时间周期内最多使用 200ms 的 CPU 时间片,即 2 个 CPU 核心。让应用管理员常常感到疑惑的是,为什么容器的资源利用率并不高,但却频繁出现应用性能下降的问题?从 CPU 资源的角度来分析,问题通常来自于以下两方面:
- 内核在根据 CPU Limit 限制容器资源消耗时产生的 CPU Throttle 问题;由于应用突发性的 CPU 资源需求(如代码逻辑热点、流量突增等),假设每个请求的处理时间均为 60 ms,即使容器在最近整体的 CPU 利用率较低,由于在 100 ms~200 ms 区间内连续处理了4 个请求,将该内核调度周期内的时间片预算(200ms)全部消耗,Thread 2 需要等待下一个周期才能继续将 req 2 处理完成,该请求的响应时延(RT)就会变长。这种情况在应用负载上升时将更容易发生,导致其 RT 的长尾情况将会变得更为严重。若想彻底解决 CPU Throttle,通常需要将 CPU Limit 调大两三倍,有时甚至五到十倍,问题才会得到明显缓解。而为了降低 CPU Limit 超卖过多的风险,还会降低容器的部署密度,进而导致整体资源成本上升。CPU Burst 解决了内核 BWC 调度时针对 CPU Limit 的限流问题,可以有效提升延时敏感型任务的性能表现。但 CPU Burst 本质并不是将资源无中生有地变出来,若容器 CPU 利用率已经很高(例如大于50%),CPU Burst 能起到的优化效果将会受限,此时应该通过 HPA 或 VPA 等手段对应用进行扩容。
- 受 CPU 拓扑结构的影响,部分应用对进程在 CPU 间的上下文切换比较敏感,尤其是在发生跨 NUMA 访问时的情况。在 NUMA 架构下,节点中的 CPU 和内存会被切分成了两部分甚至更多(例如图中 Socket0,Socket1),CPU 以不同的速度访问内存的不同部分,当 CPU 跨 Socket 访问另一端内存时,其访存时延相对更高。因此我们需要避免将 CPU 分散绑定到多个 Socket 上,提升内存访问时的本地性。Kubelet 提供的 CPU 管理策略 “static policy”、以及拓扑管理策略 “single-numa-node”,会将容器与 CPU 绑定,可以提升应用负载与 CPU Cache,以及 NUMA 之间的亲和性,但绑核策略并不是“银弹”,某 CPU Limit = 2 的容器,其应用在 100ms 时间点收到了 4 个请求需要处理,在 Kubelet 提供的 static 模式下,容器会被固定在 CPU0 和 CPU1 两个核心,各线程只能排队运行。CPU 绑核解决的是进程在不同 Core,特别是不同 NUMA 间上下文切换带来的性能问题,但解决的同时也损失了资源弹性。
我们为 cpushare 化应用普遍开启了 CPU Burst 策略后,这类应用的 Throttled 率显著降低,在日常态有很好的应用效果。但是,对整体 CPU 利用率较高的机器,大范围上调节点上 Throttled Pod 的 CPU Limit,存在着较大的 CPU 超卖风险,可能反而导致容器间毫秒级的 CPU 争抢,加剧应用延时的波动。因此,CPU Burst 策略引入了对节点 CPU 利用率的实时监控,当节点利用率高出冷却阈值(Cooling)时,延缓当前 Throttled Pod 的 CPU Limit 上调;当节点利用率进一步高出过载阈值(Overload)时,关闭所有 Pod 的 CPU Limit 上调。PS:pod limit 有一个不断上调的过程
张彦飞:离线任务一般需要很大的cpu 计算量,它会争抢在线任务的cpu,让在线任务的处理延迟(调度延迟)变高。也可能会干扰在线任务的cpu缓存,导致在线任务的运行性能变差。解决离线任务队在线任务抢夺的办法中,有taskset 限制离线任务的核数、调整离线任务的nice值等,但都治标不治本。因为对于内核来讲,它并不知道哪些任务是在线任务,哪些任务是离线任务,它都一视同仁的去调度,自然无法从根本上解决问题。我认为最根本的办法是修改调度器,直接深入到调度器层次来解决问题。比如腾讯的Tencent-OS就是在调度算法层开发了离线调度算法BT,该算法知道哪些任务是在线任务哪些任务是离线,在在线任务有需要的时候,可以及时抢占离线任务使用的cpu。如果在线任务比较多,就会排挤离线任务,让在线任务像没有离线任务一样使用所有的cpu核。当在线任务不忙时,离线任务才得以运行,充分榨干计算资源。PS:之前的很多设计更多是通过qos 标记不同任务的级别,某个qos任务可以被抢占。
集群层面:调度
一文看懂业界在离线混部技术目前主要有几种决策方式:
- 整机分时复用:在固定的时间点 (比如凌晨以后) 跑离线作业,白天让出资源给在线服务。这种以时间维度切分的混部方式比较简单易理解,但整体资源利用率提升有限。
- 资源部分共享:将单机的资源整体划分为在线资源、离线资源以及在离线共享资源,各资源之间隔离,提前划分预留。这种从单机资源维度切分的混部方式比分时复用相对更精细一些,但是需要资源规格较大的机器切分才有意义。
- 资源完全共享:通过及时准确的资源预测手段、快速响应资源变化的能力,以及一套可以在资源水位发生变化时的服务保障措施,更高效自动化地实现机器资源复用。资源归属不预设,完全依据实时指标决策。
前一种属于静态决策,相对来说对底层可观测性体系的要求、对调度系统的高可用高性能的要求较低。后两种属于动态决策,在资源利用率的提升上比静态决策更优,但对前述支撑系统要求也更高。
超发资源(是一种新的资源)可以极大的提升集群的资源利用率,但也会凸显集群内节点之间资源利用率不均匀的现象。这个现象在非混部环境下也是存在的,只是因为 Kubernetes 原生是不支持资源超发机制,节点上的利用率往往不是很高,一定程度上掩盖了这个问题(PS:无非node1 利用率20%,node2 利用率40%的问题)。利用率不均匀一般是节点之间不均匀以及出现局部的负载热点,在负载高的节点上,在线应用和离线任务之间可能会存在的严重的资源冲突,影响到在线应用的运行时质量。为了解决这个问题, Koordinator 的调度器提供了一个可配置的调度插件控制集群的利用率。该调度能力主要依赖于 koordlet 上报的节点指标数据,在调度时会过滤掉负载高于某个阈值的节点,防止 Pod 在这种负载较高的节点上无法获得很好的资源保障,另一方面是避免负载已经较高的节点继续恶化。在打分阶段选择利用率更低的节点。该插件会基于时间窗口和预估机制规避因瞬间调度太多的 Pod 到冷节点机器出现一段时间后冷节点过热的情况。
调度器调度时是根据当时集群内的情况和配置,做出综合的判断,选择出一个最合适的节点分配给 Pod 使用。但随着时间和工作负载的变化,原本最合适的节点也会变差,差异化 SLO 和调度器都提供了丰富的能力帮助改善这些问题,但差异化 SLO 更多还是关注在自身单机的情况,无法感知全局的变化。 从控制的角度看,我们也需要根据集群内的情况做出决策,把异常的 Pod 驱逐迁移到更合适的节点,让这些 Pod 有机会可以更好的对外服务。
各个公司实现
规范NRI(发展中)
kubelet 的 CPU Manager 和 Memory Manager 对 CPU 和内存的管理策略比较朴素,且没有提供扩展机制。
NRI:下一代节点细粒度资源控制方案为了满足不同业务应用场景的需求,特别是在在线任务与离线任务混布的场景下,在提高资源利用率的同时,也要保证延迟敏感服务可以得到充分的资源保证,这就需要 Kubernetes 提供更加细粒度的资源管理功能,增强容器的隔离性,减少容器之间的互相干扰。例如,CPU 编排,内存分层,缓存管理,IO 管理等。目前有很多方案,但是都有其一定的局限性。
- Proxy 模式,在客户端(Kubelet)和 CRI Runtime(containerd,CRI-O 等) 之间增加一个 CRI Proxy 中继请求和响应,在 Proxy 中劫持 Pod 以及 Container 的创建/更新/删除事件,对 Pod 的 Spec 进行修改或者完善,将硬件感知的资源分配策略应用于容器中。缺点:增加了 Pod 创建管理流程的链路以及部署和维护成本
- Standalone 模式,在每一个 Work Node 上创建一个 Agent,当这个 Agent 监听到在本节点的 Pod 创建或者修改事件的时候,再根据 Pod Spec 中的 annotation,转换成细粒度资源配置的 Spec,然后调用 CRI Runtime 实现对 Pod 的更新。缺点:在侦听到 Pod 创建以及修改的事件后,才会对 Pod 进行更新,会有一定的延迟。
NRI(Node Resource Interface), 是用于控制节点资源的公共接口, 是 CRI 兼容的容器运行时插件扩展的通用框架。它为扩展插件提供了跟踪容器状态,并对其配置进行有限修改的基本机制。主要由两个部分组成,一个是集成在 CRI 运行时中的 Adaptation,另一个是用户自定义的 NRI 插件。Adaptation 和 NRI 插件之间通过 Unix Socket 进行通信。
NRI 插件有两种运行方式。
- 一种是 NRI 插件作为一个独立的进程,通过 NRI Socket 与 CRI 运行时进行通信。这种情况下可以把 NRI 插件部署成为一个 DaemonSet,更方便在 Kubernetes 上管理。
- 另一种是可以把编译好的 NRI 插件二进制文件放在 containerd 的指定路径下,由 containerd 发起对 NRI 插件的调用,这种方式有点类似于 CNI 的机制。
NRI 为扩展插件提供了跟踪容器状态,并对其配置进行有限修改的基本机制。它几乎覆盖了 Pod/Container 所有的生命周期事件,对容器常用的修改主要有三部分:Annotation、环境变量、系统资源。NRI 可以 Hook 的 Pod 的生命周期事件有 3 个,Container 的事件有 8 个。NRI 插件除了可以通过 Hook 的方式获取容器事件,NRI 提供了一个 stub.UpdateContainer 的方法也可以主动对容器进行更新。
腾讯
浪费主要来自以下几个方面:
- 业务需求与节点可调度资源很难完全匹配,因此在每个节点上都可能剩余一些碎片资源无法被分配出去。
- 业务通常为了绝对稳定,会申请超出自身需求的资源,这会导致业务锁定了资源但事实上未能有效利用。
- 资源用量存在波峰波谷,很多在线业务都是有着规律性的服务高峰和低峰的,如通常白天负载较高,资源用量较大,而夜间在线访问降低,资源用量也会跌入低谷。
Crane 提供了 Request 推荐、副本数推荐、HPA 推荐以及 EPA 等业务优化能力,能辅助业务自动化决策进行资源配置优化。然而在较大的组织中,业务改需要所有业务组件负责人的支持和配合,周期长、见效慢。如何在不改造业务的前提下,迅速提升集群资源利用率,在提升部署密度的同时保证延迟敏感和高优业务的稳定性和服务质量不受干扰?混部。
如果只是简单的将不同业务类型部署到一起,而不进行任何层面的资源隔离,那么在线业务服务质量必然会被影响,这也是为什么混部难以落地的核心原因。
- 从资源维度,干扰可能发生在任何一个资源维度,比如常见的 CPU 以及 CPU 相关的 L1、L2、LLC 缓存、内存带宽、磁盘 IO、网络 IO 等。其次从干扰发生的层级来看,干扰可能发生在应用代码、操作系统、硬件等不同层级。 2 对于应用而言,任何一环都可能成为干扰的来源;同时这些因素之间也会互相关联,例如应用网络流量上升,不仅会造成带宽的抢占,通常还会导致 CPU 资源消耗上升;又比如一个应用虽然计算逻辑简单,但需要频繁访问内存数据,如果此时缓存失效,则应用需要访问物理内存,而 CPU 负载会因为忙等而上升。
因此判断干扰是否发生,进一步寻找干扰源,并通过技术手段避免干扰是复杂的,这是干扰检测自动化门槛高的核心原因,如何能在关联的因素中识别干扰以及绕过表象找到真实的干扰源是混部需要解决的核心问题。
混部方案的能力概览如下:闲置资源识别、回收 ==> 弹性资源利用(低优业务即可通过资源声明将弹性资源利用起来)==> 干扰检测与主动回避(判断干扰是否发生,干扰发生时牺牲低优Pod以确保高优业务的服务等级不变,包括:压制谁,有哪些压制手段)
- 节点负载画像与弹性资源回收 Crane 实时采集节点利用率数据,并基于多种预测算法计算出未来的闲置资源,为节点构建画像,并将其以扩展资源形式更新成节点可调度资源。弹性资源的多少随高优业务真实用量变化,高优业务用量上升,弹性资源减少。
- 弹性资源再分配,低优业务使用弹性资源,调度器确保低优业务首次调度时有足够弹性资源可用,防止节点过载。
- 基于自定义水位线的干扰检测和主动回避能力
- NodeQoS API 允许集群运维定义节点水位,包括总 CPU 水位,或者弹性资源分配率、弹性资源水位等,并定义当真实用量达到水位时的回避动作。
- PodQoS API 定义不同类型工作负载的资源隔离策略,如 CPU 调度优先级,磁盘 IO 等,同时定义该类型业务允许的回避动作。
- AvoidanceAction 定义调度禁止、压制、驱逐等动作参数,当节点水位被触发,只有允许某个动作的业务 Pod 才可以执行该操作。
- 基于内核隔离的增强 QoS 能力 Crane 的开源方案中,可以通过动态调节 CGroup 压制干扰源资源上限。同时,为支撑大规模生产系统的的隔离需求,Crane 基于腾讯 RUE 内核,通过多级 CPU 调度优先级,以及绝对抢占等特性,保证高优业务不受低优业务的影响。
- 支持模拟调度的优雅驱逐等增强的重调度能力 当压制不足以抑制干扰时,就需要从节点中驱逐低优 Pod 以确保高优业务的服务质量。Crane 支持模拟调度的优雅驱逐重调度能力能够借助集群全局视角和预调度能力降低重调度对应用的影响。
离线调度算法(BT)业界现有混部方案无法普遍适用,问题就在离在线业务在cpu调度算法这一层次没有做区分,造成:如果离线业务在运行时,在线业务到来无法及时的抢占CPU;再者,在做负载均衡时,无法区分离在线业务,造成在线业务挤占相同的CPU,无法均匀合理的分散到所有所有CPU上。 为此,好的混部方案就是将离在线业务彻底分开,所以在调度算法这一层次就要做区分。基于这种考虑,开发了针对离线业务的新调度算法bt,该算法可以保证在线业务优先运行。新调度算法的基本算法借鉴于CFS,但在CPU选择、抢占、负载均衡、时延处理、CPU带宽控制等多个方面都有自己的特点和要求,有特有的处理方式。特别是配有特有的负载均衡策略、CPU带宽控制策略等。PS: 有几个图非常经典可以学一下。
字节
Katalyst开源方案实践:字节如何实现全天高水平集群资源利用效率二次销售在线未使用的资源,利用离线工作负载能够很好地填补这部分超售资源,实现资源利用效率在全天保持在较高水平。
- 阶段一,在离线分时混部
- 阶段二:Kubernetes/YARN 联合混部
- 阶段三:在离线统一调度混部
- 最开始开展混部项目的时候,我们的底层隔离能力还并不十分完善,所以我们早期采取的是 “0/1” 的方式进行混部,同一时段不会同时有在线业务和离线业务运行在同一台机器上,当在线服务的波谷来临后,几乎所有服务都会因为弹性缩容而导致副本数降低。当集群的部署水位低于设置的阈值后,控制面会通过一定规则选取部分在线节点,将该节点上的 Pod 驱逐到别的节点上,并标记该节点不可调度,最后将离线服务调度到该节点上实现资源的出借。当在线服务的波峰来临后,会发生一个逆向的控制过程,控制面在通知并确保离线任务撤离后,重新将节点设置为在线可调度状态,实现资源的回收。分时弹性混部的控制面最主要的职责就是控制节点的动态出让和回收。
- 离线业务稳定性保证。弹性资源最大的特点是它整体的资源供应量不确定,当在线服务出现抖动时,我们需要优先保证在线服务的稳定性,极端情况下需要通过杀死离线业务来为在线服务腾挪资源。而离线任务通常运行的时间长,频繁杀死和重跑任务对离线业务来说也会造成较大的影响。为了解决资源回收的过程中无脑地杀死离线业务的问题,研发团队构建了弹性资源的优先级,基于优先级实现资源回收。以 PS-Worker 架构的离线分布式训练为例,PS 作业可能会处于一个 High 的优先级;能够满足基本运行的 Min 的 Worker 处于中优的优先级;为了进行弹性加测的 Worker 处于 low 的优先级。除此之外,我们在分时弹性混部的控制面引入提前通知的机制,在提前通知的时间窗口内,不会再调度新的离线任务,同时尽可能保证那些已经被调度的任务顺利跑完,从而将任务杀死率维持在一个可接受的范围内。
- 总体来说,分时弹性混部比较适合基础设施能力建设尚处于早期的用户,在现有环境中快速上量,实现资源效能提升。
字节跳动大规模K8s集群管理实践 值得细读。单集群支持运行各种负载,上面联邦层管理,最上面给用户一个paas平台,资源可以在集群范围内腾挪。每个任务出费用单;下调某类、部门的资源用量 腾挪给突发活动等。基本上理清了一个公司 paas 的终态。datacenters as a computer。
后 Hadoop 时代,字节跳动如何打造云原生计算平台调度系统融合后,在 Kubernetes 集群的基础上增加三个组件:
- Yodel:模拟实现 YARN 的 ResourceManager,支持 YARN API 及其 AM 管理、Quota 管理、权限管理等功能。
- Unified Scheduler:高性能调度器,取代 Kubernetes 原生调度器,提供了强大的多租户资源隔离能力,以及更丰富的调度策略。
- BigData Plugin:单机大数据插件,用于辅助 Kubelet 完成大数据作业的 Localization、Shuffle 等工作。 在离线业务都统一使用同一个融合集群。具有多租户资源隔离和管控的 Unified Scheudler 统一对集群中所有 Pod 进行调度,统一管控了在离线资源的动态划分。在线服务按照原有接口,提交到 API Server;离线作业按照 YARN 接口,提交到 Yodel,无需任何改动。Yodel 具有和 YARN ResourceManager 一样的功能,并且可以把 YARN Resource Request 转换成 Kubernetes Pod,再转换成 YARN Container。在单机上,所有 Pod 统一由 Kubelet 启动和管理。原来 YARN NodeManager 具有的大数据特有功能移植到 BigData Plugin,辅助 Kubelet 完成,比如为大数据作业提前下载 Jar 包,这个过程又称为 Localization。
从混合部署到融合调度:字节跳动容器调度技术演进之路 混合部署
- 基于时延容忍度和可重试性两个维度,字节内部的服务被大致划分为以下几种类型:
- 混部技术架构的逻辑大致是:优先满足在线微服务的资源需求,提供剩余的闲置资源给离线服务使用;并且当在线服务需要更多资源时,能够快速抽调离线的资源供给在线服务。
- Sysprobe 作为一个系统监控,它会拿到单机层面各种容器的资源使用情况,并通过一系列机器学习算法推导出机器上离线侧可使用的资源类型,然后将它出让给 NodeManager,由 NodeManager 动态上报到中心的 RM 来进行资源的统一展示。PS:识别弹性资源
- 基于集群的三层调度系统视角来构建混部,包括中心的调度器、节点的调度器以及内核的调度器,它们分别承担了三种不同的资源调度角色。中心的调度器比如 K8s 和 YARN RM, 它们主要负责完成容器到节点的选择,尽可能平衡资源、稳定负载。但是当节点层面在线服务发生 QoS 抖动时,我们往往需要做出更快的响应,此时分钟级的调度响应延迟是完全不被接受的。Sysprobe QoS Controller 组件需要实时动态地调整节点的实际资源分配,当在线需要更多资源时,能够快速地回收资源。至于秒级的响应,由于 Sysprobe QoS Controller 处于用户态的角色,因此它通常会受到单机层面高负载异常情况的影响,也难以做到彻底的兜底。因此我们还需要在 Kernel 级别,比如 CPU 调度器、 IO 调度器上做一些更深度的定制,实现系统层面更强的兜底能力,更好地保障延迟敏感的服务对 CPU 时间的需求,同时保证吞吐型服务能够获得足够多的 CPU 时间,从而兼顾延迟与吞吐,达到整体效能的最大化。
实现了离在线混部并不意味着调度系统演进就此终止,整个数据中心的利用率其实还未全面充分得到提升。
- 一方面,上述混部系统的资源表达、抽象是不完整的,并非所有的离线作业都可以使用不稳定的资源;
- 另一方面,它仍然是两个独立的系统,其资源管理体系、底部机器供给运维都是割裂的,上层平台和周边设施是独立建设的,这就导致了更大范围的共池复用非常困难。 统一资源抽象:调度器支持全场景调度需求 是不同负载共池的基础,其支持的资源类型也需要是在离线场景的类型超集。
字节跳动的云原生技术历程演进为了解决资源统一管理这个问题,我们提出了三个思路:
- 抽象能够提供的资源售卖模型,方便不同的业务线、业务系统准确地表达自身的需求;我们给应用提供的资源形态以 CPU 维度为例一共分为三级:
- 独占核/dedicated_core:以独占的形式去获得物理核,这些 core 上除了应用自身以外不会运行其他租户的进程。我们又细分了 Numa 的拓扑分配以及忽略拓扑结构的两个子类,提供了对微拓扑结构上的优化选项;
- 共享核/shared_core:把不同的应用的 Pod 运行在一个共享 CPU 的 Pool 上,这样可以同时针对不同应用形态在 CPU 调度域上的划分,更细粒度地隔离开应用之间的影响;
- 回收核/reclaimed_core:在共享核的基础上,通过混部控制系统的方式去回收部分的低优资源,我们可以低优混部的共享方式去提供算力的供给。
- 创建一套统一的 Quota 管理平台,这个平台可以让开发者们灵活地管理自身的各类资源;
- 资源分层调度系统使得单机集群对字节内部所有计算资源做到快速灵活的交付。
- 单机调度主要是扩展了 Kubernetes 的单机资源管控:资源的微拓扑结构感知和资源的分配策略,主要解决了如何让不同 cores 形态的 Pod 统一运行在一个节点之上。
- 集群中心调度器需要解决的核心问题是如何让不同形态的应用在整个集群里自由地调度。需要满足不同的调度语义细粒度的要求,充分降低集群空置率。在调度性能方面,同时要满足低频次和批式的大吞吐的调度场景。针对各种应用场景提升调度场景的质量也是集群中心调度器需要解决的问题。
- 全局调度,考虑到字节跳动的整体规模,单一的集群能力不足以满足管理字节全球数据中心的需求,并且在应用之间的隔离、多区域的容灾以及算力的标准化问题上字节也有更加细粒度的调度要求。
字节跳动开源 Katalyst:在离线混部调度,成本优化升级
当联邦层被架设在不同的离线和在线集群之上后,我们通过统一的虚拟化队列管理,实现了离线到在线的非常灵活的资源拆借逻辑。举一个例子,原来在准备活动资源时,可能会涉及大量的运维和搬迁,而现在只需要通过平台调整对应的 Quota ,就可以高效地实现离线到在线的容量切换,实现百万核心分钟级别地交付和梯度式回收。
美团
提升资源利用率与保障服务质量,鱼与熊掌不可兼得?LAR全称是集群负载自动均衡管理系统(LAR,Load Auto-Regulator)
按照很多同学的理解,通过非常简单的操作即可达成这个目标——提高单机的服务部署密度。但如此简单的操作,为何全球数据中心资源利用率仅为10%~20%呢?利用率如此之低,这里最为关键的因素有三个:
- 部署到同一台物理机的服务在资源使用上存在相互干扰。
- 服务在流量上存在高低峰,反映在资源使用上也有高低峰。
- 关键核心在线服务的服务质量下降无法接受。
传统的方案通过节点资源超卖来解决资源申请和实际资源使用之间存在的Gap,并引入根据负载的动态调度策略。
- 调整节点资源超售,虽然能在一定程度上缓解资源申请和使用的Gap问题,但由于Gap在不同的服务间并不相同,加上服务资源使用的波峰波谷分布集中的情况(美团在线业务的典型特征),此方法在整体上过于粗放,会导致节点间的负载分布不均衡,部分节点负载很高,影响服务质量;另一部分节点负载极低,实际上形成资源浪费。
- 而根据负载直接进行资源调度,由于负载是动态变化的,在调度算法设计及计算框架实现上会非常复杂,且效果一般。
提升资源利用率的本质是提升资源共享复用水平,而保障服务质量则需要通过资源隔离能力,保障服务的性能稳定。针对上述两个根本点,LAR在Kubernetes上提出两个核心创新点:
- 资源池化分级
- 通过将单机资源划分到不同的资源池,提升资源在池内的共享复用水平。
- 不同的资源池之间有不同的优先级,并提供不同的资源隔离水平(资源隔离水平越高,资源共享复用水平越低)。
- 资源在不同优先级的资源池之间根据优先级和资源池的资源负载水平流动,优先保障高优资源池服务的资源使用,从而保障其服务质量。
- 动态负载和静态资源映射
- 资源的分配,本质上是负载空间的分配。假设单机整体CPU利用率小于50%的情况下,运营在其上的服务的服务质量不会有影响,那么这个机器的静态资源其实对应的就是节点50% CPU利用率的负载空间。换个角度看,就是无论如何调度分配资源,只要这个节点的负载不超过50%即可。
- 业务静态的资源申请,根据服务的特征经过调度计算后,服务被放入对应的资源池,而资源池的资源配置则根据池内所有服务的实际负载进行资源配置,并可以实时地根据负载调整资源配置,实现静态资源分配和动态负载的映射管理。PS:pod的request /limit 是用户填的,不去调整,但是pod 的cgroup 都从属于k8s的某个qos cgroup,可以调整k8s 某个qos cgroup的限额。
通过池间资源隔离达到池间服务的干扰隔离。资源池内资源的配置依据服务的负载进行动态调整,并通过资源配置的调整,控制资源池内部的资源负载维系在相对稳定的范围内,从而保证服务质量。
以3级资源池为例,节点资源被划分为0、1、2三类资源池,优先级依次降低。初始整个机器无服务调度其上,资源全部集中在Pool2。随着服务的调度,Pool1先调度了服务1,这时会根据上述的资源计算方式,LAR将Pool2的对应的资源调整至Poo1,Pool2资源减少。随着Pool1中服务增多,配置的资源随之增多,Pool2相应资源减少。优先级最高的Pool0调入服务后,同样的资源从Pool2调整至Pool0;Pool2调度入服务时,Pool2资源不变。 3个资源池配置不同的资源配置管理策略,0号池优先级最高,池内目标CPU负载控制在30%~50%之间;1号池优先级次之,池内目标CPU负载控制在45%~60%之间;2号池优先级最低,池内目标CPU负载控制在50%~80%。已分配的资源由资源池内服务共享,在池间相互隔离。在负载低时,不同资源池根据资源池管理策略,自动调整各资源池的资源配置,保证资源池内负载稳定;出现资源紧张时,高优资源池可以从低优资源池抢占资源,优先保障高优服务的资源需求。
池内分配资源会随着负载进行变化,引起池间的资源流动。池间资源流动遵循以下规则:
- 所有资源池的池内分配资源之和为节点可分配的资源总量。
- 当池内负载降低,释放资源到最低等级的资源池,复用闲时资源。
- 当池内负载升高,向等级低于自身的资源池,根据从低到高的顺序进行资源请求,根据优先级满足服务资源需求。
- 池内的资源最多不会超过用户申请的量。
以3级资源池为例:
- 当Pool1负载升高时,从等级更低的Pool2抢占资源,优先保障自身的服务资源需求,Pool1负载降低时,将冗余的资源释放回Pool2。
- 当Pool0负载升高时,优先从Pool2抢占资源,当Pool2资源不足时,从Pool1抢占资源,保证更高等级的服务资源需求,当Pool0负载降低时,冗余的资源被释放回Pool2,此时3. 若Pool1存在负载压力,则会重新从Pool2抢占资源。
QoS服务质量保障机制,为提升资源利用率会导致资源竞争,LAR通过池间、池内两层QoS服务质量保障机制,分级保证服务的隔离性和稳定性。
- 池间多维度资源隔离,LAR对资源池进行了多维度的资源隔离与限制。除了基础资源(CPU、Memory),还对磁盘I/O、CPU调度、Memory Cache、内存带宽、L3 Cache、OOM Score、网络带宽等更细粒度的资源进行了隔离,进一步提升不同等级服务间的隔离性,保证服务不会受到其他资源池的影响。PS:由MTOS 的相关特性支持
- 池内多层级保障策略,当资源池内负载出现不符合预期的情况时(如容器负载异常),由于资源池内资源共享,整个资源池的服务都可能受到影响。LAR基于资源池内不同的负载等级,制定了多级保障策略。QoSAdaptor周期性(秒级)地获取节点负载的数据,并计算资源池的负载等级。当负载达到一定的资源等级时,执行对应的负载策略。通过CPU降级、驱逐等行为,根据优先级对部分容器进行资源降级,保障池内绝大多数容器的稳定性。
- 容器驱逐:当池内Memory使用接近Cgroup限制,避免整个资源池出现OOM,影响所有容器的正常运行,会结合优先级筛选Memory使用较多的容器进行驱逐操作。PS:Kubernetes原生的驱逐策略基于整个节点的负载,LAR中将策略缩小到了资源池维度
- CPU降级:池内CPU负载超过一定负载等级,避免高负载导致的容器间互相影响,LAR会结合优先级筛选CPU使用较多的容器,对其CPU使用进行单独的限制。降级操作存在定时检查机制,当负载恢复正常,或有资源可以抢占的情况下,会将CPU限制进行恢复。
- 强制抢占:从更低等级的资源池抢占资源,与普通资源抢占的区别为,即使资源已经被其他池使用,强制抢占会优先满足高等级资源池的需求。
LAR基于资源池的历史负载与历史分配情况,对池内高峰资源使用情况进行预测,为节点资源调整提供指导。由于资源池负载变化比较频繁,同时受到池内服务变更、资源总量、高低峰时间区间等因素的影响,节点基于实时负载进行池内资源的变更较不稳定。Recommender周期性地根据各节点资源池的历史负载与分配情况进行高峰资源预测,并下发到节点,提供高峰负载控制指导,提升资源池资源保障的稳定性。同时通过RCF完成动态负载和静态资源的转换,在调度层屏蔽了动态负载变化,减少负载频繁变化对调度准确性的影响。
LAR的设计目标是在保障服务质量的同时提升整体资源的利用率,在资源分池分级的设计上,针对通用的在线服务进行服务分级,对接不同资源池,提供不同的服务质量保障,从而提升资源的利用率。而对于离线服务,本身相对于在线服务的服务质量要求低,故而LAR天然地适用于混部场景。PS:就是抛开在离线的概念,独立搞出一个带有优先级的资源池的概念
- 对于在线服务,通过对服务进行分级,并通过服务画像对服务进行细致刻画,将资源敏感型服务和关键核心服务部署到LAR优先级最高的资源池中。 Serverless:基于个性化服务画像的弹性伸缩实践
- 而对于一般的在线服务,部署在次优先级资源池。
- 在混部场景中,假设将资源池分为0、1、2三个级别,优先级依次由高到低。0和1号池分别对应核心关键在线服务和一般的在线服务,而2号池对应离线服务使用的资源池。
一方面我们对高优资源池配置更强的资源隔离策略(比如CPU绑核、进程优先调度等),另一方面高优池资源利用率控制在一个安全较低的水位;而低优池,则相对在一个更高的水平。LAR的资源动态调整保障负载能力,会自动将0号池与1号池在业务低峰期(负载低)的闲置资源回收,提供给2号池的离线服务使用。并且QoS服务质量保障机制,可以确保在业务高峰来临时,秒级抢占2号池资源(对于内存等非复用型资源,通过驱逐方式强制回收),从而保障在线服务的资源使用。
B站
- colocation agent模块中,策略组件会实时加载当前接收到的混部配置,并调用autopilot组件进行混部算力的计算,然后通过device-plugin组件上报混部扩展资源,例如
caster.io/colocation-cpu
。 - 动态计算。针对各类物理资源,例如cpu、memory等,我们会分别设置机器的安全水位值n%。agent会实时探测在线进程的资源使用量online_usage,然后根据安全水位和在线负载动态计算出可混部资源量。在线使用量和可混部资源量是此消彼长的,随着在线使用量上升,我们上报的可混部量就会下降,反之,当在线使用量下降,可混部量就会上升。
- 分时计算。如果部分在线业务在某些时间段不希望部署混部任务,我们就需要用到分时策略,即在某些时间段关闭混部或者减少上报混部资源量。另外,我们还支持设置grace period,在分时混部结束前,会提前停止调度混部任务到该k8s node,并等待存量任务结束,做到优雅退出。
- 混部任务在申请资源配额时,仍然申请原生cpu资源,但是会增加pod标签
“caster.io/resource-type: colocation”
。k8s webhook模块根据标签识别到混部pod,然后将pod申请的资源修改为混部扩展资源。这种方式对业务层屏蔽了底层扩展资源,通过标注pod标签即可指定是否使用混部资源。PS: 混部pod不能占用原生的资源配额(例如cpu request) - 混部调度器job-scheduler根据pod申请的扩展资源量以及各个节点上报的混部扩展资源量进行调度。
webhook会对混部任务申请的资源类型进行修改,去除原生的资源类型request.cpu
和request.memory
,改成caster.io/colocation-cpu
和caster.io/colocation-memory
。由于没有申请原生资源类型,那么k8s会自动将这类pod归类为best effort类型,并且最终通过runc将该类pod的cgroup设置到/sys/fs/cgroup/cpu/kubepods/besteffort
目录,我们称为“混部大框”。
- cpu动态隔离。在cgroup层面,我们给大框设置了最小的cpu share值,保证在资源争抢时,混部任务获得cpu时间片的权重最小。colocation agent中的cgroup manager组件负责动态地调整“混部大框”的cpu quota,从而对混部任务进行整体的资源限制。当colocation agent检测到在线负载降低时,就会调大“混部大框”的cpu quota,让混部任务充分利用空闲的算力。当在线负载升高时,则是缩小“混部大框”的cpu quota,快速让出资源给在线业务使用。此外,我们通过cpuset cgroup对整体混部大框做了绑核处理,避免混部任务进程频繁切换干扰在线业务进程。当混部算力改变时,agent会给大框动态选取相应的cpu核心进行绑定。另外,选取cpu核心的时候也考虑了cpu HT,即尽量将同一个物理核上的逻辑核同时绑定给混部任务使用。否则,如果在线任务和混部任务分别跑在一个物理核的两个逻辑核上,在线任务还是有可能受到“noisy neighbor”干扰。
- 内存动态隔离。与cpu隔离方式类似,colocation agent会根据当前在线业务内存使用情况,动态扩缩混部大框的memory quota。另外,通过调节oom_score_adj,混部任务的oom_sore被设为最大值,保证oom时混部任务尽量优先被驱逐。PS:cpu 是软性资源,缩小quota 会可以立刻让出cpu时间,感受上就是“立即让出cpu”的效果,离线任务只是慢了,对于内存则做不到,因为不可能立即把内存“夺走”,这个时候为了让出内存资源,就只能强行驱逐。
- 网络带宽限制。我们使用了cni-adaptor组件,使得k8s node可以支持多种网络模式,对于转码混部任务,通常不需要被外部访问,因此通过annotation可以指定bridge网络模式,分配host-local的ip,避免占用全局网段中的ip资源。同时也利用了linux tc进行混部pod的网络带宽限制。
- 驱逐机制。混部agent层支持内存、磁盘、cpu load等维度的驱逐机制。当任意资源负载达到设置的驱逐水位时,agent会立即驱逐机器上的混部任务。为了防止任务在同一台机器上被频繁驱逐,需要在驱逐后设置一定的冷却时间,冷却期内禁止调度混部任务。
针对训练、转码等离线集群跑大数据混部任务的场景,我们基于在离线混部框架做了功能增强,其关键点在于yarn nodemanager on k8s。
- yarn node manager以daemonset的形式部署在相应的混部节点上
- 节点上的混部agent会动态检测在线容器负载变化,并根据设置的混部策略计算可混部值。这个值一方面会通过接口上报给yarn rm,另外一方面会设置到混部大框的cgroup中进行动态的资源限制
- 用户把大数据任务提交到混部集群时,rm会找到集群中有充足资源的混部节点,并最终在nm中拉起task
为了降低大数据任务对非混部业务的影响,大数据团队也做了相关技术改造,例如支持remote shuffle、基于应用画像识别小规格任务进行调度等。
vivo
vivo 在离线混部探索与实践 混部平台必须具备两个产品能力:
- 强大的调度、隔离能力
- 完善的监控、运维能力
我们把混部组件分为管控组件和单机组件两大类。
- 管控组件主要负责调度和控制,根据vivo业务使用场景,我们对调度器做了一些增强,提供了numa感知、负载感知,热点打散,批量调度等能力。
- 混部控制器主要提供了一些配置管理能力:如资源画像统计、node slo配置、node扩展资源变更等。内核层面我们使用了龙蜥OS,它具备强大的资源隔离能力,可以帮助我们更好的隔离在线、离线资源使用,保障在线服务质量。
我们为混部建立一套完整的可视化监控体系。
- 针对在线服务我们提供了:容器资源使用指标,受离线干扰指标、业务混部收益指标等监控能力。
- 针对离线任务,我们提供了离线可用资源、任务异常状态等监控能力。 在平台层面上我们提供了节点、内核,核心组件的监控,通过这些监控可及时发现平台潜在的风险。
为了简化运维操作,提升运维效率,我们对混部集群搭建和节点扩缩容操作进行了白屏化改造,开发了资源池管理功能,简化了物理机接入流程,运维效率大幅提升。在运维平台上运维人员可以快速调整混部隔离、水位线等参数,如果发现在线服务受到干扰,运维人员可以一键关闭混部,驱逐离线任务,保障在线服务质量。