很多刚接触Hadoop生态的人,第一次看到YARN的介绍会被一堆术语劝退:ResourceManager、NodeManager、ApplicationMaster、Container……我当年啃《Hadoop权威指南》的YARN章节时也绕了不少弯子。后来我把YARN硬理解成三个字——调度台,整个框架一下子清晰了。这篇就是一份YARN入门笔记,目标是让一个只跑过MapReduce Demo的新手,也能真正看懂YARN在做什么,并且能在一台机器上把YARN拉起来,亲手跑通一个作业。
不管你之后是去碰Spark、Flink,还是继续深挖Hadoop本身,YARN都是绕不开的一层。它不是某个可有可无的组件,而是整个分布式计算资源的“操作系统内核”。理解了YARN,你看任何分布式计算框架的提交方式、资源申请、任务调度,都会觉得眼熟。
1. 从MapReduce 1.x的痛点到YARN的设计思路
1.1 老版本的JobTracker为什么撑不住
在YARN出现之前,Hadoop里的MapReduce计算框架是“包办一切”的。集群里有两个核心角色:JobTracker和TaskTracker。JobTracker负责接收作业请求、分配任务、监控任务进度、处理失败重试,同时还负责管理整个集群的资源;TaskTracker则老老实实在每台机器上执行Map和Reduce任务,定期向JobTracker汇报心跳。
听起来分工很明确,但问题也很明显:JobTracker把所有责任都揽在自己身上,几千个节点、几万个并发任务的心跳和作业状态全压在它一个人的肩上。集群规模一上来,JobTracker就成了一个巨大的单点瓶颈。你经常会遇到集群明明还有很多空闲节点,但新作业就是提交不进去,JobTracker的CPU被打满、内存持续告急。
更僵化的是,老版本的资源模型是“槽位(Slot)”,分为Map Slot和Reduce Slot两类。固定数量的Map Slot只能跑Map任务,Reduce Slot只能跑Reduce任务。这种硬编码的设计导致一个尴尬局面:如果Map任务很多但Reduce任务很少,Reduce Slot就会白白闲置,集群资源利用率上不去。而且槽位资源是死的,想让每个任务按需申请不同大小的资源,基本不可能。
还有一个让我印象很深的问题:老架构里槽位资源模型和MapReduce框架绑得太死,导致其他分布式计算框架根本没法跑在Hadoop集群上。你想在同一个集群里跑一套实时流计算、跑一套图计算?不行,集群被MapReduce框架锁死了。这种“计算框架与资源管理强耦合”的架构,天然限制了Hadoop生态的扩展。
1.2 YARN的核心思路:拆开“资源管理”和“作业调度”
YARN的全称是Yet Another Resource Negotiator,从名字就能看出来它的野心:我就是另一个资源协商者。它做的最核心的一件事,就是把老架构里JobTracker身上的两种职责拆开,变成两个独立的部分:
- 集群资源管理:交给ResourceManager(RM)和NodeManager(NM)负责,你不关心跑的是什么计算框架,只关心哪台机器有多少内存、多少核数可以分配。
- 作业生命周期的管理:交给每个作业自己的ApplicationMaster(AM)负责,每个作业(Job)提交后,都会有一个对应的AM去申请资源、跟踪任务执行、处理失败重试。
这个拆分极其关键。你可以用航空公司来类比:ResourceManager是机场塔台,它只管理空域里的飞机起降和跑道资源分配,不操心你到底从哪来、去哪送货;ApplicationMaster则是每个航班的机长,负责这趟航班的具体飞行计划、机组调度、应急处置。塔台只管资源,机长只管任务,两者各司其职。
1.3 为什么这种设计更优雅
把JobTracker拆成RM和AM,带来的直接收益是集群可以横向扩展。RM不再埋头追踪每个任务的执行进度,只需要维护节点资源视图和作业申请;AM则把复杂的任务调度、执行、重试分散到各个作业自己的进程里。集群规模从几百台扩展到几千台,RM的压力增长也远没有老JobTracker那么夸张。
计算框架的生态也被解放了。YARN上的资源模型不再是绑定Map/Reduce的Slot,而是抽象的Container(容器),里面描述的是内存大小和CPU核数。MapReduce能跑,Spark能跑,Flink能跑,甚至一些自研的计算框架也能通过实现AM协议接入YARN。你只需要一个共享集群,就能同时跑批处理和流处理任务,资源池统一调度,大幅提高了利用率。
对入门者来说,理解这一刻就够了:YARN不是替代MapReduce,而是把MapReduce从中解放出来,让它从“资源管理者”降级为“运行在YARN上的一个普通应用”。
2. 核心组件拆解与一次作业的完整旅程
2.1 ResourceManager:集群资源的“中央银行”
ResourceManager(RM)是集群里的全局资源仲裁者,运行在独立的主节点上。它内部主要包含两个核心组件:一个叫Scheduler(调度器),另一个叫ApplicationsManager(应用管理器)。
Scheduler是个纯调度模块,它不管某个作业的状态如何,不参与具体任务的重试和监控,它只做一件事:根据各个队列的资源配额和当前的空闲资源,决定把资源分配给哪些作业。ApplicationsManager则负责接收来自客户端的作业提交请求,为作业启动第一个Container来运行ApplicationMaster,并在作业结束后把这些元数据清理掉。
RM还有一个重要职责:接收所有NodeManager的心跳,根据心跳里汇报的资源使用情况,实时更新自己手里的集群资源视图。这个资源视图决定着后续能不能给新的申请分配Container。简单说,RM是“管钱的央行”,它只维护账本,不直接下场干活。
2.2 NodeManager:每台机器上的“驻场监理”
NodeManager(NM)是YARN在每台工作节点上的代理进程,相当于机器上的驻场监理。它负责启动Container、监控Container的资源使用情况,然后周期性地向RM汇报本机资源余量。
一个NM可以同时运行多个Container,每个Container有点像一个独立的“沙箱”进程。NM会盯着每个Container的内存和CPU使用是否符合申请值,一旦Container实际使用的内存超过了申请值,NM会毫不犹豫地把它“杀掉”,防止一个失控任务拖垮整台机器。这个机制就是后面排查问题时经常会遇到的Container被强杀现象的根源。
在实际环境里,一台机器上的总可用资源需要在yarn-site.xml里明确告诉NM。很多入门者会忽略这个配置,结果提交作业时发现资源总是不对,实际跑起来的任务永远只有一两个,多半是NM的资源配少了,给集群的总资源不够。
2.3 ApplicationMaster与Container:作业的“大脑”和资源的“货币”
ApplicationMaster(AM)是每个作业特有的组件,它是作业的大脑。作业提交后,RM会分配一个Container来运行AM进程。AM接下来会做几件事:向RM注册自己,然后根据作业输入数据的分片情况,计算需要多少个Map任务、多少个Reduce任务,再以Container的形式分批向RM申请资源;申请到资源后,AM登录到对应的NM上,在Container里启动任务进程;最后持续监控任务执行进度,处理失败重试,等所有任务完成后向RM注销自己。
Container是YARN里的资源抽象单位,它并不等于Docker容器,虽然名字很像。一个Container其实就是一组资源描述:比如2GB内存、1个CPU核,对应到实际物理形态就是一个独立的JVM进程。你可以把它理解为“资源货币”,AM拿到的每一份Container,都是一笔实实在在的机器资源额度。
2.4 一个WordCount作业从提交到完成,到底走了哪些路
我把经典WordCount作业的提交流程拆成八个步骤,新手照着这个顺序理解,很多疑惑会迎刃而解:
- 客户端调用YarnClient向RM提交作业,带上作业配置、Jar包路径和输入分片信息。
- RM收到请求后做一些基础校验,比如用户权限、队列是否存在,然后给作业分配一个Application ID,并返回一个新作业提交地址。
- 客户端把作业的Jar包、配置文件、输入分片元数据上传到一个HDFS临时目录中,然后通知RM“作业文件已经放好了,可以开始”。
- RM的ApplicationsManager在某个满足资源条件的工作节点上,启动一个Container,在里面拉起ApplicationMaster进程。
- AM启动成功后,第一时间向RM注册自己,并建立心跳连接,从这一刻起,作业的“大脑”正式上线。
- AM从HDFS上读取输入分片信息,计算出需要的Map任务数量,然后以Container的形式向RM申请资源。
- RM通过Scheduler决定何时、在哪台NM上给AM分配Container。AM在拿到Container后,远程登录对应NM,在这些Container里启动MapTask,之后启动ReduceTask。
- 所有任务执行完毕后,AM向RM注销自己并释放所有Container,同时把执行结果写入最终目录,客户端通过轮询RM拿到作业成功状态。
这一步一步看下来你会发现,最耗时的不是任务执行本身,而是从作业提交到AM启动、再到资源申请分配的这个过程。这也是为什么你在YARN界面里经常看到作业状态是ACCEPTED,那表示作业已经被接受,但AM或任务资源还没完全到位。
3. 调度器怎么选:FIFO、Capacity与Fair
3.1 三种调度器的本质差异
YARN的Scheduler模块有三种主流实现,它们解决的问题各不相同。很多入门者会问“哪个调度器最好”,我的答案永远是:取决于你的使用场景。
FIFO Scheduler是最简单的一种调度器,同一个队列里作业按照“先进先出”原则排队执行。它的缺点特别明显:队头的作业如果是一个耗时很长的MapReduce大任务,后面所有的小作业都必须干等。在小集群做测试的时候FIFO挺方便,因为它完全不用配置;一旦共享集群里有多个团队使用,这种调度器就很不合适。
Capacity Scheduler是很多企业生产集群的默认选择。它的思路是把集群资源划分成多个队列,每个队列有独立的容量配额,比如data团队用60%资源、default队列用40%资源。队列内部的作业按FIFO规则排队。关键是,当某个队列空闲时,它的空闲资源可以被其他队列借用来跑作业,一旦原队列有新作业需求,借出去的资源会逐步归还。这种设计既保证了多团队之间的资源隔离,又不至于让某个队列的资源白白浪费。
Fair Scheduler听名字就知道走的是“公平路线”。它能动态地在多个运行中的作业之间平分资源,保证所有作业都能得到一个相对公平的资源份额。和Capacity的静态队列配额不同,Fair Scheduler会实时感知正在运行的作业数量,每个作业的资源份额会随着其他作业提交和结束而变化。它还支持抢占,当一个低优先级作业占用资源太久时,更高优先级的作业可以把资源抢回来。
3.2 配置示例与选型建议
Capacity Scheduler配置是在capacity-scheduler.xml里指定队列的资源配置,核心配置大致如下:
<configuration> <property> <name>yarn.scheduler.capacity.root.queues</name> <value>data,default</value> </property> <property> <name>yarn.scheduler.capacity.root.data.capacity</name> <value>60</value> </property> <property> <name>yarn.scheduler.capacity.root.default.capacity</name> <value>40</value> </property> </configuration>Fair Scheduler的配置则是另一种风格,更像一个自定义的规则文件,指定各队列的权重和容量:
<allocations> <defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy> <queue name="root"> <queue name="data"> <weight>2</weight> </queue> <queue name="default"> <weight>1</weight> </queue> </queue> </allocations>选型建议其实很直白:
- 单人或测试集群,作业很少,用FIFO最快,零配置。
- 多团队共享生产集群,作业周期性强,又要保证不同业务不被互相影响,用Capacity。
- 作业类型繁杂、交互式分析多、希望资源在作业间尽量公平流淌,用Fair,它对动态负载更友好。
我见过不少团队在生产环境里用Fair Scheduler的抢占机制来保证交互式查询的响应速度,也见过很多公司用Capacity把离线和在线业务隔离开。选择调度器没有绝对标准,关键是先想清楚你的集群是“独享”还是“共享”。
4. 实操入门:一台机器也能把YARN完整跑起来
4.1 环境准备与版本选择
入门YARN最省钱的方案就是在一台Linux机器上以伪分布式模式跑起来。所谓伪分布式,就是所有Hadoop角色都跑在同一台机器上,虽然没法体会多节点分布式调度,但足够把YARN的组件和作业提交流程完整过一遍。
你需要准备两样基础环境:JDK和Hadoop二进制包。JDK版本建议8或11,Hadoop建议用稳定的3.x版本。把Hadoop压缩包解压后,配置好HADOOP_HOME环境变量,并把$HADOOP_HOME/bin加到PATH里。第一步先在jps命令里能看到基本的进程,后续才有意义。
在开始前有个很容易踩的坑:伪分布式模式下,HDFS和YARN是两套独立系统,YARN作业的输入数据通常存放在HDFS上。所以启动YARN之前,必须先确认HDFS已经在运行。很多新手只启动YARN不启动HDFS,结果作业提交后一直报找不到输入路径,让人头大。
4.2 核心配置文件与参数解读
YARN相关的核心配置文件主要有四个:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml。对入门来说,yarn-site.xml配好以下几个参数就够跑了。
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>3072</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>2048</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>512</value> </property> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> </configuration>其中yarn.nodemanager.resource.memory-mb是单台NM可分配给Container的总内存。如果你的机器物理内存是4GB,操作系统和Hadoop其他进程要预留约1GB,那么给YARN的可用内存设成3072MB是相对稳妥的。yarn.scheduler.maximum-allocation-mb决定了单个Container最多能申请到的内存,默认值是8192,但我建议在入门机器上调小一点。yarn.nodemanager.vmem-check-enabled是个入门阶段经常需要关掉的开关,NM默认会做虚拟内存检查,本机Container的虚拟内存使用稍微高一点,就会被NM直接杀掉,入门期关掉能减少很多无谓的干扰。
mapred-site.xml里还需要配置作业历史服务器的地址,否则MapReduce作业完成后你很难在界面上看到作业的历史详情:
<configuration> <property> <name>mapreduce.jobhistory.address</name> <value>localhost:10020</value> </property> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration>这行mapreduce.framework.name很容易被忽略,它的作用是明确告诉MapReduce:作业运行框架走YARN,而不是本地模式。
4.3 启动集群并验证进程
在配置好上述文件后,启动顺序有讲究。先格式化HDFS的NameNode,生成元数据,然后依次执行:
hdfs namenode -format start-dfs.sh start-yarn.sh执行完后用jps查看进程,正常情况下你应该能看到以下进程:NameNode、DataNode、ResourceManager、NodeManager。如果还启动了作业历史服务器,还会有JobHistoryServer进程。
接着打开YARN的资源管理界面,默认端口是8088。在浏览器里访问,你会看到一个包含集群节点列表、作业列表、队列状态的页面。首页右上角的Active Nodes显示当前可用节点数。如果你配置正确,这里应该显示1个节点,并且可用内存和核数的数字应该和你配的yarn.nodemanager.resource.memory-mb对应。
4.4 提交一个MapReduce作业并观察全流程
为了验证YARN真的能跑,我习惯用Hadoop自带的示例程序做一个简单的字符统计作业。先在HDFS里建一个目录,放进去两个小文本文件:
hdfs dfs -mkdir -p /input hdfs dfs -put *.txt /input/ hdfs dfs -ls /input/然后提交示例作业:
yarn jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar wordcount /input /output命令执行后,终端会打印作业ID和一个跟踪URL。这时候切换到YARN的8088界面,你会看到作业状态从ACCEPTED变成RUNNING,再变成SUCCEEDED。点进作业详情,能看到运行中的ApplicationMaster、各个Map和Reduce任务的Container分布情况。
这个过程的观赏性其实很强。我第一次完整跑通的时候,明白了前面说的“AM申请资源”到底是怎么回事:作业启动后,YARN先给AM分配一个Container,然后AM自己向调度器申请更多的Container来跑Map和Reduce任务。你在界面上能看到每个Container所在的主机IP、内存大小、开始时间,这些信息对排查和调优都非常有用。
5. 常见问题与排查技巧实录
5.1 Container被强杀:物理内存还是虚拟内存超标
最典型的现象是作业跑着跑着,某个任务反复失败,日志里出现“Container killed on request”,或者“Physical memory usage exceeds 1.0x of limit”一类的字样。
这个问题十有八九是资源相关参数没配好。可能是容器申请的内存小于任务实际需要的内存,也可能是虚拟内存检查没过。我的处理逻辑是:先查看对应任务的日志,确认是物理内存还是虚拟内存超限;如果是物理内存超限,就把mapreduce.map.memory.mb或mapreduce.reduce.memory.mb调大一点;如果是虚拟内存超限,且机器上任务确实需要较多虚拟内存,可以把yarn.nodemanager.vmem-check-enabled设为false。
还要检查一下yarn.scheduler.maximum-allocation-mb和yarn.scheduler.minimum-allocation-mb的配置。如果最大分配值小于任务申请的内存,AM无论怎么申请都会被拒绝。这个检查顺序很有用:先看任务申请了多少,再看Container最大能分多少,最后看NM有没有足够剩余资源。
5.2 作业一直卡在ACCEPTED状态
作业提交后,界面显示ACCEPTED,但等很久都没有进入RUNNING,这是最折磨人的问题之一。ACCEPTED表示RM已经接受了作业,但AM的Container还没有被调度出来。
根本原因通常是资源不足。你配的yarn.nodemanager.resource.memory-mb可能太小,调度器最大分配值也偏小,导致即使有节点也无法满足AM启动所需的资源。排查时先看8088界面上的节点资源情况,看Active Nodes下面显示的总内存、可用内存是不是都为零。如果可用内存充足仍然不调度,再去看调度器配置里有没有队列容量限制,作业被提交到了容量为零的队列里,资源永远卡死。
5.3 只有一台节点但显示资源为0
这个问题几乎每个入门者都遇到过。你在界面上看到Active Nodes为0,或者只有节点但可用内存是0,作业提交后一直等不到资源。
典型的配置错误是:配置文件里指定了yarn.resourcemanager.hostname,但RM的Web UI访问的却是别的主机名;或者NM的本地IP和RM配置的主机名不一致,导致NM心跳无法注册成功。还有一个隐蔽原因:yarn.nodemanager.resource.memory-mb被设成了很大值,但机器实际内存不够,NM起不来或者起来后就被系统杀掉。
遇到这种问题,先看ResourceManager的日志,再看NodeManager的日志,重点搜“registered”“heartbeat”和“resource to be registered”相关关键词。NM启动后第一件事是向RM注册并汇报本机总资源,如果注册失败,日志里会有明确提示。
5.4 伪分布式常见坑:忘了启动HDFS或历史服务器
还有一类问题“不像问题”,但也容易卡住新手:
- 只启动了YARN没启动HDFS,提交作业时报输入路径不存在。
- 作业跑完了,想在命令行看日志却没有历史日志,因为JobHistoryServer没有启动。
- 格式化NameNode之后又对集群配置反复修改,导致元数据不一致,启动异常。
这些坑的共性是流程依赖没理清。YARN作业依赖HDFS存储输入数据和中间结果,作业历史服务器负责把已经完成作业的任务级日志保存下来。建议每次学习前按固定顺序执行:先HDFS,再YARN,再HistoryServer,用jps确认进程齐全后再提作业,能省掉很多重复排查的时间。
5.5 排查问题时我自己的日志阅读顺序
YARN的日志分布在多个地方,新手容易懵。我的习惯是遇到作业失败时,按以下顺序找日志:
- 先在8088界面找到作业详情页,看“Diagnostics”列给出的标准报错。
- 再点进对应失败的Task页面,里面有Container对应的日志链接。
- 最后去NodeManager所在机器的$HADOOP_HOME/logs目录下,翻container开头的日志目录,直接看syslog。
大部分情况下,第二步里的任务级日志就足以定位问题了。ResourceManager和NodeManager自己的日志通常用来排查框架层面的问题,比如节点掉线、心跳中断、调度异常,这两类日志关键字差异明显,不要混着看。
6. 看完YARN之后,我建议你再往前走两步
如果你已经能在一台机器上跑通YARN,理解了一个作业从提交流程到Container调度的全部环节,接下来可以再走两步巩固。
第一步是把调度器从默认配置切换成Fair Scheduler,在提交多个作业时观察资源如何被动态调整。你会发现,当第二个作业提交时,第一个作业的资源份额会被自动让出一部分,这就是“公平调度”的直观体现。第二步是去看Capacity Scheduler的官方XML,试着在里面配两个队列,然后通过mapreduce.job.queuename参数把不同作业提交到不同队列,观察两个队列的资源隔离如何生效。
我个人在实际操作中的体会是:YARN入门最大的门槛不是技术本身,而是被庞杂的术语吓住。当你理解RM、NM、AM、Container四者的关系后,再回头翻《Hadoop权威指南》里YARN章节,会发现所有细节都能串起来。资源申请与释放就是YARN的主线,“申请—分配—运行—释放”循环就是它工作的全部秘密。把这条主线刻在脑子里,你后续看任何分布式计算框架,都会比别人多一分底气。