说一个大多数人刚接触Hadoop时都会被绕晕、但集群一旦跑不顺十有八九跟它有关的东西——YARN。我第一次搭三节点集群的时候,以为HDFS能正常读写数据了,MapReduce作业也应该顺手就能跑起来。结果提交一个小作业,等了五分钟还显示ACCEPTED,翻到ResourceManager的Web界面一看,队列可用内存是0,容器一直起不来。那时候我才意识到,真正卡住我的不是MapReduce代码,而是最底层的资源管理没有配好。
如果你拆开看整个Hadoop技术栈,YARN是最容易被忽略、又最绕不开的一层。它负责统一管理和调度整个集群的CPU、内存、磁盘等资源,让MapReduce、Spark、Flink这些计算框架按需申请资源、并发运行。说白了,HDFS解决“数据放哪里”,YARN解决“算力怎么分”,这两者配合到位,上层计算引擎才有发挥空间。这篇文章不打算罗列网上到处都能搜到的“YARN三大组件分别是什么”这类概念,而是从一线运维和调优视角,把YARN的架构逻辑、调度器选型、关键参数、实际配置和排错经验完整梳理一遍。不管你是刚搭完伪分布式集群的新手,还是正在为集群资源利用率发愁的运维,这套思路都可以直接参考。
1. 为什么说YARN是大数据集群的“中枢神经”
1.1 从第一代MapReduce说起:资源管理为什么会被单独拆出来
在YARN诞生之前,Hadoop跑的是第一代MapReduce架构。那时候所有作业调度和资源分配都集中在JobTracker上,而实际执行任务的TaskTracker分散在各个节点。JobTracker一个人要干好几份活:既要做资源管理,又要做作业调度,还要管任务失败重试、进度监控和状态上报。集群规模一上来,JobTracker就成了绝对瓶颈——单点故障、内存溢出、调度延迟高,一个几千个Map任务的大作业能把整个集群拖到濒临崩溃。
更麻烦的是,这种设计把资源管理和MapReduce计算引擎强耦合在一起。你想让Spark在同样的HDFS数据上跑,要么重新搭一套集群,要么跟MapReduce排队抢资源,共享程度非常差。所以社区后来下了决心,干脆把资源管理这层从计算框架里抽出来,做成一个独立的通用资源调度层,这就是YARN诞生的直接背景。它解决的问题很本质:让“资源分配”和“具体计算”解耦,让多种计算框架共用同一批物理资源。
1.2 一个作业在YARN上是怎么走完全程的
我先用最直白的方式描述一下整个流程。你提交一个MapReduce作业(或者说任何兼容YARN的应用),实际上是提交给ResourceManager,RM会找一台NodeManager启动一个ApplicationMaster,这个AM就是你这个作业的“项目经理”。它负责向RM申请资源、拿到资源后通知对应的NodeManager启动Container,然后真正的计算任务在Container里跑。等任务全部完成,AM再向RM注销,所有资源释放回集群。
从资源管理角度看,整个生命周期最关键的有三个阶段:客户端向RM申请启动AM、AM向RM请求后续运行所需的资源、任务运行期间NM和RM的心跳汇报。每一步都涉及内存和CPU的动态分配,任何一处参数不匹配,表现出的症状就是作业卡住、容器反复被杀、节点失联。这也是为什么很多人调业务代码调了半天没效果,最后发现是YARN资源参数的问题。
1.3 YARN的价值不只是“资源够不够”,更是“多框架共存”
我见过不少刚入行的同学,以为YARN就是给MapReduce提供内存和CPU的东西。其实它最大的意义在于让计算框架和资源管理彻底解耦。同一个Hadoop集群上,你可以同时跑MapReduce做批处理、Spark跑SQL、Flink跑实时流,各算各的互不干扰,靠的就是YARN统一的资源抽象。这也是大数据面试题里经常问“YARN和MapReduce是什么关系”的原因——很多人只记得Hadoop等于HDFS加MapReduce,却忽略了中间还夹着一层更底层的资源调度系统。
2. 认识YARN架构里的四个关键角色
2.1 ResourceManager:管全局的“总调度台”
ResourceManager,通常简称RM,是整个YARN集群的决策层。它接收客户端的作业提交请求、维护整个集群的资源账本、决定哪个作业拿多少资源、让哪个节点启动Container。RM本身不直接执行计算任务,它的职责就是“分配”和“拍板”。你可以把它理解成机场塔台,所有飞机的起飞降落都得听它指挥,但塔台自己不开飞机。
生产环境里RM必须做高可用。最常见的方案是Active/Standby双节点部署,让ZooKeeper帮忙做自动故障切换。如果你只在伪分布式环境里做实验,不配RM高可用,那RM宕机时整个集群所有作业都会中断,只能等它恢复。这也是为什么在真实的高可用Hadoop集群里,ZooKeeper不只是给HDFS的NameNode做HA用的,它同时也在给YARN的RM值守。做集群整合的时候,把RM和NameNode放在同一批ZK节点上管理,是最常见的做法。
2.2 NodeManager:每个节点上的“资源管家”
NodeManager(NM)跟数据节点绑定,每台机器跑一个。它负责启动和管理本机上的Container,定时向RM主动汇报节点资源状态,比如还剩多少内存、几个vcore、正在跑几个容器。RM做调度决策时,会优先选择资源充足的节点。NM相当于每个仓库分站点的主管,知道自己这有多少货架、多少货物,定期向总部汇报。
有一点我要特别提醒新手:NodeManager只是个“管家”,它不会替你做调度决策,也不关心你跑的是MapReduce还是Spark。它只看容器的事:启动、监控、回收。如果你某天发现某个节点任务总是失败,先去排查这台机器的NM日志和本地目录磁盘占用,大概率是磁盘满了或者资源被系统进程吃掉。
2.3 ApplicationMaster:每个作业的“项目经理”
AM是YARN架构里最容易被忽略、但又非常重要的角色。每个提交到YARN上的应用都会有一个属于自己的AM,MapReduce作业有MapReduce AM,Spark on YARN会启动Spark AM。AM的作用是:向RM申请Container资源、在这些Container上调度执行任务、跟踪任务状态、失败时重新申请资源和重试。
理解了AM的存在,你就知道为什么YARN能同时支撑多种计算框架。RM只负责给AM分配资源,至于AM拿到资源后去跑什么任务是MapReduce的Mapper还是Spark的Executor,RM根本不关心。这种“总调度台”加“项目经理”的协作模式,让YARN天然支持多框架共存。如果你面试时被问到“YARN是如何支持多种计算框架的”,核心回答点就在这里。
2.4 Container:最小资源单元到底是什么
Container是YARN分配资源的最小单元,它抽象了内存、CPU、磁盘等资源,同一个节点上可以同时跑多个Container。Container的大小由RM配置决定,比如最小分配1GB内存加1个vcore,最大可以到几十GB。从实现层面看,Container不是一台独立的虚拟机,而是NodeManager进程内部的一个资源边界,NodeManager通过控制进程和cgroup来约束它真正能使用多少CPU和内存。
3. 三种调度器横评:FIFO、Capacity、Fair
3.1 先看核心差异与适用场景
YARN内置三种资源调度器:FIFO、Capacity Scheduler、Fair Scheduler。它们的核心差异在于“多个作业或队列抢资源时,谁先谁后、能不能弹性扩容”。
FIFO最简单粗暴,严格按提交顺序先进先出,前面的大作业不跑完,后面的作业只能一直等待。好处是思路清晰、实现简单,坏处是资源利用率低,不适合多租户、多作业共存的场景。我只在本地做单作业测试时用过FIFO,生产环境基本不会选它。
Capacity Scheduler是生产环境默认配置,也是大多数公司的选择。它把集群资源按比例切分成多个队列,每个队列内部可以再切子队列,队列之间互不抢占。比如default队列分60%、BI队列分30%、ad_hoc队列分10%,每个队列有独立的资源上限和用户权限,从机制上杜绝了一个作业吃光整个集群资源的极端情况。
Fair Scheduler讲究“资源公平”:不需要提前预留队列,系统根据当前运行的作业数动态计算每个作业能分到多少资源,跑得慢的作业可以让出资源给跑得快的作业。适合用户多、作业类型杂、期望资源动态平衡的团队。
3.2 三种调度器的对比速查表
| 对比维度 | FIFO | Capacity Scheduler | Fair Scheduler |
|---|---|---|---|
| 调度模型 | 先来先服务 | 队列配额、层级 | 按作业动态公平分配 |
| 是否支持多队列 | 否 | 是 | 是 |
| 多租户隔离 | 弱 | 强 | 中 |
| 资源弹性 | 无 | 有,支持maximum-capacity | 有 |
| 适用场景 | 测试、单作业 | 生产默认、多业务线 | 多用户共享、动态负载 |
| 配置复杂度 | 最低 | 中等 | 中等 |
3.3 生产环境为什么默认选Capacity
从我自己的实操经验来看,绝大多数公司选Capacity并不是因为它的功能最多,而是因为“队列资源隔离”这个特性太适合企业内部多团队共用一套集群的场景。数据平台给算法团队、数据仓库团队、报表团队各建一个队列,分别设定资源比例,再通过队列访问控制列表把用户分开。这样就算某个团队突然提交一堆大作业,也不会把其他团队的资源全部抢走,调度层面的“交通事故”能少很多。
Fair Scheduler在资源公平性上有优势,但它的动态分配机制在多队列配额控制上不够直观,很多运维人员习惯用Capacity那种“明确百分比”的配置方式。两种调度器都能用,但如果你接手的是别人搭好的集群,建议先确认当前配置,别轻易切换,因为切换调度器会导致集群重启,所有运行中作业全部中断。
4. 集群当中最关键的配置项与计算逻辑
4.1 内存和CPU从物理机到Container的换算
这部分我直接上公式和例子。假设你有三台128GB内存、32核CPU的物理机。注意,不是所有内存都能给YARN,操作系统、HDFS DataNode、系统监控进程都要占用一部分。常见经验是给操作系统和non-YARN进程留25%到30%,剩下大约70%到75%给YARN NodeManager。
按这个比例算:
- yarn.nodemanager.resource.memory-mb = 128 * 1024 * 0.7,算出来大约91750MB,实践中我习惯取整到92160MB,差不多90GB。
- yarn.nodemanager.resource.cpu-vcores = 32 * 0.8,约25个vcore。
然后确定单个Container的最小和最大范围:
- yarn.scheduler.minimum-allocation-mb = 1024
- yarn.scheduler.maximum-allocation-mb = 8192,或根据大作业实际需要再调大
- yarn.scheduler.minimum-allocation-vcores = 1
- yarn.scheduler.maximum-allocation-vcores = 8
这里有一个关键点:RM在给容器分配资源时,内存会按minimum-allocation的整数倍向上取整。比如任务申请1500MB,RM实际分配时可能按2048MB计算。所以你会发现明明配置了Spark执行器内存是2GB,实际容器占用的资源可能比这还多一点。
4.2 一张表看懂yarn-site.xml里最值得调的参数
| 参数 | 默认值 | 推荐调整说明 |
|---|---|---|
| yarn.scheduler.class | CapacityScheduler | 生产环境显式配置,避免环境差异导致默认值变化 |
| yarn.nodemanager.resource.memory-mb | 8192 | 必须改成物理机实际可分配给YARN的内存 |
| yarn.nodemanager.resource.cpu-vcores | 8 | 按物理核数乘以0.75到0.85计算 |
| yarn.scheduler.minimum-allocation-mb | 1024 | 资源碎片最小粒度,不需要频繁改 |
| yarn.scheduler.maximum-allocation-mb | 8192 | 如果任务容器内存需求大,适当调大 |
| yarn.nodemanager.pmem-check-enabled | true | 容器频繁被杀时可临时关闭验证,不建议长期关 |
| yarn.nodemanager.vmem-check-enabled | true | 虚拟内存超限是常见杀手,谨慎处理 |
| yarn.log-aggregation-enable | false | 开启后日志集中聚合,排错会方便很多 |
4.3 为什么内存分配比例不同会导致任务失败
写配置的时候,最常见的坑就是MapReduce任务的内存参数和YARN容器内存参数不匹配。比如给某个Map任务申请了2048MB内存(mapreduce.map.memory.mb=2048),但YARN单个Container最大只允许1024MB,那任务会直接提交失败,报错类似“Resource Request exceeds maximum allowed allocation”。反过来,如果最大Container内存调得非常大,但MapReduce任务声明的内存又太小,会造成资源浪费,一个10节点的集群可能只能同时跑几个任务,看起来利用率极低。
这个问题的核心矛盾是“容器资源”和“任务实际需要资源”之间的对齐。调优的经验法则是:先定容器范围,再设置MapReduce或Spark的执行内存,保证所有申请值都能被YARN的容器资源范围覆盖,同时给JVM堆外内存留出余量。比如任务实际需要2GB堆内存,申请容器时最好给到2.5GB或3GB。
4.4 capacity-scheduler.xml多队列配置实例
下面是一份我在测试集群用过的配置片段,逻辑就是建两个业务队列,一个default跑日常批处理,一个bi给报表任务用,两个队列之间用maximum-capacity限制弹性上限。
<property> <name>yarn.scheduler.capacity.root.queues</name> <value>default,bi</value> </property> <property> <name>yarn.scheduler.capacity.root.default.capacity</name> <value>70</value> </property> <property> <name>yarn.scheduler.capacity.root.bi.capacity</name> <value>30</value> </property> <property> <name>yarn.scheduler.capacity.root.bi.maximum-capacity</name> <value>60</value> </property>这里的capacity是权重不是绝对内存,两个队列加起来是100。maximum-capacity等于60表示当default队列没有任务时,bi队列最多可以借用到60%的集群资源,但不能超过这个上限。这样既做到基础隔离,又保留弹性,是很多公司实际采用的“配额加弹性”策略。
5. 实操过程与核心环节实现
5.1 三节点测试集群的搭建要点
搭建一套能验证YARN行为的集群,至少需要1台master跑RM,2台worker跑NM。性能要求不用太高,但有几个点必须注意:所有节点时间要同步,建议用NTP或chrony;主机名和/etc/hosts要一致;master要能SSH免密登录到所有worker;JDK版本和Hadoop版本要匹配,尽量选官方支持的LTS版本。
配置方面,把core-site.xml里的fs.defaultFS指向HDFS的NameNode,yarn-site.xml里把yarn.resourcemanager.hostname指向master,然后整个配置目录完整分发到所有节点。记得在hadoop-env.sh、yarn-env.sh里设置好JAVA_HOME。我第一次搭建时漏了yarn-env.sh里的JAVA_HOME,结果启动YARN时直接报找不到Java环境,排查了半天才发现是环境变量没生效。这种小坑在集群初始化阶段特别常见,建议所有配置文件修改后都用grep确认一遍关键项。
5.2 启动确认YARN状态
首次启动先用start-dfs.sh启动HDFS,再执行start-yarn.sh启动YARN。启动完用jps查看进程,master节点应该有ResourceManager,worker节点应该有NodeManager。接着可以用一组命令快速确认集群状态:
yarn node -list yarn application -list yarn top如果想用脚本或程序去管理YARN任务,最方便的方式不是解析命令行输出,而是直接调YARN的REST API。比如用Python访问http://rm-host:8088/ws/v1/cluster/apps就能拿到所有应用的JSON数据,拿到指定应用状态就拼上applicationId。下面是一段我在自动化运维脚本里用过的Python调用片段:
import requests rm_host = "master01" apps_url = f"http://{rm_host}:8088/ws/v1/cluster/apps" resp = requests.get(apps_url) data = resp.json() for app in data.get("apps", {}).get("app", []): print( app.get("id"), app.get("name"), app.get("state"), app.get("queue") )这种方式比shell命令更容易集成到监控平台里,而且REST API返回的信息更全,比如应用提交时间、内存秒数、CPU秒数等。你在设计自动化运维系统时,这一层接口非常值得提前接入。
5.3 通过实际队列看资源分配
配置好队列后,提交一个测试MapReduce作业到不同队列观察效果。用hadoop jar命令加参数-Dmapreduce.job.queuename=bi指定队列。提交后打开RM的8088端口Web界面,切到Scheduler页面,可以看到每个队列的运行中应用数、容器数、活跃内存情况。
按照常见经验,如果bi队列下缓存了大量任务,但default队列没任务,资源会自动允许bi队列用到maximum-capacity上限。但如果你发现一个队列里积压了一堆任务却总是拿不到资源,优先检查是不是队列的maximum-capacity或用户限制设得太小。这里有个细节:用户在某个队列下的最大资源占用比例,由yarn.scheduler.capacity.root. .user-limit-factor控制,默认是1,表示单个用户最多用到队列资源的一定比例,多用户共享时特别值得关注。
6. 常见问题与排查技巧实录
6.1 任务一直等待、一直ACCEPTED:八成是资源不足
作业提交到YARN后一直显示ACCEPTED,既不运行也不报错,这种情况我遇到最多。首先要看RM Web界面的Scheduler页面,确认目标队列的可用内存。如果可用内存为0或很小,说明要么是其他任务占了资源,要么是队列容量设置太小。也可以用命令:
yarn application -list -appStates ACCEPTED把所有等待中的应用列出来,看谁在占用资源。这里有一个隐蔽的细节:很多集群配置了延迟调度,资源不足时会等待一段时间再触发,所以看起来卡住不一定是故障。等一两分钟再观察,如果还一直ACCEPTED,再从资源配额上找原因。
6.2 NodeManager失联:优先查磁盘和心跳
生产上最常见的NodeManager故障是节点“挂了”,在RM页面能看到RUNNING节点数减少。排查顺序很重要:先登录对应节点看磁盘,NM本地目录满了会写不了状态,直接导致失联;再看NM日志,重点找“Unexpected error”或“Connection refused”;然后确认时间同步,NM和RM时间差太大会导致心跳不合法;如果NM内存资源被系统杀掉,还得看dmesg。
我自己处理过一次NM反复挂掉的事件,最后发现是NM本地目录在/tmp下面,/tmp被系统清理服务清空,NM状态文件被删了。恢复方式就是清理残留后换一个稳定目录,比如/data/yarn/nm-local-dir,再从配置层面绑定到非临时目录。这类问题在虚拟机和容器环境里特别容易出现,配置NM本地目录时一定要选持久化路径。
6.3 容器被杀、OOM、虚拟内存超限
容器启动后不久就被杀,是最让人头疼的。常见原因有三个:物理内存超过容器上限,YARN通过监控会主动kill掉超限容器;虚拟内存超限,有些任务比如JVM、Python的虚拟内存占用很大,导致vmem检查判死;系统层面物理内存不足,NM进程被系统OOM Killer杀掉。
排查时先看container相关日志,再检查yarn-site.xml里vmem-check-enabled开关。我的经验是:如果确认任务本身没问题,可以先临时把vmem-check-enabled设为false验证,但不要长期关闭。真正要做的还是把任务的堆内存和JVM参数调好,让申请值比实际使用量有20%到30%的余量。比如给Map任务申请3GB堆内存,JVM参数-xmx最好设成2GB到2.5GB,剩余空间留给元空间和堆外内存。
6.4 新手避坑速查表
| 现象 | 大概率原因 | 快速对策 |
|---|---|---|
| 作业一直ACCEPTED | 队列资源不足 | 查看Scheduler页面配额占用情况 |
| 容器反复被杀 | 内存参数不匹配 | 检查MapReduce或Spark内存与容器范围对齐 |
| NM节点失联 | 磁盘满或状态目录被清 | 清理磁盘、换稳定NM本地目录 |
| 报错超过max allocation | 任务申请内存超过最大容器 | 调大scheduler.maximum-allocation-mb |
| 提交任务失败提示队列不存在 | 未指定队列名 | 用-Dmapreduce.job.queuename指定队列 |
| 虚拟内存oom | vmem检查误杀 | 先验证再永久调整vmem-check-enabled |
7. 从资源管理角度回看整个大数据集群
7.1 排查问题时先看资源,再想代码
我个人遇到大数据任务失败时,排查顺序永远是“资源层优先”。如果YARN上的容器都起不来,那就先别去一行行读业务代码错不错误。先确认队列空闲、内存充足、节点存活,再进集群容器看日志。否则很容易绕一大圈,最后才发现只是资源配置没对齐。
7.2 日常巡检最该盯的几个指标
日常维护中,我会重点盯这几个指标:RM Web界面里的集群可用内存与总内存;是否有长时间ACCEPTED的应用;NodeManager数量与实际节点数是否一致;每个队列的利用率是否长期超过90%或长期低于10%。如果某个队列长期占用90%以上,要么给任务瘦身,要么调整队列容量。如果长期低于10%,说明资源分配策略不合理。另外,如果做了HDFS节点扩容,记得同步检查新节点的NM资源配置,否则数据量上去了,计算资源反而跟不上。
7.3 最后分享一个土办法
有一次BI队列的任务总是凌晨堆积,查了半天发现不是资源问题,而是作业提交时间集中在整点,把队列瞬时打满了。解决办法很简单:在调度层给不同业务的定时任务错峰,并给关键任务设置更高优先级。做资源管理不可能完全靠调参数,业务侧的节奏和管理侧的习惯同样重要。这也是我后来给别人做集群资源评估时,一定会先看任务时间分布的原因——很多“资源不够”的表象,背后其实是“提交节奏不合理”。