YARN 的优化与实践
优质博文:IT-BLOG-CN
本文从利用率提升、多负载场景优化、稳定性提升、异地多活四个方面介绍了字节跳动在四年来对 Hadoop YARN 进行的一系列的优化,以及生产环境中的实践经验。
YARN 简介
YARN 生态圈
YARN (Yet Another Resource Negotiator) 是 Hadoop 集群的资源管理系统,是 Hadoop 生态中非常重要的成员项目。
一般来说,离线生态可以分为五层:
【1】最底层是裸金属层, 由众多物理节点组成,每个节点上运行着通用的操作系统。
【2】次底层是集群资源管理层, YARN 就处在这一层中。
【3】再往上是分布式计算引擎层, MR/Spark/Flink 等计算引擎处于这层,为了能让业务同学更加低成本的写计算任务, 各个引擎都支持 SQL 功能。
【4】再往上是作业托管层,用来提交 ad-hoc 的作业,管理周期性的批处理作业,管理长时间运行的流式作业。
【5】最上层是用户逻辑层,如数据日报,数据分析,模型训练等。
YARN 架构
上图中灰色背景区域是 YARN 的主要架构, 主要包含两种角色:
ResourceManager:
【1】整个集群的大脑,负责为应用调度资源,管理应用生命周期。
【2】对用户提供接口,包括命令行接口,API,WebUI 接口。
【3】可以同时存在多个 RM,但同一时间只有一个在工作,RM 之间通过 ZK 选主。
NodeManager:
【1】为整个集群提供资源,接受 Container 运行。
【2】管理 Contianer 的运行时生命周期,包括 Localization,资源隔离,日志聚合等。
YARN 上运行的作业:在运行时会访问外部的数据服务,常见的如 HDFS,Kafka 等。会在运行结束后由 YARN 负责将日志上传到 HDFS 中。
字节跳动对 YARN 的定制
字节跳动的 YARN 是在 16 年从社区当时最新的 2.6.0 版本中 fork 出来的,主要承载着公司内的离线作业/流式作业/模型训练三大场景。由于公司内的 YARN 服务规模巨大、场景复杂,遇到了各种问题,在社区版本没有提供解决方案之前,内部研发同学定制了许多内容来解决具体问题,经过 4 年来上千次的修改,公司内的版本已经跟社区的版本相差较大。今天给大家介绍一些比较关键的定制,希望能给大家带来一些启发。这些关键定制主要包括四个方面:
【1】利用率提升:包括分配率提升和物理使用率提升。
【2】多种负载场景优化:包括批处理 / 流式 / 模型训练 三种场景下的体验提升。
【3】稳定性提升:包括摆脱对 HDFS 强依赖,Container 分级与驱逐,非受控 Container 管理。
【4】异地多活:包括统一的 YARN Client 和 UI 等内容。
利用率提升
多线程版本的 Fair Scheduler:
我们为提升单集群规模做了一系列的优化:
【1】首先,通过对 YARN 内部事件梳理调整,精准的修改了一些事件处理逻辑。
【2】然后,将 NodeManager 节点的心跳机制改为根据 ResourceManager 的压力动态调整。
【3】之后,修改内存单位(int->long)突破单个集群 21 亿 MB 的限制。
【4】再之后,通过对切主过程进行深度优化,将切主时间控制在秒级。
当然还有很多其它的细节优化不再一一列举,最终的效果是让单个生产集群达到了 2 万节点的规模。
公司内离线的资源全天都比较紧张,而流式作业和在线服务的资源使用量随着用户的行为,在时间上有明显的波峰波谷,在凌晨时通过混部的方式将流式和在线富余的资源提供给离线可以全面的提升利用率。
我们通过将 NodeManager 改造为可以根据宿主机的富余资源动态的调整的 NM,来达到与流式作业和在线服务的混部,为离线提供更多资源的目的。目前生产环境中已有数万台节点进行了混部,混部后将原机器的 CPU 利用率绝对值提升了 20%以上。
原生的 YARN 中,用户申请的资源和实际使用的资源经常会出现比较大的偏差,导致出现大量的资源浪费的情况,为此我们开发了一整套的资源动态调整方案,可以将申请的资源调整到接近于实际使用资源的数值。
并且,在实际使用中发现,如果资源调整必须以一个核为最小粒度的话,还是会出现很严重的浪费,比如用户真实的需求可能是 0.001 个核*1000,原生的 YARN 只能分配 1000 个核,就白白浪费了 999 个核。我们开发了以千分之一核为最小粒度的功能,可以有效的减少资源的浪费。并且千分之一核与资源动态调整结合,可以更加精细化的调整资源。
多种负载场景优化
字节跳动的 YARN 承载了公司内的 批处理 / 流式 / 模型训练 三大场景,由于 YARN 天生是为批处理而设计的,很多地方与流式 / 模型训练场景并不匹配,为了给这些场景更好的体验,需要做一些定制工作。
Gang Scheduler 提供了一个 All-or-Nothing (一次全交付或不交付)的语义,如作业申请 1000 个 container,那么要么直接返回 1000 个 container,要么就返回失败,并提示失败的原因。这样可以有效的避免两个作业都只拿到一半的资源,谁也无法启动的互锁局面。
除此之外,Gang Scheduler 还有个特性是超低延迟, 它可以在毫秒级给出 All-or-Nothing 的结论,这样可以大大缓解流式作业在重启时的 lag 积压问题。
最重要的是,Gang Scheduler 为流式作业和训练作业提供了全局视角,每个作业可以通过配置自己定制的强约束和弱约束来达到全局最优的放置策略。其中,强约束是指必须要满足的条件;弱约束是指尽量满足,但确实无法满足时可以接受降级的约束。目前支持的强约束包括节点属性, 高负载等;支持的弱约束包括:节点属性,高负载,Container 打散,Quota 平均,GPU 亲和性等。
对于训练场景,我们还定制了更丰富的内容。包括:
【1】为了更好的隔离性,定制了支持 GPU 和 Ceph 的 Docker;
【2】为了更灵活的资源申请,定制了带范围的资源值 (传统的 YARN 资源只有个数, 没有范围,比如多少个 CPU,多少 GB 内存,但在训练场景下,有时希望有范围,比如当需要两个 GPU 卡时,不止希望随意的两张卡,而是希望要一台机器上两个连号的 GPU 卡,比如卡 0 和卡 1 是连号的,而卡 0 和卡 2 不是连号的。这个场景同样也适用于端口号。)
【3】为了更高效的同时使用 CPU 和 GPU 机器,定制了节点属性功能。
稳定性优化
异地多活
未来工作
同时,我们也会继续完善 YARN Gang Scheduler,包括:
【1】更加丰富的调度谓词;
【2】更加低延迟;
团队介绍
基础架构 YARN 团队负责字节跳动公司内部离线/流式/模型训练三大场景的资源管理和调度, 支撑了推荐/数仓/搜索/广告等众多核心业务,管理着在集群规模、调度吞吐能力、资源利用率、业务复杂性等多个方向上都在业界领先的超大规模集群。
针对公司内的抖音、今日头条等产品重度依赖推荐的特点, 团队对调度器进行了深度定制以支持流式(Flink)训练和 GPU 训练等场景, 拥有几十项专利技术。同时为了进一步提升集群资源利用率,调度团队已经开启在离线大规模混部,并且预期在不久后会进一步融合 YARN / K8S 等调度系统。