【YARN 权威指南】10. 理解 ApplicationMaster 与 Container 启动


YARN Client 把应用提交给 ResourceManager 以后,真正决定应用如何运行的是 ApplicationMaster。Client 提交的是 ApplicationSubmissionContext,其中包含 AM Container 的启动上下文;ApplicationMaster 启动后,才开始承担应用内部调度、普通 Container 申请、启动协调、状态监控和完成汇报。

如果把 ResourceManager 看成 cluster-level scheduler 和 allocator,那么 ApplicationMaster 就是单个应用自己的协调器。它不负责全局资源调度,也不是普通任务进程。它站在应用内部,知道自己需要多少个 Container、每个 Container 要运行什么命令、失败后是否重试、什么时候算成功、什么时候应该向 RM 汇报失败。

这次我提交了一个 Distributed-Shell 应用,让它在 root.etl 队列里启动 2 个普通 Container:

hadoop jar /opt/hadoop/share/hadoop/yarn/hadoop-yarn-applications-distributedshell-3.4.1.jar \
  -jar /opt/hadoop/share/hadoop/yarn/hadoop-yarn-applications-distributedshell-3.4.1.jar \
  -appname YarnAMContainerDemoOK \
  -queue etl \
  -master_memory 512 \
  -container_memory 256 \
  -num_containers 2 \
  -shell_command 'echo YARN_AM_CONTAINER_20250226; hostname; pwd; ls -la; printenv | sort | head -20; sleep 8'

运行结果如下:

Application ID: application_1740547572690_0001
Application Name: YarnAMContainerDemoOK
Application Type: YARN
Queue: root.etl
State: FINISHED
Final-State: SUCCEEDED
Log Aggregation Status: SUCCEEDED

ResourceManager 中的 ApplicationMaster 运行结果

1. AM 与 Container 的启动模型

1.1 ApplicationMaster 先向 ResourceManager 注册

ApplicationMaster 是由 NodeManager 启动的,但它启动以后必须先向 ResourceManager 注册。注册成功以后,RM 才知道这个应用的协调者已经可用,后续资源申请、进度上报和完成汇报也才有主体。

这个注册过程解决了一个边界问题:ResourceManager 启动了 AM Container,但 AM 进程是否真正起来、是否能和 RM 通信,需要 AM 自己确认。RM 页面里的应用从 ACCEPTED 进入 RUNNING,通常就意味着 AM 已经启动并注册。

如果应用长时间停在 ACCEPTED,大概率是 AM Container 没有成功分配或没有成功启动。如果应用进入 RUNNING 后很快失败,就要看 AM 日志或后续普通 Container 的状态。

AM 注册以后,会通过 heartbeat 和 RM 持续通信:向 RM 申请资源,接收分配结果,联系对应 NodeManager 启动 Container,监控 Container 完成情况,最终向 RM 注销并报告结果。如果 AM 进程失败,RM 可以根据应用配置启动新的 application attempt;新 attempt 是否能恢复状态,取决于 AM 自己的状态持久化和幂等设计。

1.2 AM 申请的是普通 Container

Client 提交阶段申请的是 AM Container;AM 启动后申请的是普通 Container。这个区别非常重要。

在这次命令里:

master_memory=512
container_memory=256
num_containers=2

master_memory 决定 AM Container 的资源;container_memory 和 num_containers 决定 AM 后续申请的普通 Container 资源。ResourceManager 页面里能看到应用最终消耗了资源秒数,但真正的容器数量要从日志和 attempt 关系里看。

日志聚合输出里能看到这次应用至少涉及三个 Container:

container_1740547572690_0001_01_000001  ApplicationMaster
container_1740547572690_0001_01_000002  shell command
container_1740547572690_0001_01_000003  shell command

第一个 Container 是 AM,后两个 Container 执行用户指定的 shell 命令。这正是 YARN 应用的典型结构:一个 AM 管理多个普通 Container。

1.3 ResourceRequest 表达资源意图

ApplicationMaster 向 ResourceManager 申请资源时,并不是说“给我某台机器上的一个进程”。它提交的是资源请求。这个请求通常包含:

内容 含义
memory Container 内存
vcores Container vCore
priority 请求优先级,调度器会用它参与同一应用内的请求排序
resourceName 节点、机架或 *,分别对应 node-local、rack-local 和 ANY
capability 资源能力描述
numContainers 请求数量

MapReduce AM 会根据输入分片和数据本地性申请 Map Task Container,通常先争取 node-local,再退到 rack-local,最后才是 ANY。Distributed-Shell AM 则更简单,只按用户指定数量申请 Container。无论哪种应用,RM 都只负责根据调度器规则返回分配结果,不负责启动和执行用户命令。

这意味着 AM 不能假设请求一定立即满足。队列资源不足、节点资源不足、用户限制、AM 资源比例、节点标签都可能影响分配。一个设计良好的 AM 要能处理等待、部分分配、失败和重试。

1.4 分配结果回来以后,AM 要联系 NodeManager

ResourceManager 给 AM 的是 Container 分配结果。真正启动进程的是 NodeManager。AM 收到分配后,需要拿到 Container 所在节点的信息,并通过 NodeManager 的 ContainerManagementProtocol 发起启动请求。这里的链路可以理解为:AM 向 RM 申请资源,RM 返回分配结果,AM 再联系被分配到的 NM 启动 Container。

这一步经常被误解。ResourceManager 不会替 AM 执行业务命令,它只告诉 AM:“这个 Container 分给你了,位置在某个 NodeManager 上。”AM 再把启动上下文发给该 NodeManager,由 NodeManager 下载资源、解压归档、创建工作目录、生成启动脚本、设置环境变量并启动进程。

这种设计把全局资源分配和节点进程执行分开。RM 保持轻量,不需要知道每个应用的命令细节;NM 专注本机资源和进程;AM 专注应用语义。

1.5 ContainerLaunchContext 决定进程如何启动

AM 启动普通 Container 时,核心对象是 ContainerLaunchContext。它包含启动一个 Container 需要的几乎全部信息:

内容 说明
LocalResource 需要本地化的文件、jar、脚本、归档
Environment 环境变量
Commands 最终执行的命令
ServiceData 给辅助服务使用的数据
Tokens 访问 HDFS、RM、NM 等服务的凭据
ACLs Container 访问控制

换成结构关系可以这样看:

ContainerLaunchContext
  ├── LocalResource   # jar、脚本、配置、归档
  ├── Environment     # 环境变量
  ├── Commands        # 启动命令
  ├── Tokens          # 安全凭据
  ├── ServiceData     # auxiliary service 数据
  └── ACLs            # 访问控制

Distributed-Shell 的普通 Container 最终执行的是类似这样的命令:

exec /bin/bash -c "echo YARN_AM_CONTAINER_20250226; hostname; pwd; ls -la; printenv | sort | head -20; sleep 8"

这个命令不是用户直接登录节点执行的,而是 AM 通过 ContainerLaunchContext 交给 NodeManager。launch_container.sh 由 NodeManager 根据 CLC 生成和执行,不是 AM 自己在节点上执行脚本。

如果命令里包含复杂引用、括号、重定向或环境依赖,很容易在生成脚本后暴露问题。写 YARN 应用时,应该尽量让启动命令可解释、可记录、可复现,同时注意 shell 注入和引号转义问题。否则失败时只会看到 Container 退出码,很难判断是资源问题、环境问题还是命令本身的问题。

1.6 LocalResource 让 Container 拿到需要的文件

普通 Container 往往不只需要一条命令,还需要 jar、脚本、配置文件、模型文件或压缩包。AM 要把这些资源写入 LocalResource,NodeManager 才能在启动前本地化。

LocalResource 的价值在于统一资源分发。应用不需要自己 scp 到每台机器,也不需要假设每台机器都有相同目录。只要资源在 HDFS 上可访问,NodeManager 就能按类型和可见性把它本地化到 Container 工作目录。NodeManager 还会维护 localization cache,尽量复用已经下载过的资源;timestamp、大小和路径会参与资源版本判断,避免旧资源被误用。

但这也引入了几个排查点:

问题 可能原因
Container 启动前失败 LocalResource 路径不存在、HDFS 权限不足或 token 不可用
部分节点失败 节点本地目录不可写或磁盘不足
应用版本不一致 上传资源被缓存或时间戳不匹配
文件找不到 命令路径和本地化后的工作目录不一致

这次验证命令里执行了 pwd 和 ls -la,就是为了验证 Container 工作目录中能看到本地化后的资源和运行时文件。实际业务应用也应该保留类似可诊断信息,至少在 debug 模式下能输出本地目录、环境变量和关键文件。

1.7 Container 日志属于运行现场

Container 日志先落在 NodeManager 本地目录,再由日志聚合收集到 HDFS。完成后可以用 yarn logs 查看:

yarn logs -applicationId application_1740547572690_0001

这次日志里能看到两个普通 Container 都执行了命令:

Container: container_1740547572690_0001_01_000002
YARN_AM_CONTAINER_20250226

Container: container_1740547572690_0001_01_000003
YARN_AM_CONTAINER_20250226

这说明 AM 至少完成了三件事:

  1. 从 RM 拿到了两个普通 Container。
  2. 向对应 NodeManager 发起了启动请求。
  3. 两个 Container 都执行了 shell 命令并产生日志。

如果日志聚合失败或还没完成,应用本身仍可能成功,但排查能力会下降。生产环境里不能只看 Final-State=SUCCEEDED,还要看 Log Aggregation Status。日志聚合存在延迟,刚结束的应用可能需要等下一轮聚合后才能通过 yarn logs 查全。没有日志,成功只能证明结果,不能解释过程。

1.8 AM 要监控 Container 状态

启动 Container 只是第一步。AM 还要持续从 RM 获取 Container 完成状态。每个普通 Container 结束后,AM 要判断它是成功、失败、被杀掉,还是需要重试。

Distributed-Shell 的判断相对简单:指定数量的 Container 都成功,应用就成功;只要有必要 Container 失败,应用就可能失败。MapReduce 更复杂,Map Task、Reduce Task、TaskAttempt、Shuffle、OutputCommitter 都会参与状态判断。

但底层模式是一致的:

AM 申请 Container
RM 分配 Container
AM 请求 NM 启动 Container
NM 执行命令
Container 结束
AM 收到完成状态
AM 判断应用是否完成
AM 向 RM 注销

这条链路能解释很多状态问题。应用 RUNNING 只说明 AM 已启动并注册,不代表所有 Container 都正常;普通 Container 全部结束也不代表 AM 已经完成注销;AM 成功注销后,RM 才能把应用标记为最终状态。FINISHED 是 YARN 生命周期状态,不等于业务成功,最终还要看 Final-State 是否为 SUCCEEDED。

2. 失败语义和状态机

2.1 失败时要先判断失败层次

ApplicationMaster 和 Container 启动相关问题,可以按层次拆:

层次 典型现象 排查入口
RM / 调度层 应用停在 ACCEPTED Scheduler、队列、AM 资源、AM 资源比例
AM 控制层 应用快速 FAILED AM Container 日志、AM attempt
NM 执行层 AM 正常但普通 Container 启动失败 NodeManager 日志、Container stderr
Container / 业务层 Container 有退出码 用户命令、环境变量、本地文件
日志不可见 状态成功但查不到日志 日志聚合、HDFS 权限

不要把这些都归为“YARN 有问题”。YARN 只提供资源和执行框架,最终启动的命令、依赖的文件、环境变量是否正确,仍然是应用开发者要负责的部分。

2.2 AM 的状态机比代码入口更重要

写 ApplicationMaster 时,很多人会先找 main 方法、RPC 客户端和回调处理。真正落到工程上,更重要的是状态机。AM 至少要回答几个问题:

  1. 应用总共需要多少个 Container。
  2. 已经申请了多少个。
  3. 已经分配了多少个。
  4. 已经启动了多少个。
  5. 成功完成了多少个。
  6. 失败了多少个。
  7. 什么条件下应用成功。
  8. 什么条件下应用失败。

Distributed-Shell 的逻辑很简单:期望数量的 Container 都完成,应用就成功;失败数量超过阈值,应用就失败。MapReduce、Spark、Flink 会复杂很多,还会加入超时处理、重试策略、推测执行和框架自己的任务状态,但底层仍然绕不开这些计数和状态转换。

如果 AM 状态机设计不清楚,最容易出现两类问题。第一类是资源泄漏:Container 已经失败或完成,但 AM 没有释放或没有更新状态,应用一直挂着。第二类是误报成功:部分任务失败了,但 AM 没有正确处理完成状态,最终向 RM 汇报 SUCCEEDED。这两类问题都比普通命令失败更隐蔽。

因此,自定义 YARN 应用里,AM 日志应该重点记录状态变化,而不是只记录“启动了某个 Container”。至少要记录:

requested containers
allocated containers
launched containers
completed containers
failed containers
final application status

有了这些信息,应用运行异常时才能判断是资源没分到、NM 启动失败、命令执行失败,还是 AM 自己的状态判断有问题。

2.3 ContainerLaunchContext 需要可审计

ContainerLaunchContext 是启动 Container 的说明书。它里面的 LocalResource、环境变量和命令,决定了 NodeManager 最终会怎么创建进程。问题是,这个对象在代码里构造完成以后,如果没有日志记录,失败时很难还原。

我更倾向于在 AM 日志里记录一份脱敏后的启动摘要:

内容 记录方式
Container ID 完整记录
NodeManager 完整记录
LocalResource 名称 完整记录
LocalResource 路径 可按敏感级别脱敏
环境变量 key 记录 key,敏感 value 脱敏
启动命令 记录命令模板,敏感参数脱敏
资源请求 完整记录

这不是为了增加日志量,而是为了可审计。Container 启动失败时,NodeManager 日志通常能看到最终脚本,但 AM 侧也应该能说明自己发出了什么启动请求。否则很难判断是 AM 构造错了,还是 NM 本地化或执行阶段出了问题。

这次测试里,日志聚合能看到命令被包装成:

exec /bin/bash -c "echo YARN_AM_CONTAINER_20250226; hostname; pwd; ls -la; printenv | sort | head -20; sleep 8 ..."

这类信息对排查很有帮助。它让我们确认用户命令不是在客户端执行,而是在普通 Container 中执行;stdout/stderr 也被重定向到 YARN 日志目录。

2.4 本地化目录解释了很多“找不到文件”

Container 启动时,NodeManager 会创建应用目录和 Container 目录,把 LocalResource 本地化进去,必要时解压归档,然后生成启动脚本。用户命令运行时看到的是 Container 的工作目录,不是客户端提交命令时的目录。

这解释了很多“本地能跑,YARN 上找不到文件”的问题。客户端机器上存在 /opt/app/config.yaml,不代表 NodeManager 上的 Container 能看到它。除非这个文件已经作为 LocalResource 上传并本地化,或者每台节点都预装了相同路径。

在生产环境里,我会尽量避免依赖节点本地固定路径,除非它是明确的基础运行时,例如 JDK、Hadoop 客户端、系统工具。业务 jar、脚本、配置、模型文件应该通过 LocalResource 分发。这样做虽然多一步上传,但能减少节点差异。

如果必须依赖节点本地路径,就要把它纳入节点基线检查:

  1. 路径是否存在。
  2. 权限是否一致。
  3. 版本是否一致。
  4. 磁盘是否可读。
  5. 是否在所有 NodeManager 节点存在。

单机环境不会暴露这些差异,多节点集群会非常明显。某个任务只在少数节点失败,很多时候不是 YARN 调度问题,而是节点本地环境不一致。

2.5 AM 与 NM 的失败语义不同

ApplicationMaster 和 NodeManager 都可能导致应用失败,但语义不同。

AM 失败通常意味着应用控制器出了问题。RM 可以根据最大尝试次数重启 AM attempt,但如果 AM 代码或配置本身有问题,重试只会重复失败。AM 失败时要看 AM Container 日志、LocalResource、AM 启动命令和凭据。

NM 侧失败更多发生在普通 Container 启动或运行时。比如本地化失败、启动脚本失败、命令退出码非 0、内存超限、磁盘问题。AM 需要接收这些完成状态,再决定是否重试或终止应用。

二者的区别可以这样看:

失败位置 影响 常见处理
AM Container 失败 应用控制器不可用 RM 重试 AM 或应用失败
普通 Container 失败 某个执行单元失败 AM 决定重试或失败
NM 节点异常 该节点上的 Container 受影响 RM/NM 心跳超时后重新调度
日志聚合失败 结果可能成功但证据不完整 修复日志配置或 HDFS 权限

排查时要先看失败的是 AM 还是普通 Container。AM 是应用大脑,普通 Container 是执行单元。大脑没启动和某个执行单元失败,不应该用同一种处理方式。

3. AM 设计和资源治理

3.1 自定义 AM 是否值得写

YARN API 给了很强的控制力,但不是所有场景都值得写自定义 AM。写自定义 AM 意味着要自己处理资源申请、启动、失败、日志、状态、清理和安全。对于大多数批处理、流处理、SQL、机器学习任务,更合理的选择是使用已有框架。

适合考虑自定义 AM 的场景通常有几个特征:

场景 说明
需要管理长期服务 比如一组服务实例,需要按 Container 部署
需要特殊资源编排 普通计算框架不适合表达资源关系
需要框架级控制 要自己定义任务调度、重试和状态
需要和 YARN 深度集成 队列、资源、日志、生命周期都要接入

如果只是想批量执行脚本,Distributed-Shell 或调度平台通常已经够用。如果只是跑计算任务,MapReduce、Spark、Flink 更合适。自定义 AM 的价值在于框架级能力,而不是替代 shell 脚本。

3.2 AM 要处理资源释放和清理

应用成功或失败以后,AM 不能只退出进程。它应该明确完成清理和注销。最基本的是向 ResourceManager 发送完成请求,告诉 RM 这个应用最终是成功、失败还是被终止。否则 RM 只能依赖心跳超时或进程退出推断状态。

清理还包括几类资源:

资源 清理方式
未使用的 Container 请求 向 RM 释放或停止继续申请
已分配但不再需要的 Container 通知 NM 停止
临时状态 删除 AM 自己创建的临时文件
输出目录 成功提交或失败回滚
业务服务 停止对外暴露的端口或进程

Distributed-Shell 很简单,命令执行完就可以结束。真正的服务型应用更复杂。比如一个 AM 管理多个长期运行的服务实例,应用失败时要不要保留已经运行的 Container,是否允许新 AM attempt 接管旧 Container,这些都要提前设计。

清理逻辑还有一个要求:幂等。AM 可能失败重启,清理动作可能执行一半。删除临时文件、释放资源、提交输出都要能承受重复执行。否则一次 AM 重试就可能造成残留目录、重复输出或状态错乱。

3.3 AM 的心跳决定状态可见性

ApplicationMaster 和 ResourceManager 之间不是持续同步的强一致关系。AM 通过心跳向 RM 申请资源、释放资源、接收分配和上报进度。心跳间隔会影响状态变化被 RM 感知的速度。

这能解释一些看似奇怪的页面现象:

现象 可能解释
Container 已结束,页面还显示运行中 状态尚未通过心跳同步
AM 已申请资源,但暂时没有分配 调度器还没有可用资源或下一轮调度未发生
应用失败后诊断信息滞后 AM/NM/RM 状态同步存在时间差
日志已经聚合,但页面仍显示旧链接 Web UI 展示和日志服务状态不同步

这不是说页面不可靠,而是要理解页面是状态投影。YARN 的实际状态分布在 RM、NM、AM 和 HDFS 日志里。排查时如果看到短暂不一致,不要立刻下结论,要结合命令和日志确认。

AM 如果要给用户展示进度,也应该通过心跳或协议上报。MapReduce 能显示 Map/Reduce 进度,是因为 MRAppMaster 持续维护并上报作业内部状态。Distributed-Shell 的进度很简单,复杂框架则需要自己设计进度模型。

3.4 Container 启动命令要减少环境假设

Container 运行在 NodeManager 管理的环境里,不等同于用户登录 shell。启动命令应该尽量减少对交互式环境的假设。

常见问题包括:

  1. 依赖 .bashrc 或 .zshrc 中的变量。
  2. 依赖当前目录下的文件。
  3. 使用节点上不一定存在的命令。
  4. 假设 PATH 包含业务目录。
  5. 在命令里写复杂引号和括号。
  6. 忘记 stdout/stderr 会被重定向。

更稳妥的做法是在启动命令里显式设置必要环境,或者通过 LocalResource 分发脚本,由脚本自己做检查。比如先打印 pwd、hostname、关键环境变量和依赖文件列表。调试阶段多输出几行,通常能节省很多排查时间。

这次验证命令里包含 pwd、ls -la、printenv | sort | head -20,目的就是确认 Container 看到的运行环境。生产应用不一定要保留这么多输出,但 debug 开关应该具备类似能力。

3.5 资源申请不是一次性动作

ApplicationMaster 申请 Container 时,很容易被理解成“告诉 RM 我要几个 Container”。实际运行里更接近一个持续协商过程。AM 会根据应用进度、失败情况和队列资源,不断向 RM 发送请求,也会不断收到分配结果。资源申请、Container 启动、Container 完成这三件事交错发生。

这带来一个重要结论:AM 不能假设所有资源会一次性到位。队列资源紧张时,应用可能长时间停在 ACCEPTED 或只有 AM 在运行;部分 Container 启动后,后续 Container 还在等待;节点异常时,已经分配的 Container 也可能启动失败。AM 的状态机必须能处理这些中间状态。

可以把 AM 内部状态拆成几类:

状态 AM 要做的事
已注册 初始化应用上下文,开始申请资源
等待分配 持续心跳,检查是否超时
已分配 联系 NM 启动 Container
运行中 跟踪 Container 状态和进度
部分失败 判断是否重试或整体失败
完成中 停止多余 Container,注销应用

MapReduce 的 MRAppMaster 比 Distributed-Shell 复杂很多,原因就在这里。它不仅要申请 Container,还要维护 map task、reduce task、attempt、shuffle、commit 等状态。YARN 只提供资源和进程生命周期,应用语义由 AM 自己承担。

3.6 Container 资源和进程资源要对齐

YARN 分配给 Container 的资源,不会自动等于进程内部真正使用的资源。Container 内存是 YARN 看到的边界,JVM 堆、进程堆外内存、子进程、shell 命令都在这个边界里消耗资源。AM 构造启动命令时,必须让这些资源语义对齐。

常见错误是 Container 申请 1024MB,但启动命令给 JVM 配了 -Xmx1024m。这样看似刚好,实际上没有给 Metaspace、线程栈、Native 内存和其它进程留下空间,很容易触发内存超限。更合理的做法是 Container 内存大于 JVM 堆,并留出一定余量。

CPU 也类似。YARN 的 vcore 是调度维度,不等于操作系统强制隔离的全部细节。应用如果在一个 Container 里启动多个高 CPU 线程,可能会影响同节点其它任务。生产环境要结合 cgroups、NodeManager 配置和框架参数一起治理。

这也是为什么 AM 不能只负责“启动命令”。它还要理解自己启动的是什么进程、需要多少资源、失败后是否重试、重试是否会放大资源压力。Container 是调度单位,业务进程是执行实体,两者之间需要 AM 做准确映射。

4. 排查和工程化落点

4.1 日志目录是 AM 排查的主线

YARN 应用失败后,第一反应不应该是登录节点翻随机目录,而应该沿着日志聚合链路定位。一个 Application 会有 AM Container 日志,也会有普通 Container 日志。AM 日志回答“控制器为什么失败或做了什么决策”,普通 Container 日志回答“执行命令为什么失败”。

排查顺序通常是:

  1. 看 RM Application 的 diagnostics。
  2. 看 AM Container 的 stdout/stderr/syslog。
  3. 看失败普通 Container 的 stdout/stderr/syslog。
  4. 对照 AM 记录的资源申请和启动命令。
  5. 必要时再查 NodeManager 本地日志。

如果日志聚合不可用,排查会困难很多。应用结束后,Container 本地日志可能被清理,用户也不一定能登录对应节点。对于长期使用 YARN 的集群,日志聚合不是锦上添花,而是基础运维能力。

Distributed-Shell 的输出虽然很简单,但它展示了日志链路的完整性。普通 Container 输出的 YARN_AM_CONTAINER_20250226 能通过 yarn logs 查到,说明命令确实在 YARN Container 中执行,而不是只在 Client 端打印。

4.2 AM 设计要避免把所有逻辑塞进一个线程

一个可靠的 AM 通常不会把所有事情写在一个顺序流程里。注册 RM、申请资源、处理分配、启动 Container、监听完成、更新进度、处理超时、响应停止请求,这些动作都可能交错出现。简单示例可以顺序写,真正框架需要事件驱动或清晰的状态机。

如果 AM 只是一个大循环,后续会遇到几个问题:

问题 后果
心跳被业务逻辑阻塞 RM 认为 AM 不活跃
启动 Container 串行过慢 资源分配后不能及时使用
完成事件处理滞后 失败重试不及时
清理逻辑分散 应用结束后留下资源残留

因此 AM 代码里要把“和 RM 通信”“和 NM 通信”“业务状态管理”“日志和指标”分开。YARN API 提供的是底层能力,工程质量取决于 AM 怎么组织这些能力。

5. 小结

ApplicationMaster 是 YARN 应用运行时的核心。它向 ResourceManager 注册,申请普通 Container,处理分配结果,联系 NodeManager 启动进程,监控 Container 完成状态,最后向 RM 汇报应用结果。

可以把这套模型压缩成五个角色:RM 是集群级 scheduler 和 allocator,NM 是节点级 executor,AM 是应用级 coordinator,Container 是运行时资源单元,Task 或用户进程才是真正的业务执行单元。

理解 AM 后,Container 也不再只是“一个资源单位”。它有本地资源、环境变量、启动命令、工作目录、日志和完成状态。YARN 把这些细节统一成可调度、可监控、可聚合的运行模型。应用开发者真正要做的,是把自己的业务逻辑放进这套模型里,并在失败时能说清楚问题发生在哪一层。


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