在上一篇中已经介绍了 YARN 的整体架构:ResourceManager(RM)负责集群资源调度,NodeManager(NM)负责单机资源管理,ApplicationMaster(AM)负责单个应用的生命周期管理,Container 则是 YARN 分配资源的基本单位。
这一篇主要补充这些组件各自负责什么,以及它们之间如何配合完成一次应用运行。
1. 体系架构概述
YARN 将资源管理与计算引擎的执行逻辑解耦。ResourceManager 不直接管理每一种计算框架内部的任务执行细节,而是负责全局资源调度;具体任务如何拆分、如何重试、如何汇报进度,则交给各个框架自己的 ApplicationMaster 处理。
这种设计带来的结果是:
- 集群可以同时运行 MapReduce、Spark、Flink、Tez 等不同类型的应用。
- ResourceManager 的职责更集中,不需要维护所有任务的执行细节。
- 不同应用可以根据自己的计算模型实现不同的 ApplicationMaster。
- NodeManager 负责本节点上的 Container 生命周期管理,包括启动、监控和清理。
2. ResourceManager
ResourceManager 是整个 YARN 集群的中心服务,负责接收应用提交、调度资源、管理节点状态和维护应用生命周期。
ResourceManager 主要包含两类核心职责:
- 资源调度:由 Scheduler 负责,根据队列容量、资源使用情况、应用优先级和本地性等因素分配 Container。
- 应用管理:由 ApplicationsManager 负责,接收客户端提交请求,启动 ApplicationMaster,并在 ApplicationMaster 失败时根据策略重新启动。
ResourceManager 不直接启动用户任务,也不跟踪每个任务的业务执行细节。但它会为应用分配用于启动 ApplicationMaster 的第一个 Container。之后,ResourceManager 主要维护集群资源状态、节点心跳信息、应用资源请求以及已经分配出去的 Container。
2.1 客户端和 ResourceManager 的交互
客户端提交应用时,会先向 ResourceManager 申请一个新的 ApplicationId,然后上传应用运行所需的文件,最后提交应用启动上下文。启动上下文中通常包括:
- ApplicationMaster 启动命令。
- ApplicationMaster 需要的内存和 CPU。
- 应用依赖的 jar 包、配置文件等 LocalResource。
- 环境变量和安全令牌。
ResourceManager 接收应用后,会为 ApplicationMaster 分配第一个 Container,并通知对应 NodeManager 启动它。
2.2 ApplicationMaster 和 ResourceManager 的通信
ApplicationMaster 启动后会向 ResourceManager 注册。注册完成后,它就可以根据应用内部任务数量和资源需求,持续向 ResourceManager 申请 Container。
ResourceManager 返回 Container 分配结果后,ApplicationMaster 再去联系对应的 NodeManager 启动任务。任务执行结束后,ApplicationMaster 会继续向 ResourceManager 汇报状态,直到应用完成并注销。
2.3 NodeManager 和 ResourceManager 的通信
NodeManager 会周期性向 ResourceManager 发送心跳,心跳中包含本节点的健康状态、资源使用情况和 Container 运行状态。ResourceManager 根据这些心跳维护集群视图。
如果某个 NodeManager 长时间没有心跳,ResourceManager 会认为该节点不可用,并把它上面运行的 Container 标记为失败。具体是否重试任务,由对应应用的 ApplicationMaster 决定。
3. YARN 调度器组件
YARN 的 Scheduler(调度器)是可插拔的,常见调度器包括 FIFO、Capacity 和 Fair。
3.1 FIFO 调度器
FIFO 调度器按照应用提交顺序分配资源,先提交的应用优先获得资源。它实现简单,但不适合多用户共享集群,因为大任务可能长时间占用资源,导致后续小任务排队等待。
3.2 Capacity 调度器
Capacity 调度器面向多租户场景。管理员可以把集群资源划分成多个队列,每个队列配置一定的容量。不同团队或业务线提交到不同队列中,在保证队列容量的同时,也可以在资源空闲时临时使用其他队列未用完的资源。
Capacity 调度器常用于生产集群,因为它能兼顾资源隔离和集群利用率。
3.3 Fair 调度器
Fair 调度器的目标是让多个应用尽可能公平地共享集群资源。它会不断调整资源分配,使每个应用在一段时间后都能获得相对公平的资源份额。
Fair 调度器适合多用户、任务大小差异较大的环境,可以减少小任务被大任务长期阻塞的问题。
4. Container
Container 是 YARN 分配资源的基本单位。一个 Container 表示某个节点上的一组资源,例如一定数量的内存和 CPU。
需要注意的是,Container 不是一个具体的进程,而是资源和启动上下文的抽象。它通常包含 CPU、内存等资源能力,也包含启动命令、环境变量、LocalResource 等运行上下文。Container 被分配给应用后,NodeManager 会根据 ApplicationMaster 提供的启动上下文,在这个资源范围内启动具体进程。
一个 YARN 应用至少会有一个 Container 用于运行 ApplicationMaster。ApplicationMaster 启动后,可以继续申请更多 Container 来运行具体任务。
Container 的生命周期大致如下:
- ApplicationMaster 向 ResourceManager 申请资源。
- ResourceManager 分配某个节点上的 Container。
- ApplicationMaster 请求该节点的 NodeManager 启动 Container。
- NodeManager 准备运行环境,启动用户进程。
- Container 运行结束后,NodeManager 汇报状态并清理本地资源。
5. NodeManager
NodeManager 是每个计算节点上的代理服务,负责管理本节点资源和 Container。
NodeManager 的主要功能包括:
- 向 ResourceManager 注册并周期性发送心跳。
- 接收 ApplicationMaster 的请求,启动或停止 Container。
- 监控 Container 的内存、CPU 等资源使用情况。
- 管理应用依赖文件的本地化。
- 收集和聚合 Container 日志。
- 清理应用结束后的本地临时文件。
NodeManager 不决定哪个应用可以获得资源,也不决定应用内部任务如何调度。它不参与全局调度决策,只负责执行 ResourceManager 和 ApplicationMaster 分配到本节点上的 Container 生命周期管理工作。
6. ApplicationMaster
ApplicationMaster 是每个 YARN 应用的协调进程(coordinator)。不同计算框架会实现自己的 ApplicationMaster,例如 MapReduce 有 MapReduce ApplicationMaster,Spark on YARN 中也会启动对应的 ApplicationMaster。
ApplicationMaster 的主要职责包括:
- 向 ResourceManager 注册应用。
- 根据任务需求向 ResourceManager 申请 Container。
- 和 NodeManager 通信,启动具体任务。
- 跟踪任务运行状态、处理失败重试。
- 向客户端提供应用进度。
- 应用结束后向 ResourceManager 注销并清理资源。
ApplicationMaster 是 YARN 的核心协调组件之一。它让 ResourceManager 不需要理解每种计算框架的内部逻辑,也让不同框架可以根据自己的执行模型管理任务。
ResourceManager 和 ApplicationMaster 的职责边界可以简单对比如下:
| 组件 | 关注范围 | 主要职责 | 不负责的事情 |
|---|---|---|---|
| ResourceManager | 整个集群 | 维护资源状态、接收应用提交、调度 Container、启动 ApplicationMaster | 不直接执行用户任务,不理解具体计算框架的任务逻辑 |
| ApplicationMaster | 单个应用 | 申请 Container、协调 NodeManager 启动任务、跟踪任务进度和失败重试 | 不做全局资源调度,不管理其他应用 |
7. YARN 资源模型
YARN 使用 ResourceRequest 表达应用对资源的需求。一个资源请求通常包含以下信息:
- 资源位置:可以是某个节点、某个机架,也可以是不指定位置。
- 优先级:同一个应用内部不同请求之间的优先级。
- 资源能力:每个 Container 需要多少内存和 CPU。
- Container 数量:需要多少个满足条件的 Container。
ResourceManager 会根据这些请求,在集群可用资源范围内尽量满足应用需求。如果应用请求了特定节点或机架,调度器会优先考虑数据本地性;如果本地资源不足,也可能退化为机架本地或任意节点。
把前面的组件串起来,一次典型的应用提交和资源申请流程如下:
- Client 向 ResourceManager 提交应用,并携带 ApplicationMaster 启动上下文。
- ResourceManager 为 ApplicationMaster 分配第一个 Container。
- NodeManager 在对应节点上启动 ApplicationMaster。
- ApplicationMaster 向 ResourceManager 注册,并根据任务需求申请更多 Container。
- ResourceManager 通过 Scheduler(调度器)分配 Container。
- ApplicationMaster 联系对应 NodeManager,要求其启动具体任务。
- NodeManager 管理 Container 生命周期,并通过心跳向 ResourceManager 汇报状态。
8. 管理应用程序的依赖文件
YARN 在启动 Container 前,需要先把应用依赖的文件准备到运行节点上,这个过程叫资源本地化。被本地化的文件称为 LocalResource。
LocalResource 通常包含应用 jar 包、配置文件、归档文件或脚本。它有几个重要属性:
- 类型:文件、归档包或模式匹配资源。
- 可见性:PUBLIC、PRIVATE 或 APPLICATION。
- 时间戳:用于判断资源是否发生变化。
- 大小:用于校验和资源管理。
NodeManager 会负责下载这些资源,并放到 Container 的工作目录中。这样用户任务启动时,就可以直接读取本地文件,而不需要每个任务自己去 HDFS 或其他存储系统拉取依赖。
9. 小结
YARN 的核心思想是把集群资源管理拆成几个明确的角色:
- ResourceManager 管全局资源和应用入口。
- NodeManager 管单个节点上的资源和 Container。
- ApplicationMaster 管单个应用的生命周期。
- Container 表示一次具体的资源分配。
理解 YARN 的核心在于理解职责边界,而不只是记住组件名称。理解这些边界后,再看 Spark、Flink、MapReduce 等框架运行在 YARN 上的流程,就会清楚很多:YARN 只提供资源管理和调度能力,真正的计算逻辑仍然由上层计算框架自己完成。