YARN架构本质:资源抽象、调度解耦与容器化治理
2026/6/8 21:29:22 网站建设 项目流程

1. 项目概述:YARN不是“资源调度器”那么简单,它是Hadoop生态的中枢神经系统

很多人第一次看到“Hadoop YARN Architecture”这个标题,下意识会把它当成一个技术文档里的标准章节——翻两页、记几个组件名、背下ResourceManager和NodeManager的职责,考试或面试时能答出来就算过关。但我在一线支撑过27个PB级离线数仓、参与过6次跨版本YARN集群升级、亲手调优过3000+节点的混部集群后,越来越确信:YARN从来就不是一套孤立的资源调度框架,而是整个Hadoop生态得以规模化、工业化运行的底层操作系统。它解决的不是“任务怎么跑”的问题,而是“当上千个作业、上万个Container、几十种计算引擎(MapReduce/Spark/Flink/Tez/Presto)同时争抢CPU、内存、磁盘IO、网络带宽时,系统如何不崩溃、不饿死、不内耗”的生存级问题。关键词“YARN Architecture”背后,藏着的是资源抽象粒度设计、多租户隔离边界、调度策略与业务SLA的映射关系、以及故障传播的阻断机制——这些才是决定一个大数据平台是“能用”还是“敢用”的分水岭。这篇文章适合三类人:正在搭建生产集群的运维工程师(你需要知道为什么不能直接照搬官网默认配置)、开发Spark/Flink作业却总被OOM Kill的算法工程师(你提交的每个--executor-memory其实在和YARN的内存模型博弈)、以及准备深入理解分布式系统本质的架构师(YARN的ApplicationMaster模型,本质上是对“分布式状态机”最朴实也最有效的工程实现)。它不讲概念复述,只讲我踩过的坑、算过的账、压测出的阈值、以及在凌晨三点集群雪崩时真正救回来的那几行配置。

2. YARN整体设计与思路拆解:为什么必须把“资源管理”和“作业调度”彻底分离?

2.1 从MRv1的“单体困局”到YARN的“微内核革命”

要真正吃透YARN架构,必须回到它的诞生原点:MapReduce v1(MRv1)的致命缺陷。在MRv1时代,JobTracker身兼三职:既是资源管理者(分配TaskTracker上的slot),又是作业调度器(决定哪个Job的Map/Reduce Task先跑),还是作业监控中心(跟踪每个Task状态、重试失败Task)。这种“大包大揽”的单体设计,在集群规模超过500节点后就开始显露出不可调和的矛盾:

  • 资源粒度粗放:TaskTracker只有“Map Slot”和“Reduce Slot”两个固定类型,无法表达Spark需要的“2核8G容器”或Flink需要的“4核16G容器”这种细粒度需求;
  • 单点瓶颈严重:JobTracker的JVM堆内存成为最大瓶颈,我们曾在一个1200节点集群中观测到JobTracker GC停顿长达47秒,期间所有新作业提交全部超时;
  • 扩展性归零:新增一种计算引擎(比如想接入Storm),必须修改JobTracker源码并重启整个集群,这在生产环境是不可接受的。

YARN的设计哲学,就是用“分而治之”打破这个困局。它把MRv1的JobTracker一拆为二:ResourceManager(RM)专注做“资源管理”这件事,ApplicationMaster(AM)则由每个应用自己实现,专注做“作业调度”这件事。这个分离不是简单的代码拆分,而是架构范式的升维——RM变成了一个无状态的、可水平扩展的“资源银行”,它只管“账户余额”(集群总资源)和“取款规则”(调度策略),不管“钱怎么花”;而每个应用的AM,则是这个银行的“VIP客户经理”,它清楚自己客户的每一笔资金需求(需要多少Container、什么规格、何时释放),并代表客户去RM柜台排队取钱。这种设计带来的直接好处是:Spark可以有自己的AM来实现DAG调度,Flink可以有自己的AM来管理Checkpoint协调,而RM完全不用感知它们的存在。我们上线Flink SQL服务时,集群零改造,只部署了新的Flink AM Jar包,这就是架构解耦的力量。

2.2 ResourceManager:不是“老大”,而是“资源交易所”的清算中心

很多人误以为ResourceManager是YARN的“大脑”,其实更准确的比喻是“资源交易所的清算中心”。它不直接控制任何物理资源,只维护两套核心数据结构:

  • ClusterMetrics:实时聚合所有NodeManager上报的资源使用快照(CPU利用率、内存占用、磁盘剩余、网络吞吐),形成集群全局视图;
  • Scheduler State:记录每个队列(Queue)当前已分配、待分配、预留的资源量,以及每个Application的Container请求列表。

关键在于,RM本身不执行任何调度决策。真正的调度逻辑被封装在Scheduler插件中(如CapacitySchedulerFairScheduler),RM只是调度器的“执行代理”。当一个Container请求到达时,RM的流程是:

  1. 将请求转发给当前激活的Scheduler;
  2. Scheduler根据队列配额、用户权重、资源亲和性等规则,计算出该请求应分配到哪个NodeManager;
  3. RM向目标NodeManager发送StartContainerRequest指令,并更新自身状态。

这个设计规避了RM成为性能瓶颈的风险。我们做过压测:在3000节点集群中,启用CapacityScheduler时,RM处理Container请求的P99延迟稳定在8ms以内;而如果把调度逻辑硬编码进RM主循环,这个延迟会飙升到200ms以上。更重要的是,Scheduler可以热替换——去年我们因业务需要将FairScheduler无缝切换为CapacityScheduler,全程RM未重启,业务无感知。这背后是YARN对“关注点分离”原则的极致贯彻:RM只负责可靠地传递指令和维护状态一致性,调度策略的复杂性被隔离在可插拔的模块里。

2.3 ApplicationMaster:每个应用的“自治政府”,也是故障隔离的第一道墙

ApplicationMaster(AM)是YARN架构中最容易被低估,却最关键的组件。它不是一个预装的系统服务,而是每个应用启动时动态创建的、生命周期与应用绑定的独立进程。以Spark为例:当你执行spark-submit,客户端首先向RM申请一个Container来启动Spark AM;这个AM启动后,立刻向RM申请一批Executor Container;拿到Container后,AM再通过RPC通知对应的NodeManager启动Executor进程。整个过程,RM只看到“一个AM请求N个Container”,完全不知道这些Container里跑的是Spark还是Flink。

AM的核心价值在于实现了应用级的故障自治。当某个Executor因OOM被NodeManager Kill时,NodeManager只向该Executor所属的AM报告失败,AM根据自己的容错策略(如Spark的Stage重试、Flink的Checkpoint恢复)决定是重启Executor还是降级处理。RM对此一无所知,也不会因此触发集群级告警。这种设计将故障影响范围严格限制在单个应用内部。我们在一个混合集群中曾观察到:一个Spark Streaming作业因Kafka分区倾斜导致持续GC,其AM不断申请新Container又不断被Kill,但同一集群上的Hive批处理作业完全不受影响——因为它们的AM是独立的,资源请求走的是不同的调度队列。反观MRv1,一个Map Task卡死会导致整个JobTracker心跳超时,进而引发全集群假死。YARN用AM这个轻量级“自治单元”,把分布式系统的“爆炸半径”从集群级压缩到了应用级。

2.4 NodeManager:不是“打工仔”,而是“资源守门人”与“安全沙箱”

NodeManager(NM)常被简单理解为“在节点上干活的工人”,但它实际承担着三重关键角色:

  • 资源守门人(Resource Guardian):NM定期向RM汇报本节点资源(如yarn.nodemanager.resource.memory-mb=64000),但这个值不是静态配置,而是NM主动探测的结果。它会扫描/proc/meminfo获取真实可用内存,减去OS保留内存(yarn.nodemanager.resource.system-reserved-memory-mb)、Docker守护进程内存、以及为其他非YARN服务(如ZooKeeper)预留的内存后,才上报给RM。我们曾在线上发现某节点因内核OOM Killer干掉了NM进程,根本原因就是system-reserved-memory-mb配置过小,NM上报了虚高内存,导致RM过度分配,最终触发系统级OOM。

  • 安全沙箱(Security Sandbox):NM是YARN安全模型的执行终端。当AM请求启动Container时,NM会:

    1. 校验Container Token(由RM签发,含资源规格和用户身份);
    2. 使用Linux Container Executor(LCE)以指定用户(如hive)身份启动进程;
    3. 通过cgroups v1/v2对CPU份额、内存上限、磁盘IO权重进行硬隔离;
    4. 挂载只读的HDFS Kerberos ticket cache(/var/lib/hadoop-yarn/tokens)供Container访问HDFS。

没有NM的沙箱能力,YARN的多租户就只是纸面协议。我们曾因关闭LCE改用DefaultContainerExecutor,导致一个恶意用户通过ulimit -v unlimited耗尽节点内存,拖垮了同节点上所有其他用户的作业。

  • 本地化加速器(Localization Accelerator):NM内置的LocalResourcesTracker服务,会预拉取并缓存AM指定的Jar包、配置文件、共享库到本地磁盘。当Container启动时,NM直接从本地路径加载,避免每次启动都走HDFS网络IO。我们通过监控nm-local-dir磁盘使用率,发现热点Jar包(如Spark Core)的缓存命中率高达92%,将Container启动时间从平均3.2秒降至0.8秒。

3. 核心细节解析与实操要点:参数不是数字,而是资源边界的刻度尺

3.1 内存模型:从“Java堆”到“Container内存”的三级映射

YARN的内存管理是新手最容易栽跟头的地方。你以为设置spark.executor.memory=8g就万事大吉?实际上,这8G只是JVM堆内存,而YARN要求你为整个Container划定一个硬性内存上限yarn.scheduler.maximum-allocation-mb),这个上限必须大于等于JVM堆+堆外内存+JVM自身开销。我们曾因忽略这点,在一个Spark作业中设置executor-memory=12g,但集群maximum-allocation-mb=10240(10G),导致所有Executor Container被NM拒绝启动,日志里只有一行冰冷的Container is not running, state=COMPLETE, exitStatus=-100,排查了两天才发现是内存规格越界。

完整的内存映射链路如下:

YARN Container Memory (e.g., 16GB) ├── JVM Heap Memory (spark.executor.memory=12g) ├── JVM Off-Heap Memory (spark.executor.memoryOverhead=4g) │ ├── Netty Direct Buffer │ ├── Spark Shuffle External Sort Buffer │ └── JVM Code Cache & Metaspace └── JVM Process Overhead (OS进程开销,约1-2GB)

其中memoryOverhead是关键桥梁。它的计算公式是:max(384MB, 0.1 * executor-memory)。但这个0.1倍是经验值,实际需根据作业特征调整。例如,一个大量使用Kryo序列化的Spark作业,memoryOverhead设为0.15 * heap更稳妥;而一个纯计算型(无Shuffle)作业,0.07 * heap即可。我们通过jstat -gc <pid>监控Executor的CCST(Compressed Class Space Used)和MU(Metaspace Usage),发现当memoryOverhead不足时,MU会持续增长直至触发Full GC。最终我们为不同业务线制定了内存配置矩阵:

业务类型executor-memorymemoryOverheadContainer Memory说明
Hive on Spark16g2g20gShuffle密集,需大Direct Buffer
实时ETL8g1.5g12g网络IO高,Netty缓冲区大
机器学习训练32g4g38g大模型加载,Metaspace占用高

提示:yarn.nodemanager.vmem-pmem-ratio参数(默认2.1)决定了虚拟内存(VMEM)与物理内存(PMEM)的换算比例。当Container的VMEM使用超过PMEM上限 * ratio时,NM会Kill该Container。很多用户遇到“OOM Killed”却查不到内存泄漏,根源就是这个ratio太小,导致正常堆外内存被误判为溢出。我们线上统一调至4.0,并配合-XX:MaxDirectMemorySize显式限制Netty Direct Buffer。

3.2 CPU调度:从“核数”到“CPU份额”的认知跃迁

YARN对CPU的抽象比内存更激进——它不直接分配“CPU核心”,而是分配“CPU份额”(CPU vcores)。这是为了兼容异构硬件(如Intel Xeon Platinum vs AMD EPYC)和云环境(如AWS EC2的vCPU)。yarn.nodemanager.resource.cpu-vcores参数定义了节点可提供的最大vcore数,而yarn.scheduler.maximum-allocation-vcores定义了单个Container可申请的最大vcore数。

关键认知转变:vcore不是物理核心,而是调度器分配CPU时间片的计量单位。一个Container申请2个vcore,意味着它在调度周期内有权获得约200%的CPU时间(即2个物理核心的等效算力),但这不保证独占2个物理核心。CapacityScheduler通过Linux cgroups的cpu.shares文件实现份额分配,其值为1024 * requested_vcores。例如,申请1个vcore的Container,其cpu.shares=1024;申请2个vcore的Container,cpu.shares=2048。当多个Container竞争CPU时,内核按shares比例分配时间片。

我们曾在线上验证:在4核节点上,同时运行两个Container(各申请2vcore),top显示它们的CPU%总和稳定在200%左右(即2个核心满载);而当其中一个Container申请4vcore时,其CPU%可飙升至400%,另一个Container被压制到接近0%。这证明YARN的CPU调度是抢占式的,而非静态绑定。因此,对延迟敏感的作业(如Flink实时处理),应避免在同一个节点上混布高vcore请求的批处理作业,否则会遭遇不可预测的CPU抖动。

注意:yarn.nodemanager.resource.percentage-physical-cpu-usage参数(默认-1)用于将vcore映射到物理CPU使用率。设为100表示1个vcore = 100%物理CPU,此时vcores=4等价于绑定4个物理核心。但此模式会破坏YARN的弹性调度能力,仅建议在超低延迟场景且硬件同构时谨慎启用。

3.3 容器生命周期:从“启动”到“销毁”的七步状态机

YARN Container的状态流转不是简单的“RUNNING → COMPLETE”,而是一个严谨的七步状态机,每一步都对应明确的资源操作和错误处理逻辑:

  1. NEW:AM向RM提交Container请求,RM生成Container ID并进入等待调度状态;
  2. ALLOCATED:Scheduler分配成功,RM向NM发送StartContainerRequest,Container在NM端创建但尚未启动;
  3. ACQUIRED:NM完成资源准备(下载依赖、创建cgroups组、设置环境变量),向AM返回确认;
  4. RUNNING:NM调用launchContainer()启动用户进程,Container正式运行;
  5. COMPLETE:用户进程退出,NM回收cgroups、清理临时文件,向RM和AM报告终态;
  6. KILLED:AM或RM主动发送StopContainerRequest(如作业取消、资源抢占);
  7. FAILED:NM检测到进程异常退出(如OOM Kill、Segmentation Fault),或健康检查失败。

这个状态机的价值在于可观测性。我们通过监听ContainerStatus事件流,构建了Container健康度看板。例如,当ALLOCATED → ACQUIRED耗时超过5秒,说明NM本地化(localization)慢,需检查nm-local-dir磁盘IO;当RUNNING → COMPLETE但exitStatus为143,基本确定是JVM收到SIGTERM(被YARN优雅终止);而exitStatus=137则铁定是OOM Kill(SIGKILL)。一次线上事故中,我们发现某业务线Container的FAILED率突然升高,追踪exitStatus=137占比达89%,立即定位到其memoryOverhead配置不足,避免了更大范围的作业失败。

3.4 队列与资源池:用CapacityScheduler构建企业级资源银行

在生产环境中,CapacityScheduler(CS)是绝对主流,因为它将YARN变成了一个可计费、可审计、可SLA保障的“资源银行”。CS的核心是层级化队列(Hierarchical Queues),每个队列有独立的资源配额、访问控制和调度策略。我们的典型配置如下:

<property> <name>yarn.scheduler.capacity.root.queues</name> <value>default,prod,dev,ml</value> </property> <property> <name>yarn.scheduler.capacity.root.prod.queues</name> <value>etl,reporting,streaming</value> </property> <property> <name>yarn.scheduler.capacity.root.prod.etl.capacity</name> <value>40</value> <!-- 占prod队列40%资源 --> </property> <property> <name>yarn.scheduler.capacity.root.prod.etl.maximum-capacity</name> <value>60</value> <!-- 紧急时最多借到60% --> </property> <property> <name>yarn.scheduler.capacity.root.prod.etl.user-limit-factor</name> <value>2</value> <!-- 单用户最多用队列2倍资源 --> </property>

这里的关键参数是maximum-capacity(弹性上限)和user-limit-factor(用户弹性因子)。它们共同构成了资源“潮汐调度”能力。例如,etl队列基础配额40%,但允许在reporting队列空闲时,临时借用至60%;而单个ETL用户在队列资源充足时,甚至能用到80%(40% * 2)。这种设计让资源利用率从静态的60%提升至动态的85%以上。但我们踩过一个深坑:当user-limit-factor设得过高(如5),而用户提交大量小作业时,会导致单节点Container密度过高,触发NM的yarn.nodemanager.container-executor.max-user-containers限制(默认20),造成Container排队。最终我们将user-limit-factor统一设为1.5,并通过yarn.scheduler.capacity.root.<queue>.minimum-user-limit-percent(最小用户保障百分比)确保小用户不被饿死。

4. 实操过程与核心环节实现:从零搭建一个抗压的YARN集群

4.1 硬件规划:不是“堆配置”,而是“画资源边界”

搭建YARN集群的第一步,不是下载安装包,而是用白板画出每台物理机的资源边界图。我们坚持“一机一图”,包含以下必填项:

  • 物理资源:CPU型号/核心数/超线程开关、内存总量/通道数、SSD/NVMe容量/IOps、万兆网卡数量;
  • OS保留system-reserved-memory-mb(我们取min(4096, total_memory * 0.1))、reserved-cpu-cores(留2核给OS和监控);
  • YARN可用资源yarn.nodemanager.resource.memory-mb=total_memory - system_reservedyarn.nodemanager.resource.cpu-vcores=physical_cores - reserved_cores
  • NM进程开销:为NM JVM单独分配2G内存(-Xmx2g),避免与Container争抢;
  • 本地存储规划yarn.nodemanager.local-dirs至少挂载2块SSD,yarn.nodemanager.log-dirs单独挂载NVMe盘(日志写入频繁)。

这个过程看似繁琐,但避免了90%的资源争抢问题。我们曾因一台服务器BIOS中未关闭C-states节能模式,导致NM上报的CPU频率虚高,Scheduler过度分配vcore,最终引发CPU软中断风暴。硬件规划表就是集群的“宪法”,所有后续配置都必须严格遵循。

4.2 RM高可用:不是“加节点”,而是“建仲裁环”

YARN RM HA不是简单的主备切换,而是基于ZooKeeper的多活仲裁环。我们采用3节点RM部署(rm1/rm2/rm3),ZK集群5节点(zk1-zk5),关键配置如下:

<!-- yarn-site.xml --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.cluster-id</name> <value>yarn-cluster</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2,rm3</value> </property> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>rm1-host</value> </property> <!-- ... rm2/rm3 hostname ... --> <property> <name>yarn.resourcemanager.zk-address</name> <value>zk1:2181,zk2:2181,zk3:2181,zk4:2181,zk5:2181</value> </property> <property> <name>yarn.resourcemanager.recovery.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.store.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value> </property>

HA的核心是状态持久化。所有RM状态(Application状态、Container分配记录、队列配额)都通过ZKRMStateStore写入ZooKeeper的/yarn-leader-election/rmstore路径。当rm1宕机,rm2会尝试在ZK创建/yarn-leader-election/yarn-cluster的临时顺序节点,序号最小者成为Active RM,并从ZK加载完整状态。这个过程平均耗时12秒(P95),远低于MRv1的分钟级恢复。但要注意:ZK的tickTime必须设为2000ms(默认2000),且initLimitsyncLimit需根据网络RTT调整。我们曾因ZK集群syncLimit=5(默认5),而跨机房网络RTT达120ms,导致RM状态同步超时,反复触发Leader选举。最终将syncLimit调至10,问题解决。

4.3 NM调优:让每台机器成为“资源堡垒”

NodeManager的调优直接决定集群的稳定水位。我们针对三个关键维度做了深度定制:

1. Container启动加速

  • 启用LinuxContainerExecutor(LCE),关闭DefaultContainerExecutor
  • yarn.nodemanager.delete.debug-delay-sec=3600:延长临时文件保留时间,便于故障时抓取现场;
  • yarn.nodemanager.localizer.fetch.thread-count=10:提升Jar包并行下载速度。

2. 资源隔离强化

  • 强制启用cgroups v2:yarn.nodemanager.linux-container-executor.cgroups.hierarchy=/hadoop-yarn
  • 设置严格的内存限制:yarn.nodemanager.linux-container-executor.resources-handler.class=org.apache.hadoop.yarn.server.nodemanager.util.CgroupsLCEResourcesHandler
  • CPU份额隔离:yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage=true

3. 健康检查闭环

<property> <name>yarn.nodemanager.health-checker.script.path</name> <value>/opt/hadoop/bin/nm-health-check.sh</value> </property> <property> <name>yarn.nodemanager.health-checker.script.opts</name> <value>-w 80 -d 10</value> <!-- -w: 磁盘使用率阈值80%, -d: IO延迟阈值10ms --> </property> <property> <name>yarn.nodemanager.health-checker.interval-ms</name> <value>60000</value> </property>

nm-health-check.sh脚本会实时检测:

  • /dev/shm是否满(Spark Shuffle临时目录);
  • iostat -x 1 3await是否超10ms;
  • df -h /data磁盘使用率是否超80%。
    一旦任一指标超标,NM自动向RM上报UNHEALTHY状态,RM将停止向该节点分配新Container,并迁移已有Container。这个闭环让我们将节点级故障自愈时间从小时级缩短至分钟级。

4.4 应用提交实战:Spark on YARN的“黄金配置”

以Spark作业为例,展示如何将YARN架构知识转化为实操参数:

spark-submit \ --master yarn \ --deploy-mode cluster \ --num-executors 100 \ --executor-cores 4 \ --executor-memory 12g \ --driver-memory 4g \ --conf spark.yarn.am.memory=2g \ --conf spark.yarn.am.memoryOverhead=1g \ --conf spark.yarn.executor.memoryOverhead=2g \ --conf spark.yarn.maxAppAttempts=2 \ --conf spark.yarn.am.attemptFailuresValidityInterval=1h \ --conf spark.sql.adaptive.enabled=true \ --queue prod.etl \ your-app.jar

关键参数解析:

  • --executor-cores 4:申请4个vcore,匹配NM的cpu-vcores配置,避免CPU碎片;
  • --executor-memory 12g+--conf spark.yarn.executor.memoryOverhead=2g= Container内存14g,小于集群maximum-allocation-mb=16384,留有2g余量;
  • --conf spark.yarn.maxAppAttempts=2:AM最多重启2次,防止AM自身缺陷导致无限重试;
  • --queue prod.etl:精准落入prod.etl队列,享受40%基础配额和60%弹性上限;
  • --conf spark.sql.adaptive.enabled=true:开启AQE,让Spark AM能根据运行时数据分布动态调整Shuffle分区数,减少小文件和数据倾斜。

我们曾用这套配置,在3000节点集群上稳定支撑单日20万Spark作业,平均Container启动成功率99.97%,P99启动延迟1.2秒。

5. 常见问题与排查技巧实录:那些凌晨三点教会我的事

5.1 “Container killed by YARN for exceeding memory limits” —— 不是内存泄漏,是边界没画清

这是YARN集群最经典的报错。表面看是Container OOM,但根因往往是资源配置失配。我们的标准化排查流程如下:

Step 1:定位具体Container
从Application Master UI的“Running Containers”列表,找到State=KILLEDExit Status=137的Container,记录其Container ID(如container_1678886400000_0001_01_000005)。

Step 2:分析NM日志
登录该Container所在节点,搜索/var/log/hadoop-yarn/node-manager/yarn-yarn-nodemanager-*.log

2023-03-15 02:14:22,102 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor.ContainersMonitorImpl: Container [pid=12345,containerID=container_1678886400000_0001_01_000005] is running beyond virtual memory limits. Current usage: 15.2 GB of 16 GB physical memory used; 32.1 GB of 33.6 GB virtual memory used. Killing container.

注意两个关键数字:15.2 GB of 16 GB physical memory(物理内存超限)和32.1 GB of 33.6 GB virtual memory(虚拟内存超限)。

Step 3:交叉验证JVM内存
jstat -gc 12345 1000 3查看JVM堆使用:

S0C S1C EC OC MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 0.0 0.0 1048576.0 2097152.0 1048576.0 982345.0 131072.0 124567.0 1234 12.345 0 0.000 12.345

OC(Old Gen Capacity)为2G,MU(Metaspace Used)为982MB,说明JVM堆(12G)和Metaspace(1G)共用约13G,剩余2G被堆外内存(Netty Direct Buffer、JVM Code Cache)占用。但日志显示物理内存用了15.2G,说明还有约2.2G是OS进程开销或cgroups统计误差。

Step 4:修正配置

  • spark.yarn.executor.memoryOverhead2g提高到3g
  • 检查yarn.nodemanager.vmem-pmem-ratio是否过小(我们线上设为4.0);
  • 在Spark代码中显式设置-XX:MaxDirectMemorySize=2g,限制Netty Direct Buffer。

实操心得:我们编写了一个Python脚本yarn-container-analyzer.py,输入Container ID,自动从YARN REST API拉取状态、解析NM日志、调用jstat,生成一页PDF诊断报告。这个工具将平均排障时间从47分钟缩短至6分钟。

5.2 “Application failed 2 times due to AM Container for appattempt_... exited with exitCode: -1000” —— RM与NM的“信任危机”

exitCode: -1000是YARN特有的错误码,含义是“AM Container启动失败,原因未知”。这通常暴露了RM与NM之间的通信或认证断层。排查路径如下:

1. 检查RM日志中的AM Container启动请求
/var/log/hadoop-yarn/resourcemanager/yarn-yarn-resourcemanager-*.log中搜索:

2023-03-15 03:22:11,456 INFO org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptImpl: appattempt_1678886400000_0001_000001 State change from SUBMITTED to SCHEDULED ... 2023-03-15 03:22:12,789 INFO org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler: Assigned container container_1678886400000_0001_01_000001 of capacity <memory:2048, vCores:1> on host ...

确认RM确实发出了分配指令。

2. 检查NM日志中的Container接收记录
在目标节点的NM日志中搜索Container ID:

2023-03-15 03:22:12,801 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch: Launching container container_1678886400000_0001_01_000001 ... 2023-03-15 03:22:12,805 ERROR org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch: Failed to launch container: java.io.IOException: Failed to initialize container

错误堆栈往往指向ContainerLocalizerLinuxContainerExecutor

3. 根本原因定位
90%的情况是Kerberos票据失效或权限问题

  • 检查/var/log/hadoop-yarn/node-manager/usercache/<user>/appcache/目录权限,应为drwxr-x--- <user> hadoop
  • 运行klist -c /var/lib/hadoop-yarn/tokens/,确认票据未过期;
  • 检查/etc/krb5.confdefault_realm是否与KDC一致。

我们曾因运维同事在KDC服务器上执行了kadmin -q "purgekeys <principal>",导致所有历史票据密钥被清除,NM无法解密Container Token,从而批量返回-1000。解决方案是重建Keytab并滚动重启NM。

5.3 “All nodes are unhealthy” —— 健康检查的“狼来了”陷阱

当YARN Web UI显示“All nodes are unhealthy”,集群瞬间瘫痪。这不是节点真的宕机,而是健康检查机制被误触发。我们的应对清单:

  • 检查yarn.nodemanager.health-checker.script.path脚本输出:手动执行该脚本,看是否返回非零退出码;
  • 验证ZooKeeper连接:NM健康检查会尝试连接ZK,`telnet zk1

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询