固定链接 Hadoop YARN 在滴滴离线与实时计算中的应用

Hadoop YARN 在滴滴离线与实时计算中的应用

Hadoop YARN 在滴滴离线与实时计算中的应用

滴滴目前服务国内业务的有两个 Hadoop 集群,分为离线集群和实时集群。

离线集群上主要运行 Hive(MR) 和 Spark 任务,实时集群主要运行 Spark Streaming 和 Flink Streaming 任务,那么既然都是 Hadoop 集群,为什么要分成两个集群呢?离线计算和实时计算有什么不同呢?

下面从 Hadoop YARN 对资源管理和调度的角度试着做一些比较。

Hadoop YARN 简介

YARN 全称 Yet Another Resource Negotiator,是一套全新的任务调度与资源管理框架,是 Hadoop 2.x 的关键组件之一。

YARN 也是典型的 Master-Slave 架构,Master 称为 ResourceManager(RM), Slave 称为 NodeManager(NM)。

RM 负责接收用户提交的任务,并且决定为任务分配多少资源和调度到哪个 NM 去执行;NM 是真正执行任务的节点,周期性的向 RM 汇报自己的资源使用状况并领取 RM 分配的任务,负责启动和停止任务相关的进程等工作。

根据不同的调度算法与管理方式,YARN 内置了多个调度器,滴滴使用其中的 FairScheduler,并且通过队列来管理计算资源的分配。目前离线集群有数千台机器,每天运行任务几十万个。实时集群有几百台机器,长期运行着数千个任务。

当前的部署情况示意如图:

离线与实时计算的对比

下面从任务特点、调度实现、集群管理等几个方面对比一下这两个集群的不同之处。

任务特点

离线集群 实时集群
运行时长 几分钟到几小时 永远运行
输入输出 HDFS Kafka 等消息中间件和外部存储系统
时效性要求

离线任务运行时长一般几分钟到几个小时不等,少量任务可能运行几十个小时甚至更长。实时任务则因为是流式数据,正常情况下永远运行,不会结束。

离线数据源和产出一般都是 HDFS,实时任务数据源和产出可以是 Kafka 等消息中间件,也可以 sink 到本集群外的离线存储,以备进一步的 OLAP 分析。这种情况下,实时集群对磁盘要求不高,主要用于存放任务运行所需的资源和运行过程中产生的日志,而离线集群则存放各个层次的数仓数据,对磁盘空间要求较高。

离线任务运行时间长短不影响任务结果,比较敏感的报表也只是要求规定时间之前完成即可,可以容忍分钟级甚至小时级的延迟。实时任务对时效性要求非常高,数据延迟产出后很可能就失去了价值,往往只能接受秒级甚至毫秒级的延迟。

资源隔离

离线集群 实时集群
对机器负载敏感程度
NodeLabel数 1 数十个
CGroups 控制 相对宽松 相对严格

由于任务配置问题、机型不一致等原因,目前还做不到机器负载很均衡,而实时任务对于机器负载很敏感,load 过高时数据容易延迟。因此,为了不互相影响,现在两个集群独立部署。

同样是不互相影响的原因,实时集群内部的不同业务之间也需要隔离部署。我们基于 FairScheduler 开发了 NodeLabel 机制,实现了机器间的资源隔离。目前离线只有一个 NodeLabel,隔离了几台机器,但只是因为部分任务需要配置外部系统的白名单,为了避免白名单过长而设置的。实时集群则有多个 NodeLabel,保证不同的任务可以运行在不同的机器上,使对负载格外敏感的任务互不影响。

对于进程间也有资源隔离,主要是 CPU 的隔离,通过 CGroups 实现。离线集群只开启了相对的CPU 控制,根据任务 vcore 的配置按比例分配 CPU 资源;而实时集群则相对严格,根据 vcore 配置实现了有上下限的弹性严格控制。

调度效率

离线集群 实时集群
调度效率要求
调度均衡要求 一般

离线集群因为机器多、任务多,频繁有任务提交,有资源申请,因此需要特别高的调度效率,才能保证任务能及时得到资源,同时集群资源也不被浪费。而实时集群,只有有新任务提交或者有 container 失败要重新申请时才需要资源调度,频率非常低,因此对调度效率要求不高。但出于避免数据延迟的考虑,实时集群对调度均衡要求比较高。

滴滴 YARN 对离线集群的调度效率做了多次优化,包括优化调度算法、在线更换高配置机器、削减不必要的事件等,调度效率提升了十倍以上。针对实时集群则针对均衡性做了更多优化,通过按照更敏感的 CPU 调度以及不同 NodeLabel 分别调度等方式,实现了均衡调度。并且,通过弹性资源控制,指导和推动用户合理的资源配置,避免了资源浪费和负载过高问题。

资源利用率

离线集群 实时集群
资源利用率曲线 尖刺多 比较平稳

离线任务需要的资源各不相同,每秒钟都有 container 启动和释放,因此总体资源利用率不稳定,呈现很多尖刺。实时任务因为没有频繁的 container 启动和释放,而且内部有比如 NetworkBuffer 的缓冲机制,资源利用率曲线则平稳的多,主要随打车流量的变化而变化。

比如离线集群某台机器一天的 CPU 利用率曲线如图:

实时集群某台机器一天的 CPU 利用率曲线如图:

出于为公司节省成本的考虑,在兼顾任务运行速度的同时,机器的资源利用率越高越好。离线需要整体考虑,通过资源超卖、提高队列 max resources 和 min resources 的比值、减小利用率低的队列资源等手段,提高整体资源利用率,也让真正需要资源的任务能得到更多的资源。而实时集群因为不同业务对应不同机器,则要有针对性的调整任务资源配置,才能使得每个业务的机器资源利用率都比较高。

集群调整

离线集群 实时集群
扩容 直接添加机器 添加机器后要重启任务,重启要避开高峰期
缩容 直接下线机器 下线机器,整体操作要避开高峰期

考虑到业务量的增长、机器资源利用率等因素,集群增加或减少机器是不可避免的操作。

离线集群扩容直接添加机器即可,马上会有新任务提交占据这些机器。而实时集群扩容,通常是因为现有资源不能满足现有任务,只增加机器并不能使正在运行的任务再跑到这些机器上来,因此需要重启任务或者杀掉部分机器上的 container,使之重新调度,才能跑到新的空闲机器上。但是重新调度 container 会使任务延迟,需要评估任务能否接受,因此通常选在晚上 10 点之后操作,这时候打车高峰期已过,数据流量较小,有延迟也能及时追上。

离线集群缩容直接杀掉上面的 NodeManager 和 container 即可,YARN 有重试机制,会自动重新调度上面的 container,对任务的影响只是总体进度变慢,一般都可以接受。实时集群缩容则类似扩容,重新调度 container 会导致任务延迟,要避过高峰期操作。

另外,YARN 有最大重试次数,默认为 2 次,因为实时任务通常已经运行很长时间,可能因为别的原因已经失败过 1 次,这时杀死上面的 AM 会导致任务失败。为此我们还重新开发配置了重试策略,设为 1 天内最多失败 10 次。

版本升级

离线集群 实时集群
影响 任务变慢,操作时间太长,难以接受 任务大范围延迟,不可接受

YARN 的 NodeManager 部署在集群除 master 节点外的所有机器上,一旦有版本升级,需要更新所有机器上的安装包并重启服务。原本的逻辑中,杀死 NM 会导致其上的 container 也被杀死并重新调度,这对离线和实时集群都不可接受,原因略有不同。

前文已经说过,离线集群升级会导致任务变慢,为了尽量减小影响,需要小批量的重启NM,这导致全集群升级的时间会非常长,升级成本太大。而实时集群则对延迟非常敏感,全集群 container 的重启甚至任务失败通常不可接受。

为此我们开启了 NM 的 Recovery 机制,在短时间内重启不影响 container 的运行,新启动的 NM 仍然能接管和监控已启动的 container,实现了升级无感知,大大降低了升级成本。

总结

从上文可以看出,离线集群的特点是量大而管理粗放,注重吞吐与效率;实时集群的特点是量小但管理精细,注重稳定与延时。

实时任务非常的娇贵,不到万不得已集群的任何变动都不应该影响任务。目前还做不到完全不影响,但随着一个个问题的解决,未来的集群调整和机器故障等问题对任务的影响会越来越小。

本文作者:薛康

您的留言将激励我们越做越好