写点什么

作业帮 Kubernetes 原生调度器优化实践

  • 2021 年 10 月 21 日
  • 本文字数:3075 字

    阅读完需:约 10 分钟

作业帮 Kubernetes 原生调度器优化实践

调度系统的本质是为计算服务或任务匹配合适的资源,使其能够稳定高效地运行,以及在此基础上进一步提高资源使用密度,而影响应用运行的因素非常多,比如 CPU、内存、IO、差异化的资源设备等一系列因素都会影响应用运行的表现。同时,单独和整体的资源请求、硬件 / 软件 / 策略限制、 亲和性要求、数据区域、负载间的干扰等因素以及周期性流量场景、计算密集场景、在离线混合等不同应用场景的交织也带来了决策上的很多变化。


调度器的目标则是快速准确地实现这一能力,但快速和准确这两个目标在资源有限的场景下往往会产生矛盾,这需要在二者间权衡,本文主要分享了作业帮在实际应用 K8s 过程中遇到的问题以及最终探讨出的解决方案,希望对广大开发者有所帮助。

调度器原理和设计


K8s 默认调度器的整体工作框架可以简单用下图概括:

两个控制循环


1、第一个控制循环称为 Informer Path,主要工作是启动一系列 Informer,用来监听(Watch)集群中 Pod、Node、Service 等与调度相关的 API 对象的变化。比如,当一个待调度 Pod 被创建出来之后,调度器就会通过 Pod Informer 的 Handler,将这个待调度 Pod 添加进调度队列;同时,调度器还要负责对调度器缓存 Scheduler Cache 进行更新,并以这个 cache 为参考信息,来提高整个调度流程的性能。


2、第二个控制循环即为对 pod 进行调度的主循环,称为 Scheduling Path。这一循环的工作流程是不断地从调度队列中取出待调度的 pod,运行两个步骤的算法,来选出最优 node


  • 在集群的所有节点中选出所有“可以”运行该 pod 的节点,这一步被称为 Predicates;

  • 在上一步选出的节点中,根据一系列优选算法对节点打分,选出“最优”即得分最高的节点,这一步被称为 Priorities。


调度完成之后,调度器就会为 pod 的 spec.NodeName 赋值这个节点,这一步称为 Bind。而为了不在主流程路径中访问 Api Server 影响性能,调度器只会更新 Scheduler Cache 中的相关 pod 和 node 信息:这种基于乐观假设的 API 对象更新方式,在 K8s 中称为 Assume。之后才会创建一个 goroutine 来异步地向 API Server 发起更新 Bind 操作,这一步就算失败了也没有关系,Scheduler Cache 更新后就会一切正常。

大规模集群调度带来的问题和挑战


K8s 默认调度器策略在小规模集群下有着优异表现,但是随着业务量级的增加以及业务种类的多样性变化,默认调度策略则逐渐显露出局限性:调度维度较少,无并发,存在性能瓶颈,以及调度器越来越复杂。


迄今为止,我们当前单个集群规模节点量千级,pod 量级则在 10w 以上,整体资源分配率超过 60%,其中更是包含了 GPU、在离线混合部署等复杂场景。在这个过程中,我们遇到了不少调度方面的问题。

问题 1:高峰期的节点负载不均匀


默认调度器,参考的是 workload 的 request 值,如果我们针对 request 设置的过高,会带来资源浪费;过低则有可能带来高峰期 CPU 不均衡差异严重的情况;使用亲和策略虽然可以一定程度避免这种,但是需要频繁填充大量的策略,维护成本就会非常大。而且服务的 request 往往不能体现服务真实的负载,带来差异误差。而这种差异误差,会在高峰时体现到节点负载不均上。


实时调度器,在调度的时候获取各节点实时数据来参与节点打分,但是实际上实时调度在很多场景并不适用,尤其是对于具备明显规律性的业务来说,比如我们大部分服务晚高峰流量是平时流量的几十倍,高低峰资源使用差距巨大,而业务发版一般选择低峰发版,采用实时调度器,往往发版的时候比较均衡,到晚高峰就出现节点间巨大差异,很多实时调度器往往在出现巨大差异的时候会使用再平衡策略来重新调度,高峰时段对服务 POD 进行迁移,服务高可用角度来考虑是不现实的。显然,实时调度是远远无法满足业务场景的。

我们的方案:高峰预测时调度


针对这种情况,需要预测性调度方案,根据以往高峰时候 CPU、IO、网络、日志等资源的使用量,通过对服务在节点上进行最优排列组合回归测算,得到各个服务和资源的权重系数,基于资源的权重打分扩展,也就是使用过去高峰数据来预测未来高峰节点服务使用量,从而干预调度节点打分结果。

问题 2:调度维度多样化


随着业务越来越多样,需要加入更多的调度维度,比如日志。由于采集器不可能无限速率采集日志且日志采集是基于节点维度。需要平衡日志采集速率,各个节点差异不可过大。部分服务 CPU 使用量一般但是日志输出量很大,而日志并不属于默认调度器决策的一环,所以当这些日志量很大的多个服务 pod 在同一个节点上时,该机器上的日志上报就有可能出现部分延迟。

我们的方案:补全调度决策因子


该问题显然需要对调度决策补全,我们扩展了预测调度打分策略,添加了日志的决策因子,将日志也作为节点的一种资源,并根据历史监控获取到服务对应的日志使用量来计算分数。

问题 3:大批量服务扩缩带来的调度时延


随着业务复杂度进一步上升,在高峰时段出现,会有大量定时任务和集中大量弹性扩缩,大批量(上千 POD)同时调度导致调度时延上涨,这两者对调度时间比较敏感,尤其对于定时任务来说,调度延时的上涨会被明显感知到,原因是 K8s 调度 pod 本身是对集群资源的分配,反应在调度流程上则是预选和打分阶段是顺序进行的。如此一来,当集群规模大到一定程度时,大批量更新就会出现可感知的 pod 调度延迟。

我们的方案:拆分任务调度器,加大并发调度域、批量调度


解决吞吐能力低下最直接的方法就是串行改并行,对于资源抢占场景,尽量细化资源域,资源域之间并行。基于以上策略,我们拆分出了独立的 Job 调度器,同时使用 Serverless 作为 Job 运行的底层资源。K8s Serverless 为每一个 Job POD 单独申请了独立的 POD 运行 sanbox,也就是任务调度器,完整并行。以下为对比图:


原生调度器在晚高峰下节点 CPU 使用率

优化后调度器在晚高峰下节点 CPU 使用率

总结


Work 节点资源、GPU 资源、Serverless 资源是我们集群异构资源的三类资源域,这三种资源上运行的服务存在天然差异,我们使用 forecast-scheduler、gpu-scheduler、job-schedule 三个调度器来管理这三种资源域上的 Pod 调度情况。


  • 预测调度器管理大部分在线业务,其中扩展了资源维度,添加了预测打分策略。

  • GPU 调度器管理 GPU 资源机器的分配,运行在线推理和离线训练,两者的比例处于长期波动中,高峰期间离线训练缩容、在线推理扩容;非高峰期间离线训练扩容、在线推理缩容;同时处理一些离线图片任务来复用 GPU 机器上比较空闲的 CPU 等资源。

  • Job 调度器负责管理定时任务调度,定时任务量大且创建销毁频繁,资源使用非常碎片化,而且对时效性要求更高;所以我们将任务尽量调度到 Serverless 服务上,压缩集群中为了能容纳大量任务而冗余的机器资源,提升资源利用率。

未来演进探讨


  • 更细粒度的资源域划分,将资源域划分至节点级别,节点级别加锁来进行。


  • 资源抢占和重调度。正常场景下,当一个 Pod 调度失败,这个 Pod 会保持在 pending 的状态,等待 Pod 更新或者集群资源发生变化进行重新调度,但是 K8s 调度器依然存在一个抢占功能,可以使得高优先级 Pod 在调度失败时,挤走某个节点上的部分低优先级 Pod 以保证高优先级 Pod 的正常运行,迄今为止我们并没有使用调度器的抢占能力,即使我们通过以上多种策略来加强调度的准确性,但依然无法避免部分场景下由于业务带来的不均衡情况,这种非正常场景中,重调度的能力就有了用武之地,也许重调度将会成为日后针对异常场景的一种自动修复方式。


作者介绍:


吕亚霖,作业帮基础架构 - 架构研发团队负责人。2019 年加入作业帮,负责技术中台和基础架构工作。在作业帮期间主导了云原生架构演进、推动实施容器化改造、服务治理、GO 微服务框架、DevOps 的落地实践。

2021 年 10 月 21 日 15:323053

评论 1 条评论

发布
用户头像
您好,能开源代码吗
2021 年 11 月 26 日 11:17
回复
没有更多了
发现更多内容

解读顶会CIKM'21 Historical Inertia论文

华为云开发者社区

华为云 论文 长序列时间 时间序列预测 CIKM’21

【VueRouter 源码学习】第四篇 - 创建路由映射表

Brave

源码 vue-router 9月日更

微信PaxosStore:深入浅出Paxos算法协议

OpenIM

模块二

树建

架构实战营

ReScript 与 TypeScript,谁是前端圈的“当红辣子鸡”

华为云开发者社区

Java JavaScript typescript 前端 ReScript

牛掰!阿里人用7部分讲明白百亿级高并发系统(全彩版小册开源)

Java~~~

Java 架构 面试 多线程 高并发

PostgreSQL插件之TimescaleDB

华为云数据库小助手

postgresql 插件 GaussDB 华为云数据库

学校疫苗中心管理应用搭建心得

明道云

NDK的C++ 库支持

Changing Lin

9月日更

50亿海量数据如何高效存储和分析? GaussDB (for Cassandra) 3个秘诀搞定

华为云开发者社区

存储 华为云 海量数据 分析 GaussDB (for Cassandra)

揭秘盒马鲜生 Android 短视频秒播优化方案

阿里云视频云

android 音视频 短视频 Video播放器 移动端

直播预告|如何通过“智能边缘安全”提升企业免疫力?

京东科技开发者

https 云安全 CDN加速 网站反爬 网站建设

LeetCode题解:897. 递增顺序搜索树,递归,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

限量!腾讯高工用4部分讲清楚了Spring全家桶+微服务

Java~~~

Java spring 架构 面试 微服务

当视频恋爱 App 用上了 Serverless

Serverless Devs

Serverless 云原生

杀疯了,编程语言还能卷成这样❓❗【话题讨论】

InfoQ写作平台官方

话题讨论 话题

Go- 文件读写-1

HelloBug

文件读写 Go 语言

netty系列之:搭建HTTP上传文件服务器

程序那些事

Java Netty HTTP 文件服务器 程序那些事

叹服!阿里自述SpringCloud微服务:入门+实战+案例

Java~~~

Java spring 架构 面试 Spring Cloud

华云大咖说 | 业务转型中的IT云化服务——安超云基座方案介绍

华云数据

携手强化「内容审核」能力,融云与数美科技达成战略合作

融云 RongCloud

音视频 内容安全 融云 即时通讯IM

🚄【Redis 干货领域】让你彻底会使用“Redis中最陌生且最强大的集合”(ZSET)【上部】

浩宇天尚

redis Zset 9月日更 Redis指令

自己动手写个微型博客吧,还能实现网页版 Blink,No.1

梦想橡皮擦

9月日更

书单 | 做数字化转型,离不开这10本书!

博文视点Broadview

合规安全大考核:移动应用安全策略全盘点

蚂蚁集团移动开发平台 mPaaS

RPC mPaaS 移动开发平台 隐私安全 加固

阿里一面 五问 @Transactional

skow

Java Spring Boot 后端

膜拜!首次公布Java10W字面经,Github访问量破百万

Java~~~

Java 架构 面试 微服务 多线程

Node.js 应用全链路追踪技术——[全链路信息获取]

vivo互联网技术

node.js 前端 编程语言 全链路追踪 语言 & 开发

别问了,我真的不喜欢这个注解!

why技术

Java

大厂的 SDK 写法,偷学到了!

程序员鱼皮

Java c++ Python 架构 前端

这个夏天,这群大学生奔波在盐场里忙啥?

科技热闻

作业帮 Kubernetes 原生调度器优化实践-InfoQ