本文来自OPPO互联网基础技术团队,转载请注名作者。同时欢迎关注我们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。
什么是YARN?
Apache Hadoop YARN:Yet Another Resource Negotiator,另一种资源协调者。
Apache Hadoop YARN 是一种新的Hadoop资源管理器。它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。也就是说YARN在Hadoop集群中充当资源管理和任务调度的框架。
1. YARN的架构
1.1 ResourceManager
ResourceManager是YARN中的主节点服务,它负责集群中所有资源的统一管理和作业调度。
简单来讲,ResourceManager主要完成的功能包括:
- 与客户端交互,处理来自客户端的请求;
- 启动和管理ApplicationMaster,并在它运行失败时重新启动它;
- 管理NodeManager,接收来自NodeManager的资源汇报信息,并向NodeManager下达管理指令(比如杀死container等);
- 资源管理与调度,接收来自ApplicationMaster的资源申请请求,并为之分配资源。
1.2 NodeManager
NodeManager是YARN集群中的每个具体节点的资源和任务管理者。NodeManager的主要功能包括:
- 定时向ResourceManager汇报本节点上的资源使用情况和各个Container的运行状态;
- 接收并处理ApplicationMaster对container的启动、停止等各种请求;
- 管理Container的生命周期,监控Container的资源使用;
- 管理任务日志和不同应用程序用到的附属服务(auxiliary service)。
1.3 ApplicationMaster
用户提交的每个应用程序均包含一个ApplicationMaster,主要功能包括:
- 与ResourceManager调度器协商以获取资源;
- 将得到的资源进一步分配给内部的任务;
- 与NodeManager通信以启动或停止任务;
- 监控所有任务的运行状态,并在任务运行失败时负责进行容错处理。
1.4 Container
Container是YARN中的资源抽象,它封装了某个节点上的多个维度的资源,如CPU、内存、磁盘、网络等。当ApplicationMaster向ResourceManager申请资源时,ResourceManager为ApplicationMaster 返回的资源是用Container表示的。
2. 提交Application到YARN的流程
当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:
第一阶段:启动ApplicationMaster;
第二阶段:由ApplicationMaster创建应用程序;为它申请资源,并监控它的整个运行过程,直到运行完成。
如下图所示,以一个mapreduce程序提交到YARN的过程进行分析。
2.1 作业提交
第1步:
client 读取作业配置信息并创建Job的环境,调用job.waitForCompletion 方法,向集群提交一个MapReduce 作业 。
第2步:
资源管理器给任务分配一个新的作业ID 。
第3步:
作业的client核实作业的输出路径,计算输入文件的分片,将作业的资源 (包括:Jar包、配置文件,split信息等) 拷贝到HDFS集群上的作业提交目录。
第4步:
通过调用资源管理器的submitApplication()来提交作业。
2.2 作业初始化
第5步:
当资源管理器收到submitApplciation()的请求时,就将该请求发给调度器 (scheduler),调度器向NodeManager发送一个启动container的请求。
第6步:
节点管理器NodeManager启动container,内部运行着一个主类为 MRAppMaster的Java应用。其通过创造一些对象来监控作业的进度,得到各个task的进度和完成报告 。
第7步:
然后其通过分布式文件系统HDFS来获取由客户端提前计算好的输入split,然后为每个输入split创建一个map任务,根据mapreduce.job.reduces创建 reduce任务对象。
2.3 任务分配
如果作业很小,为了降低延迟,可采用Uber模式。在该模式下,所有的Map Task和Reduce Task都在ApplicationMaster所在的Container中运行。
第8步:
如果不是小作业,那应用管理器向资源管理器请求container来运行所有的map和reduce任务 。
这些请求是通过心跳来传输的,包括每个map任务的数据位置。比如:存放输入split的主机名和机架(rack),调度器利用这些信息来调度任务,尽量将任务分配给存储数据的节点或相同机架的节点。
2.4 任务运行
第9步:
当一个任务由资源管理器的调度器分配给一个container后,AppMaster通过联系NodeManager来启动container。
第10步:
任务由一个主类为YarnChild的Java应用执行,在运行任务之前首先本地化任务需要的资源。比如:作业配置、JAR文件以及分布式缓存的所有依赖文件 。
第11步:
最后,启动并运行map或reduce任务 。
2.5 进度和状态更新
YARN中的任务将其进度和状态 (包括counter)返回给应用管理器。
客户端每秒 (通过mapreduce.client.progressmonitor.pollinterval设置) 向应用管理器请求进度更新,展示给用户。
2.6 作业完成
除了向应用管理器请求作业进度外,客户端每5秒会通过调用 waitForCompletion()来检查作业是否完成,时间间隔可以通过 mapreduce.client.completion.pollinterval来设置。
作业完成之后,应用管理器和container会清理工作状态、作业的执行详情记录文件.jhist会被转移到历史服务器目录、container的执行日志会被nodemanager上传到HDFS集群供用户长期查询。
3. Yarn-主控程序RM介绍
如图所示,ResouceManager主要包括以下几个部分组成:
1. User Service(用户交互模块)
ResouceManager分别针对普通用户、管理员和Web提供了三种对外服务。具体实现分别对应:
ClientRMService:处理普通用户的提交程序、终止程序、获取程序状态等请求
AdminService:处理管理员的刷新队列、更新ACL权限等请求
RMWebApp:展示集群资源使用情况和应用程序执行状态等信息的web页面
2. Manage NMS(nodemanager管理模块)
该模块主要包括几个组件:
NMLivelinessMonitor:监控nodemanager的存活状态,如果nodemanager超时未汇报心跳信息,则将从集群中剔除该节点
NodesListManager:维护正常节点和异常节点列表,类似于集群的黑名单和白名单节点
ResourceTrackerService:处理来自NodeManager的请求,主要包括注册和心跳请求
3. Security(安全管理模块)
ResouceManager自带了非常全面的权限管理机制,主要由以下三个模块构成:
RMDelegationTokenSecretManager;
DelegationTokenRenewer;
ClientToAMSecretManager。
4. Manage Apps(Application管理模块)
该模块主要包括几个组件:
ApplicationACLSManager :管理应用程序的查看和修改权限
RMAppManager:管理应用程序的启动和关闭
ContainerAllocationExpirer:监控AM是否在规定的时间内将获得的container在NodeManager上启动
5. Manage AMS(AM管理模块)
该模块主要的组件包括:
AMLivelinessMonitor:监控AM是否存活
ApplicationMasterLauncher:与nodemanager通信以启动ApplicationMaster
ApplicationMasterService:处理来自ApplicationMaster的注册和心跳两种请求
6. 状态机管理模块
ResourceManager使用有限状态机维护有状态对象的生命周期,共维护了4类状态机,分别是:
RMApp:一个Application的运行生命周期
RMAppAttempt:一个Application运行实例的生命周期
RMContainer:一个Container的运行生命周期
RMNode:一个NodeManager的生命周期
7. ResouceScheduler(资源调度模块)
该模块主要就是涉及资源调度一个组件,比较常见的实现是:
FairScheduler:公平调度器
CapacityScheduler:容量调度器
YARN采用了基于事件驱动的并发模型,该模型能够大大增强并发性,从而提高系统的整体性能。
在YARN中所有核心服务实际上都是一个中央异步调度器,包括:
ResourceManager
NodeManager
MRAppMaster等。
它们维护了事先注册的事件和事件处理器,并根据接收的事件类型驱动服务的运行
上面介绍的ResourceManager的各个服务和组件均是通过中央异步调度器组织在一起的。不同的组件之间通过事件交互,从而实现了一个异步并行的高效系统。
4. 一些探索和实践
为了保障公司的计算资源得到充分利用,大数据平台这边统一使用YARN进行资源管理与调度。
目前在我们的YARN集群上主要支撑了绝大部分的离线应用(MapReduce、Hive SQL、Spark-Submit、Spark SQL)和一部分实时业务(Spark streaming、Flink)。
随着业务快速发展,为了满足业务的大量计算任务需求,我们对YARN集群做了一些探索和实践的工作。
目前集群的机器规模由开始的几百台发展到现在的5000+台、单集群最大节点数2500+、单集群管理的Container数10w+、单集群日调度Container数近1亿。
对YARN开展的一些稳定和优化工作,主要包括四个方面:
- 修复集群日常运维工作中发现的问题;
- 开发一些特性满足集群的安全和使用需求;
- 跟进社区合入相关patch对YARN的调度性能提升;
- 完善YARN集群的运维管理工作。
4.1对集群暴露的一些严重bug的修复:
4.1.1
YARN HA的实现一般是采用基于zookeeper的共享存储。YARN将共享存储系统抽象成RMStateStore,以保存恢复ResourceManager所必需的信息。但一些异常任务写zk的大小会超过znode的大小,从而会触发整个YARN服务的异常。
有一次我们收到线上YARN集群主备频繁切换的告警,随后业务反应大量的作业处于一直处于NEW_SAVING(等待提交状态),整个系统处于不可用状态。
因为当时我们只有一个集群,整个的影响面非常大,只能快速备份RM堆栈信息和进程日志,随后进行重启操作。重启集群后,发现集群恢复了一段时间,随后又出现了类似反复的情况,而每次出现的时间和频率都没有规律可循。
通过搜索日志错误关键字,在社区中找到相关的issue,基本可以判断是由于异常任务导致的,结合异常发生的频率可以推测出是非调度任务触发的。
随后我们在群里公告目前发现的问题,并紧急合入社区相关patch,后面集群没有再发现同样的异常。
我们随后查阅历史作业服务器,发现了类似的异常任务,task数量非常大,依赖的jar包非常多,RMStateStore持久化了这些jar包的引用关系,我们在测试集群也验证了这个问题。
所以我们要限制RM持久化RMStateStore写zk的大小,社区相关patch包括:YARN-2368 、YARN-3469、YARN-6125、YARN-5006。
4.1.2
mr任务申请内存和cpu超过yarn调度最大资源问题,特别是大任务的话会写大量错误诊断信息到zk上,影响整个集群其他任务的正常提交。
我们在任务提交的时候能够快速检测并失败这类任务,并减少打印错误诊断信息。
4.1.3
修复跨集群的一些问题:spark任务跨集群问题&mr任务跨集群问题:
因为我们的集群是由很多个HDFS集群组建成的一个大YARN集群,而我们每个小集群的defaultFS和客户端的defaultFS都是不同的。
这样默认会造成作业的提交目录无法被appmaster完全清除,长期运行后会导致单级目录文件数超过namenode的最大限制而导致无法提交新的任务。
4.2 特性开发
4.2.1YARN队列权限改造:
YARN队列权限是解决提交任务的用户或所属组是否有队列的提交权限,而用户和组映射关系的默认的实现类是ShellBasedUnixGroupsMapping。
它是获取ResourceManager本地机器的操作系统的用户和组关系。我们开发了权限插件,新增了自定义的用户和组映射关系实现类HeyTapGroupsMapping,对接我们内部的权限系统的用户和组关系;
4.2.2 限制用户通过rest api提交任务:
默认的情况下,YARN的8088端口未做相关认证,攻击者可通过rest api 提交任务,下载与集群网络相通的脚本执行异常攻击,很多公司都遭受过此攻击;我们的改动是在YARN的提交任务rest 接口处添加开关支持,一般我们的集群任务是通过授信的客户端提交,所以我们默认关闭REST API任务的提交。
相关安全漏洞链接:Hadoop Yarn REST API未授权漏洞利用挖矿分析
https://segmentfault.com/a/11...
4.2.3按队列限制单个mr任务的task总数量:
我们的集群之前会有很多的大的mr任务,这部分任务会浪费集群比较多的资源,并且容易触发很多集群bug问题。
针对mr任务支持按队列配置提交队列的任务最大task数量,超过直接拒绝任务提交。这部分的实现是在MR任务的AppMaster初始化作业的JobImpl类中对作业的信息进行检查。
4.2.4 YARN集群熔断措施:
集群难免会有异常的时候,当异常的时候往往会出现任务大量pending和Running的情况,系统内部大量消息处理堆积。
如果我们不做熔断处理,ResourceManager会由于事件长期堆积而超过JVM最大内存而oom崩溃。
我们的改动是当集群总的running任务数+accept任务数超过历史安全阈值则进行拒绝提交;按子队列熔断数量限制,考虑实际实施过程中,子队列一些任务偶尔也会因为集群内部原因(网络问题、HDFS抖动问题)发生阻塞,所以按子队列熔断会造成一些问题,暂时不上线。
而集群级别的熔断是很有必要的。集群的熔断实现是在RM处理客户端请求的ClientRMService组件里实现,对集群的调度器不会产生压力。
4.2.5动态拉取配置:
支持hadoop客户端、hiveserver、spark任务提交时,通过zk动态拉取配置文件,减少因配置文件改动的重启频率。
我们的改动是在hadoop-common包里增加读取远程zookeeper配置中心的功能类,hadoop-hdfs包里增加功能类的引用,客户端通过配置选择模式和对比本地和远程配置更新时间,来最终拉取合适的配置。
4.3 性能优化
4.3.1
我们打印收集ResourceManager的堆栈信息和阅读源码,发现YARN调度算法有两个可以优化的地方。
- 优化减少不必要的重复计算:
YARN在计算两个队列的优先级时,有多个地方需要计算队列的资源使用情况。而它每次都是重新递归计算所有子队列的资源,这个操作很耗cpu资源。
我们的修改是第一次直接计算,后面直接使用第一次的结果,减少了大约2/3的时间消耗。
- 优化选取排序的队列和任务集合:
YARN默认会选取所有队列和任务进行选择排序,我们剔除了不需要资源的队列和任务的排序,大大地减少了排序的规模。
4.3.2
集群节点的上的IO很大一部分来源于任务启动之前的jar包下载,默认是同一个任务的jar包才能共享,这样会导致很多公共jar包的重复下载问题。
在YARN中合理使用分布式缓存并设置公共资源,可以大幅减少不必要jar包的重复下载,减少集群IO。
4.3.3
YARN中涉及到的HDFS操作主要包括:
- RM对失败APP的处理;
- appMaster移动完成任务的作业执行记录信息;
- nodemanager上传聚合日志。
这些服务都是公共服务,一旦涉及到的HDFS集群卡了一下就会造成整个计算集群任务的堆积。因此我们逐渐在把这些公共服务拆迁到一个独立HDFS集群减少由于业务异常大IO任务的干扰。
4.4 运维管理方面
4.4.1 制定YARN集群使用规范并落地:
每个队列按照业务进行划分,子队列都有优先级划分,便于故障恢复。资源的合理使用,大多数队列都设置了资源的最小值和最大值。即尽量保障优先级队列能在集群高峰期拿到最小资源,且集群空闲时大多数任务能充分利用集群资源。每个队列都限制了运行app的最大数量,避免一个队列内部过多任务争抢资源发生死锁的情况和减少过多任务运行对集群的访问压力。
4.4.2 完善YARN集群各种监控告警指标:
通过我们的监控告警工具能提前发现和解决很多问题;主要包括:RM主备切换及可用性监控、慢任务、堵塞的队列、异常状态APP数量监控、YARN持久化zk状态目录监控、YARN事件堆积监控等。
4.4.3 参数配置一些比较重要的特性:
例如:YARN集群开启了cgroup对任务使用的cpu资源进行隔离、使用LinuxContainerExecutor限制用户对nodemanager的危险操作以及限制非法用户;
4.4.4 开发大任务监控平台:
通过我们的大任务监控平台能够定位集群中的异常大任务,可以协助业务进行任务的优化工作;
4.4.5 灾备计算集群建设:
任何一个系统都不是100%的可靠,如某一个YARN集群在异常情况下无法快速诊断问题并恢复的情况下。我们能通过内部平台的部署系统对提交任务的服务配置进行更新,并快速切换计算任务到其他正常的计算集群;
5. YARN的后续规划
- 我们目前单个YARN集群调度性能还有一些提升空间,后续会继续根据集群情况进行参数调优和合入社区相关的一些patch,对我们的YARN集群进行不断优化;
- 社区3.0以后的版本对YARN调度器做了比较大的调整,引入了Global Scheduling,性能测试报告显示单集群的调度性能得到极大的提升,我们后续也会做一些集群版本的升级测试;
- 社区的YARN Federation早期的版本有很多问题,现在已经逐步完善了。新的架构可以将多个独立的YARN集群组合成一个统一对外提供服务的计算集群服务,整个集群调度扩展能力和容灾能力会提高很多。我们也在调研和验证该方案在我们计算平台实施的可行性。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。