【YARN 权威指南】12. 从计算框架看 YARN 的边界


YARN 最容易被误解的地方,是把它当成某一种计算框架的附属品。早期使用 Hadoop 的时候,MapReduce 和集群资源管理绑得太紧,用户提交作业、调度资源、启动任务、查看历史,几乎都围绕 MapReduce 展开。YARN 把这个关系拆开以后,Hadoop 集群不再只能服务 MapReduce,而是变成一个可以承载多种应用框架的资源平台。

这个变化的意义不只是“能跑更多程序”。更准确地说,集群的职责被重新划分了。YARN 负责资源调度、队列治理、节点管理、Container 生命周期和应用生命周期,日志聚合则是它提供的重要辅助能力;计算框架负责自己的 execution model、状态管理、执行计划、DAG、失败重试和业务指标。比如 MapReduce 的 map/reduce task,Tez 的 DAG,Spark 的 driver/executor,Flink 的 JobManager/TaskManager。只要框架愿意把自己的运行时映射到 YARN 的 ApplicationMaster 和 Container 模型上,它就可以进入同一个资源池。

从这个角度看,YARN 的价值不是替代计算框架,而是给它们提供共同的运行底座。不同框架可以共享一套 HDFS 数据、一套用户和队列策略、一套资源隔离方式、一套基础日志入口。对于集群管理员来说,这比每个框架独占一批机器更容易治理;对于业务团队来说,这意味着可以按任务类型选择合适的执行引擎,而不必每换一个引擎就重新申请一套集群。

1. YARN 的职责边界

1.1 YARN 职责边界:资源与生命周期

先把边界说清楚:YARN 不理解 SQL,不理解流处理,不理解图计算,也不理解 Shuffle 的业务含义。它知道有一个应用提交进来,这个应用需要一个 AM Container,随后 AM 会继续申请若干普通 Container。Container 里跑什么进程,由应用框架的 AM 通过 ContainerLaunchContext 定义。

所以不能简单说“YARN 只负责资源”。更严谨的说法是:YARN 负责资源调度、应用生命周期、基础容器管理、节点心跳和日志聚合入口;计算框架负责任务调度、执行计划、状态管理、失败重试、数据语义和业务指标。

这也是为什么不同框架在 YARN 上的表现差异很大。MapReduce 的 AM 会管理 map task、reduce task、attempt 和 JobHistory;Spark 的 driver 或 AM 会管理 executor;Flink 的 JobManager 会管理 TaskManager、checkpoint 和作业图;Tez 会管理 DAG 顶点和边。YARN 看到的是 Container,框架看到的是自己的任务模型。

可以把职责拆成两层:

层次 负责对象 典型问题
YARN 队列、资源、节点、Container、应用状态、日志聚合 为什么应用一直 ACCEPTED,为什么 Container 没分配
框架 作业图、任务拆分、状态、失败重试、业务指标 为什么 SQL 慢,为什么 checkpoint 失败,为什么 task 重试

排查问题时,这个分层非常重要。一个 Spark 任务失败,不能直接说 YARN 有问题;一个 YARN Container 被杀,也不能直接说 Spark 逻辑有问题。要先判断失败发生在哪一层:资源没有分配,是 YARN/调度器问题;executor 启动后业务代码异常,是框架或作业问题;日志聚合失败,则是 YARN 运维链路问题。

我会先按四层定位:

失败层级 典型现象 优先入口
RM / Scheduler 应用停在 ACCEPTED 队列容量、AM 资源、用户限制
AM / Attempt AM 启动失败或反复重试 AM Container 日志、application attempt
Container / NM Container 被 kill 或启动失败 NodeManager、stdout/stderr/syslog
Framework Stage、checkpoint、task 失败 框架 UI、driver/JobManager 日志

2. 典型计算框架的接入模型

2.1 MapReduce 是 YARN 上最基础的批处理框架

MapReduce 仍然是理解 YARN 框架模型的最佳入口。因为它的角色映射清晰:Client 提交作业,ResourceManager 分配 AM Container,MRAppMaster 负责作业状态机,NodeManager 启动 map/reduce task Container,JobHistoryServer 保存完成后的历史。

MapReduce 的优势不是表达能力最强,而是语义稳定、链路完整、和 Hadoop 生态结合紧密。很多集群验收仍然会用 Pi、terasort、TestDFSIO 之类的示例,不是因为它们代表所有业务,而是因为它们能快速覆盖 HDFS、YARN、MapReduce 和日志链路。

MapReduce on YARN 也说明了一个关键点:框架迁移到 YARN,不等于框架本身消失。MRAppMaster 仍然要理解 InputSplit、map attempt、reduce attempt、Shuffle、Counter 和输出提交。YARN 只是把原来 JobTracker 里资源管理的部分抽出来,让 MapReduce 不再独占集群控制权。

因此,在混部集群里,MapReduce 更适合承担稳定批处理、离线导入导出、基准测试和兼容旧作业。它不是所有任务的最佳选择,Shuffle 成本、磁盘 IO 和多阶段作业启动开销也是它的明显限制。但它是 Hadoop 集群里最稳的基础能力之一。只要 MapReduce 示例跑不通,通常不应该急着排查 Spark 或 Flink,应该先把 YARN、HDFS、Shuffle 和历史服务这条基础链路修好。

2.2 Tez 代表 DAG 执行模型

Tez 的核心价值是把传统 MapReduce 中固定的阶段拆成 DAG。很多数据处理并不是简单的一轮 map 加一轮 reduce,而是多个阶段之间存在依赖关系。如果每个阶段都落成一个独立 MapReduce 作业,中间结果要频繁写盘,作业启动和调度开销也会变高。

DAG 模型更接近复杂查询的真实形态。一个 SQL 查询可能包含过滤、聚合、Join、排序、再聚合。Tez 可以把这些阶段组织成顶点和边,让数据流在一个更完整的执行图中运行。对于 Hive 这类 SQL 引擎来说,Tez 的意义在于让 Hive 不必把查询强行压成多个 MapReduce 作业。

从 YARN 视角看,Tez 仍然是一个应用框架。它需要 AM,需要申请 Container,需要记录应用状态,需要和 Timeline 或自己的 UI 结合。但从框架视角看,它关心的是 DAG 顶点、任务并行度、边的数据传输方式和查询响应时间。

这类框架提醒我们:YARN 的 Container 模型足够通用,但并不表达上层计算图。YARN 可以给 Tez 分配资源,却不会替 Tez 优化 DAG。性能好坏仍然取决于框架执行计划、数据布局、并行度、内存配置和算子实现。

2.3 Spark 让 YARN 成为通用资源入口

Spark 在 YARN 上的意义更直观。Spark 有自己的计算模型和运行时,用户通过 spark-submit 提交应用,可以选择 YARN 作为 cluster manager。这样 Spark 应用不需要单独占用一套机器,而是进入 Hadoop 集群已有的队列体系里。

Spark on YARN 有两个重要模式:cluster mode 和 client mode。cluster mode 下,driver 运行在集群内由 YARN 管理的 ApplicationMaster 进程里;client mode 下,driver 在提交客户端进程里运行,ApplicationMaster 主要负责向 YARN 申请资源。这个差异会影响日志位置、客户端断开后的行为、网络访问方式、故障域和排查路径。driver 在客户端时,客户端网络和进程稳定性会进入故障域;driver 在集群内时,YARN 对它的生命周期管理更完整。

Spark 的例子很好地说明了 YARN 的平台价值。Spark 不需要变成 MapReduce,也不需要放弃自己的 RDD、DataFrame、SQL、executor 和 shuffle 机制。它只是把资源申请和生命周期接入 YARN。YARN 负责给它 Container,Spark 负责在 Container 里组织 executor 和任务。

对管理员来说,Spark 接入 YARN 后,治理重点会落到几个地方:

维度 需要关注的内容
队列 不同团队的 Spark 作业是否进入正确队列
资源 executor 内存、核数、数量是否合理
日志 driver 和 executor 日志是否能定位
依赖 jar、Python 包、配置文件如何分发
数据 HDFS、Hive metastore、权限是否一致

Spark 运行失败时,不能只看 RM 页面。RM 能告诉你应用是否成功、AM 是否启动、Container 是否被杀;Spark UI、driver 日志和 executor 日志才能解释 stage、task、shuffle、SQL 计划和数据倾斜。YARN 是入口,不是全部答案。

Flink 更适合用来观察长期运行应用和状态型计算如何接入 YARN。Flink on YARN 会把 JobManager 和 TaskManager 部署到 YARN Container 中,并根据作业需要分配或释放资源。官方文档也明确给出 Application Mode 和 Session Mode 等部署方式。

这和短批处理作业不同。短任务成功或失败后很快结束,资源很快释放;流处理任务可能运行很久,关注点会变成高可用、checkpoint、savepoint、状态后端、滚动升级和故障恢复。YARN 仍然能提供资源和 Container 生命周期,但跨 Container 的状态一致性要靠 Flink checkpoint、状态后端和恢复协议自己保证。

因此,Flink on YARN 的排查也有两层。第一层看 YARN:应用是否提交成功、JobManager Container 是否启动、TaskManager Container 是否分配、队列资源是否够。第二层看 Flink:checkpoint 是否稳定、反压是否严重、状态大小是否异常、TaskManager 是否频繁重启。

长期应用进入 YARN 后,还要注意资源占用方式。一个流处理任务可能长时间占住队列资源,如果没有队列隔离和容量规划,会影响批处理作业。YARN 能提供队列和容量约束,但业务团队仍然要为长期任务设计资源预算,而不是把它当作一次性批处理提交。

2.5 Storm、Giraph、Hoya 这类项目说明了早期生态探索

早期 YARN 生态里出现过很多项目,它们的共同点不是都成为主流,而是都在尝试回答一个问题:既然 YARN 能管理资源,能不能把更多类型的分布式应用放进来?

Storm 关注实时流和拓扑,核心对象是 Nimbus、Supervisor、worker 和 topology。把 Storm 放到 YARN 上,本质上是把这些长生命周期进程交给 YARN 管理,让它们共享集群资源。这个方向和 Flink、Spark Streaming 等后来更常见的方案有重叠。

Giraph 关注图计算,模型来自 Pregel 一类的 BSP 思路。图计算需要在多个 worker 之间进行迭代和消息传递,MapReduce 并不适合直接表达这种运行方式。Giraph 支持 YARN 的意义,是把图计算也纳入统一资源平台,而不是为图计算另建集群。

Hoya 这类项目尝试把 HBase 这样的服务型系统放到 YARN 上。它的目标不是执行一次计算任务,而是管理一组长期服务进程。这类探索后来有的演化,有的退出主流视野,但它们共同说明:YARN 的 ApplicationMaster 模型不只适合批处理,也可以表达服务编排。

这些项目在系列文章里不适合写成详细教程。原因很简单:生态会变化,项目活跃度会变化,部署方式也会变化。真正稳定的是背后的抽象:Client 提交,AM 协调,Container 执行,RM/NM 管资源和节点。只要抓住这个抽象,就能理解这些项目为什么曾经能跑在 YARN 上,也能判断它们是否仍适合自己的集群。

2.6 REEF、Hamster 这类项目证明 YARN 的可扩展性

REEF 和 Hamster 更像 YARN 可扩展性的例子。REEF 试图提供一套面向资源管理器的应用开发库,让开发者不用从零写 AM、Container 管理、容错和执行流程。Hamster 则关注把 MPI 类 HPC 应用映射到 YARN 集群上。

这些方向都很有启发:YARN 的 Container 并不要求里面只能跑 Java,也不要求任务一定是 Hadoop 风格。只要能和 YARN 的资源申请、启动命令、环境、本地资源和日志模型对接,Container 里可以运行各种进程。

但从工程选择上看,不能因为 YARN 能跑,就认为一定应该跑。MPI、机器学习训练、在线服务、流处理、批处理,它们对网络、存储、生命周期和故障恢复的要求差异很大。YARN 给了统一资源入口,但没有抹平所有领域差异。

REEF 后来进入 Apache Attic,这件事也提醒我们:框架是否能长期使用,不只取决于架构是否漂亮,还取决于社区、生态、运维成本和用户规模。学习 YARN 生态时,不应该把每个框架都当作推荐选型,而应该把它们当作不同阶段的架构尝试。

3. 框架选择和资源映射

3.1 选择框架时先看作业语义

如果一个团队已经有 YARN 集群,最容易犯的错误是先问“这个框架能不能跑在 YARN 上”,而不是先问“这个任务适合什么执行模型”。能跑只是入场条件,不代表适合。

更合理的判断顺序是:

问题 影响
是批处理、交互查询、流处理、图计算还是服务编排 决定框架类型
作业生命周期是分钟级、小时级还是长期运行 决定队列和资源治理
是否需要状态和恢复 决定 checkpoint/savepoint 或输出提交设计
是否依赖 HDFS/Hive/HBase 决定数据和权限集成
是否需要多租户隔离 决定队列、ACL 和容量
运维团队是否熟悉该框架 决定可维护性

比如稳定离线批处理可以继续使用 MapReduce 或 Hive on Tez;复杂 SQL 和交互分析可能更偏向 Spark SQL、Trino 或其它查询引擎;长时间流处理更适合 Flink;图计算要看 Giraph、GraphX 或图数据库方案;服务编排则要评估 YARN 是否仍是合适平台。

YARN 解决的是资源平台问题,不替团队完成框架选型。框架选型要看作业语义、团队能力、数据规模和运维成本。

3.2 计算框架映射模型

不同框架接入 YARN 时,最终都要完成一次模型映射:把自己的运行时角色映射成 Application、ApplicationMaster、Container、LocalResource、日志和状态。

可以用这张表先统一术语:

概念 YARN 视角 框架视角
Application 一次 YARN 应用生命周期 Spark application、Flink job、MapReduce job
Job YARN 不直接理解业务 job MapReduce/Hive/Spark/Flink 的作业语义
Task YARN 不直接调度 task 语义 map task、Spark task、Flink subtask
Container 资源和启动上下文单元 executor、TaskManager、task JVM 所在运行环境
Process NodeManager 启动的进程 driver、executor、JobManager、TaskManager

这里最容易混淆的是 Container 和进程。Container 是资源单位和启动上下文,不等于 Docker 容器,也不等于框架进程本身。Spark executor、Flink TaskManager 或 MapReduce task JVM 是运行在 Container 内的进程。

Application attempt 也要放进模型里看。AM 失败后,RM 可以按配置启动新的 attempt。YARN 负责重新拉起 AM attempt,但框架是否能恢复作业状态,取决于框架自己的 checkpoint、事件日志、输出提交和幂等设计。

Resource locality 也属于边界的一部分。YARN 调度器可以处理 node-local、rack-local、ANY 这类资源位置偏好,但具体如何表达本地性需求,仍然由框架的 AM 通过资源请求提交。MapReduce 会围绕 HDFS block 做本地性优化,Spark 和 Flink 则要结合自己的数据源、缓存和调度策略。

3.3 调度器模型影响多框架行为

多框架混部时,调度器不是透明背景。FIFO、Capacity Scheduler、Fair Scheduler 的目标不同,直接影响应用排队、资源扩张和多租户隔离方式。

调度器 核心特点 适合关注
FIFO 按提交顺序处理 简单集群、低并发测试环境
Capacity Scheduler 层级队列、容量保障、最大容量 多租户、团队资源预算
Fair Scheduler 尽量让应用公平共享资源 多应用并发和弹性共享

Capacity Scheduler 下,长期 Flink 作业如果占住队列 guaranteed capacity,批处理作业可能长时间等待。Fair Scheduler 下,多个 Spark 应用可能获得更均衡的资源,但资源抖动也会影响作业耗时。调度器解决的是资源分配规则,不会替框架解决执行计划、checkpoint 或 shuffle 倾斜。

3.4 多框架共享集群带来的治理问题

当多个框架进入同一个 YARN 集群,治理复杂度会上升。MapReduce、Spark、Flink、Tez 对资源的使用方式不同,日志体系不同,失败语义不同,页面入口也不同。统一资源平台减少了机器管理成本,但增加了应用治理要求。

至少要关注几类问题:

问题 说明
队列规划 批处理、查询、流处理是否共用队列
资源上限 长期任务是否占满队列
日志归档 不同框架日志是否都能追踪
依赖管理 jar、Python 包、配置文件如何分发
安全权限 HDFS、队列、日志、metastore 是否统一
版本兼容 框架依赖的 Hadoop client 是否兼容

这里最容易被低估的是依赖和版本。Spark、Flink、Hive、Tez 都会带自己的依赖体系,和 Hadoop client、Hive metastore、HDFS、YARN API 之间存在兼容关系。应用能提交成功,不代表运行时依赖没有冲突。生产环境要把依赖分发、classpath、用户隔离和升级流程纳入规范。

另一个容易被忽略的是长短任务混部。批处理作业通常希望尽快拿到资源并释放;流处理作业希望长期稳定占用资源。如果它们进入同一队列,流作业可能挤压批作业,批作业也可能在高峰期影响流作业扩缩容。这就是 resource contention 和 noisy neighbor problem。Capacity Scheduler 能提供容量和上限,Fair Scheduler 能提供更弹性的共享方式,但最终还是要靠队列设计表达业务优先级。

日志治理也不能只依赖 RM 页面。YARN log aggregation 能把 AM 和 Container 的 stdout/stderr/syslog 汇总起来,但 Spark、Flink、Hive 还会有自己的 UI、事件日志和历史服务。生产环境通常要把 YARN 日志、框架事件日志和外部日志系统,比如 ELK 或对象存储归档,放到同一条排查链路里。

3.5 YARN 生态变化后的稳定判断

早期很多项目都强调“运行在 YARN 上”。到 2025 年,判断一个框架时不能只看这句话。更应该看几个更实际的问题:

  1. 项目是否仍活跃。
  2. 官方文档是否仍维护 YARN 部署方式。
  3. 和常用 Hadoop 版本是否兼容。
  4. 运维团队是否能定位框架内部问题。
  5. 日志、指标、历史、权限是否能接入现有平台。
  6. 是否有比 YARN 更适合的部署方式。

Spark 和 Flink 仍然保留 YARN 部署方式,但 Kubernetes、云托管服务和专用集群也越来越常见。Tez 在 Hive 生态里仍有意义。部分早期项目已经不再适合作为新系统选型。这个变化并不削弱 YARN 的价值,反而说明 YARN 的定位要看清楚:它是资源管理层,不是所有大数据应用的唯一运行平台。

YARN 和 Kubernetes 的差异也要分清。YARN 的核心对象是 Application、AM、Container、队列和 Hadoop 生态权限;Kubernetes 的核心对象是 Pod、Deployment、Service、Namespace 和云原生控制面。前者更贴近 Hadoop 数据和队列治理,后者更贴近服务化、镜像、弹性和云原生生态。新平台选型时,不能只问“框架能否跑”,还要看数据位置、权限体系、运维工具和团队经验。

对于已有 Hadoop 集群,YARN 仍然有现实价值。数据在 HDFS,权限在 Hadoop 体系,批处理和部分计算框架已经接入,队列和审计也已经建立。这种情况下,让合适的框架接入 YARN,能降低数据搬迁和平台重复建设成本。但对于全新的云原生平台,是否继续以 YARN 为中心,就要结合团队和业务重新评估。

4. 接入检查和排查

4.1 框架接入 YARN 前的检查清单

一个框架宣称支持 YARN,只说明它具备接入入口,不代表可以直接进入生产队列。真正接入之前,至少要做一轮小规模验证。验证目标不是跑通一个 hello world,而是确认它能被现有运维体系接住。

我会把检查项分成六类:

检查项 要确认的问题
提交入口 用什么命令提交,是否能指定队列和应用名
资源模型 AM、driver、executor、TaskManager 分别申请多少内存和核
依赖分发 jar、配置、Python 包、归档文件从哪里来
日志路径 RM 页面、框架 UI、yarn logs 是否能串起来
权限模型 HDFS、队列、metastore、外部系统凭据是否一致
失败恢复 AM 失败、Container 失败、客户端断开后会发生什么

这些检查和框架类型无关。无论是 Spark、Flink、Tez,还是自定义 AM,都绕不开这些问题。只要其中一项含糊,后面就容易出事故。比如日志路径没打通,作业成功时没人关心,一旦失败就会变成“页面上只有 FAILED,但不知道哪里错”。再比如依赖分发没标准化,开发环境能跑,换到生产节点就出现 classpath 冲突。

框架接入还要明确资源责任。用户提交任务时经常只关心 executor 数量或并行度,但 YARN 看到的是 Container。平台应该把框架参数翻译成 YARN 资源消耗,让用户能理解“这个任务会占多少队列资源”。否则队列治理只能停留在管理员手工限额,业务侧没有成本感知。

4.2 多框架排查要先定位入口

多框架集群里,最怕的是排查入口混乱。一个任务失败后,用户打开 RM、框架 UI、JobHistoryServer、日志聚合、业务平台,看到不同状态,就会怀疑系统不一致。实际上这些页面展示的是不同层面的事实。

排查时可以先按入口分类:

入口 适合回答的问题
ResourceManager 应用是否提交、队列是否正确、AM 是否启动
NodeManager Container 是否在节点上启动、是否被本地资源或内存限制影响
yarn logs AM 和 Container 的 stdout/stderr/syslog
框架 UI 作业图、task、stage、checkpoint、executor 等框架内部状态
历史服务 应用结束后的结果、Counter、事件和日志索引

如果 RM 显示应用没有拿到 AM Container,框架 UI 通常也不会有有价值的信息,因为框架运行时还没起来。如果 RM 显示应用成功,但业务结果不对,就要去框架日志和业务输出里找。YARN success = lifecycle success only,不代表业务语义一定正确。

这个判断对 Spark 和 Flink 尤其重要。Spark 应用可能在 YARN 上成功结束,但 SQL 结果是否符合预期,要看作业逻辑;Flink 应用可能在 YARN 上保持 RUNNING,但 checkpoint 持续失败,这属于框架和业务状态问题。YARN 能告诉你资源层是否稳定,不能替你证明业务层正确。

4.3 为什么收尾要落在边界上

YARN 系列如果只停在安装、命令和组件名,很容易形成一种错觉:掌握了 YARN,就掌握了所有大数据框架。事实并不是这样。YARN 是资源层,它重要但有限。它能让多个框架共享集群,也能提供统一的队列和 Container 生命周期,但它不会把不同框架变成同一种系统。

这个边界越早建立越好。管理员需要知道哪些问题应该在 YARN 层处理,哪些问题应该交给框架团队;开发者需要知道提交到 YARN 后,自己的应用仍然要负责依赖、日志、状态和失败语义;平台团队需要知道封装 YARN API 时,不能把底层细节全部隐藏,否则问题发生时无法追踪。

从实践看,YARN 的学习路线也应该沿着这个边界展开。先理解 ResourceManager、NodeManager、ApplicationMaster 和 Container,再理解调度器和队列,然后看 MapReduce、Distributed-Shell 这类具体应用如何映射到 YARN。最后再看 Spark、Flink、Tez 等框架,就不会被框架自己的术语带偏。

5. 小结

从这些框架看 YARN,最重要的结论是:YARN 的核心不是 MapReduce,而是把资源管理从具体计算框架中剥离出来。MapReduce、Tez、Spark、Flink、Storm、Giraph、REEF、Hamster 这些名字背后,真正共同的是 ApplicationMaster、Container、ResourceManager、NodeManager 和队列调度模型。

理解这层边界后,系列前面的内容就能连起来:安装和配置解决运行底座,管理命令解决日常操作,架构理解解决组件职责,Capacity Scheduler 解决多租户资源治理,MapReduce on YARN 说明传统批处理如何迁移,Client、ApplicationMaster 和 Distributed-Shell 则说明自定义应用如何进入 YARN。

YARN 不会替任何框架完成计算语义,也不会自动解决 SQL 优化、流状态、图迭代或服务编排。它提供的是一个通用资源入口。一个框架能不能在 YARN 上稳定运行,取决于它能否把自己的执行模型清楚地映射到 YARN 生命周期里,也取决于运维体系能否覆盖队列、日志、依赖、权限和故障恢复。

这也是我对 YARN 的最终理解:它不是单个框架的替代品,而是 Hadoop 生态中承接多框架运行的资源层。学 YARN 的目的,不是把所有任务都改造成 YARN 应用,而是能判断一个任务进入 YARN 后,哪些问题由 YARN 负责,哪些问题仍然属于框架和业务自身。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录