本文从利用率提升、多负载场景优化、稳定性提升、异地多活四个方面介绍了字节跳动在四年来对 Hadoop YARN 进行的一系列的优化,以及生产环境中的实践经验。
YARN (Yet Another Resource Negotiator) 是 Hadoop 集群的资源管理系统,是 Hadoop 生态中非常重要的成员项目。
一般来说,离线生态可以分为五层:
上图中灰色背景区域是 YARN 的主要架构, 主要包含两种角色:
YARN 上运行的作业:
字节跳动的 YARN 是在 16 年从社区当时最新的 2.6.0 版本中 fork 出来的,主要承载着公司内的离线作业/流式作业/模型训练三大场景。 由于公司内的 YARN 服务规模巨大、场景复杂,遇到了各种问题,在社区版本没有提供解决方案之前,内部研发同学定制了许多内容来解决具体问题,经过 4 年来上千次的修改,公司内的版本已经跟社区的版本相差较大。
今天给大家介绍一些比较关键的定制,希望能给大家带来一些启发。这些关键定制主要包括四个方面:
社区原生版本的 FairScheduler 是单线程的,在节点数量较多时,是整体集群最大的瓶颈.
我们通过将 FairScheduler 改造为并发的多线程版本,并将调度器内部的锁拆分为更加细粒度的读锁和写锁,将调度吞吐提升 7 倍以上,在生产环境中达到每秒 3K 个 Container 的速度(未触及性能瓶颈)。
原生的 YARN 在调度时只考虑资源是否满足,经常会出现一个节点 CPU 被打满,但是内存还有剩余的情况。
我们引入节点 DRF(Dominant Resource Fairness)机制,计算每个节点的剩余资源的主资源,当调度的 Task 的主资源与节点的主资源不匹配时,先延迟此次调度,直到一定次数后再放松约束。
通过引入这个机制,集群资源的碎片化问题大幅降低,生产环境中可以达到 CPU 和内存的 24 小时平均利用率都在 90%以上。
单个集群的规模越大,就可以有更多的用户和作业使用这个集群,这个集群的利用率也会更高。但是原生的 YARN 在达到 5K 节点规模时开始出现各种问题,比如说一次简单的切主可能会导致整个集群雪崩。
我们为提升单集群规模做了一系列的优化。
当然还有很多其它的细节优化不再一一列举,最终的效果是让单个生产集群达到了 2 万节点的规模。
公司内离线的资源全天都比较紧张,而流式作业和在线服务的资源使用量随着用户的行为,在时间上有明显的波峰波谷,在凌晨时通过混部的方式将流式和在线富余的资源提供给离线可以全面的提升利用率。
我们通过将 NodeManager 改造为可以根据宿主机的富余资源动态的调整的 NM',来达到与流式作业和在线服务的混部,为离线提供更多资源的目的。
目前生产环境中已有数万台节点进行了混部,混部后将原机器的 CPU 利用率绝对值提升了 20%以上。
原生的 YARN 中,用户申请的资源和实际使用的资源经常会出现比较大的偏差, 导致出现大量的资源浪费的情况,为此我们开发了一整套的资源动态调整方案,可以将申请的资源调整到接近于实际使用资源的数值。
并且,在实际使用中发现,如果资源调整必须以一个核为最小粒度的话,还是会出现很严重的浪费,比如用户真实的需求可能是 0.001 个核*1000,原生的 YARN 只能分配 1000 个核,就白白浪费了 999 个核。我们开发了以千分之一核为最小粒度的功能,可以有效的减少资源的浪费。并且千分之一核与资源动态调整结合,可以更加精细化的调整资源。
字节跳动的 YARN 承载了公司内的 批处理 / 流式 / 模型训练 三大场景,由于 YARN 天生是为批处理而设计的,很多地方与流式 / 模型训练场景并不匹配,为了给这些场景更好的体验,需要做一些定制工作。
流式作业和训练作业的调度需求与批处理有很大的不同:批处理强调的是高吞吐,而流式/训练类型的作业更加强调低延迟和全局视角。为了弥补原生 YARN 在低延迟和全局视角上的缺陷,我们开发了一个全新的调度器 Gang Scheduler。
Gang Scheduler 提供了一个 All-or-Nothing (一次全交付或不交付)的语义,如作业申请 1000 个 container,那么要么直接返回 1000 个 container,要么就返回失败,并提示失败的原因。这样可以有效的避免两个作业都只拿到一半的资源,谁也无法启动的互锁局面。
除此之外,Gang Scheduler 还有个特性是超低延迟, 它可以在毫秒级给出 All-or-Nothing 的结论,这样可以大大缓解流式作业在重启时的 lag 积压问题。
最重要的是,Gang Scheduler 为流式作业和训练作业提供了全局视角,每个作业可以通过配置自己定制的强约束和弱约束来达到全局最优的放置策略。其中,强约束是指必须要满足的条件;弱约束是指尽量满足,但确实无法满足时可以接受降级的约束。目前支持的强约束包括节点属性, 高负载等;支持的弱约束包括:节点属性,高负载,Container 打散,Quota 平均,GPU 亲和性等。
除了开启 YANR 原生默认支持的 CGroup 限制之外,我们还配置了更加丰富的 CGroup 管理策略,比如在 share 模式下支持自定义的最大值限制,支持绑核,支持绑 NUMA 节点等. 通过这些措施,给流式作业和训练作业更加灵活的管控策略,满足不同场景下的隔离或共享需求。
对于训练场景,我们还定制了更丰富的内容。包括:
离线批处理场景经常会遇到"Fetch Failed"的问题,主要来源是本地的磁盘 IOPS 不足,导致 Shuffle Service 卡住,为了缓解这个问题,我们在资源调度的过程中加入目标主机 LoadAvg 的考虑因素,如果一台机器的 LoadAvg 过高,则暂时跳过对其分配新任务. 通过这个机制,将"Fetch Failed"问题降低了约 40%。
字节跳动的 YARN 服务规模巨大,在稳定性方面遇到了很多挑战,有很多细节方面的优化, 在这里由于时间有限,挑选几个比较有代表性的优化点跟大家分享一下:
随着公司发展迅猛,YARN 也迎来了异地多机房的场景,原生的 YARN 只支持单集群使用, 对用户的使用造成不便,如果每个集群都孤立的提供给用户的话,会让用户使用起来很困难,为此,我们对异地多活做了一些定制工作:
未来我们会持续的优化与流式和在线服务的混部工作,包括:
同时,我们也会继续完善 YARN Gang Scheduler,包括:
基础架构 YARN 团队负责字节跳动公司内部离线/流式/模型训练三大场景的资源管理和调度, 支撑了推荐/数仓/搜索/广告等众多核心业务,管理着在集群规模、调度吞吐能力、资源利用率、业务复杂性等多个方向上都在业界领先的超大规模集群。
针对公司内的抖音、今日头条等产品重度依赖推荐的特点, 团队对调度器进行了深度定制以支持流式(Flink)训练和 GPU 训练等场景, 拥有几十项专利技术。同时为了进一步提升集群资源利用率,调度团队已经开启在离线大规模混部,并且预期在不久后会进一步融合 YARN / K8S 等调度系统。
业务扩张,团队长期在北京/杭州招人。简历投递入口: https://job.toutiao.com/s/KcoXsV
欢迎关注字节跳动技术团队