4.1 YARN 架构与组件 本节摘要:YARN 由 ResourceManager(全局资源分配)、NodeManager(单机容器执行)、AppMaster(每应用一个的协调者)、Container(资源计量与隔离单元)组成。本节用一次任务启动的完整握手串起四角色,解释容器的一生,并从 MapReduce 1 的局限推导出资源与计算分离的架构动机。 一台资源交易所的比喻开场 把 YaaS(Yet Another Resource Negotiator,这名字直译"又一个资源协商者",透着工程师的自嘲)想成一座算力交易所。ResourceManager 是交易所本身:掌握全市场(集群)的库存——每台机器多少内存、多少 vCore——并按规则撮合。
本节摘要:YARN 由 ResourceManager(全局资源分配)、NodeManager(单机容器执行)、AppMaster(每应用一个的协调者)、Container(资源计量与隔离单元)组成。本节用一次任务启动的完整握手串起四角色,解释容器的一生,并从 MapReduce 1 的局限推导出资源与计算分离的架构动机。
把 YaaS(Yet Another Resource Negotiator,这名字直译"又一个资源协商者",透着工程师的自嘲)想成一座算力交易所。ResourceManager 是交易所本身:掌握全市场(集群)的库存——每台机器多少内存、多少 vCore——并按规则撮合。NodeManager 是驻场商户代表:看管自己那台机器,按交易所指令开辟摊位(容器)、监督摊位运行、超时就清场。AppMaster 是每个买家雇的采购代理:替这个应用向交易所要摊位、再把具体任务派进摊位。Container 就是摊位本身:一块有边界的内存加 CPU 配额,任务在里面跑,跑完归还。
四角色中容易混淆的是 AppMaster 与 ResourceManager 的分工。一句话分清:RM 管全局、管别人;AppMaster 管自己、管任务。RM 不知道你的作业有几个 Map 几个 Reduce,它只看到一串"给我一个 4GB、2核的容器,最好在 node7 上"的请求;AppMaster 不知道全集群还剩多少资源,它只知道自己还差几个容器。这个解耦让任意框架都能接入——只要会写"申请容器"的协议。

容器是 YARN 世界的基本货币,值得单独追踪它的状态迁移;理解了它的迁移,后面调度器的一切讨论才有着落。申请:AppMaster 向 RM 发出 ResourceRequest——描述内存量、vCore 数、优先级和可选的偏好节点(第 3 章的本地性提示就是挂在这里)。分配:调度器把配额记账到某 NodeManager 名下,容器状态 ALLOCATED,此刻资源已被锁定但进程未起。启动:AppMaster 拿到分配凭证后与对应 NodeManager 通信,NM 在本地拉起进程(写启动脚本、设环境、挂 cgroup),容器 RUNNING。结束:任务完成或失败,或 AppMaster 主动释放,容器 COMPLETE,配额归还。
隔离规则要记住一对不对称:内存是硬限制,CPU 是软限制。任务内存超配额,NodeManager 直接杀进程(日志里的 "running beyond physical memory limits");CPU 超用则只是被 cgroups 降权,不会死。因此容量规划时内存必须留余量,CPU 反而可以适度超卖——多数集群 CPU 平均利用率低,超卖是常态。
YARN 为什么要存在?看它的前身就明白。MapReduce 1 时代,JobTracker 一个进程身兼两职:作业管理(切分、监控、重试)加资源管理(把 map/reduce 槽位分给各 TaskTracker)。两个致命缺陷:
其一,槽位模型僵化。资源被预切成 map 槽与 reduce 槽两类,map 槽闲着时 reduce 也用不上;内存 CPU 更是无法按需表达。其二,扩展性天花板。JobTracker 既管作业又管任务心跳,4000 节点、同时跑几千作业就到极限。加上新框架(流式、图计算、SQL 引擎)都想用集群,却没有第二条路可走——要么伪装成 MR 作业,要么自建资源层。
YARN 的手术很干净:把资源管理从 JobTracker 拆出升格为 ResourceManager;把作业管理下放为每应用一个的 AppMaster;把槽位换成通用的内存加 vCore 容器。手术完成后,Spark、Tez、Storm、Hive 各框架都以普通 YARN 应用的身份公平入驻。这不是功能升级,而是职责的重新分层——与第 1 章"数据面与控制面分离"、第 2 章"元数据与数据分离"一脉相承,Hadoop 的每次重大演进几乎都是一次分层手术。
四角色的抽象落到机器上,是一笔很具体的账。一台 256 GB 内存、64 vCore 的 worker 节点,操作系统与页缓存留 16 GB,DataNode 进程与堆外缓冲留 24 GB,若还跑 RegionServer 再留 48 GB——NodeManager 实际可分配的可能只有一百六七十 GB。按单容器默认 2 GB 算,这台机器同时容纳八十多个任务容器。把这笔账乘到一百台,就是整个集群的"容器池"规模:八千个并发任务槽位。 ResourceManager 的调度器每秒要处理的心跳与分配决策也是千级,这就是 4.1 开头说"调度吞吐是硬指标"的由来。反过来读这笔账也成立:如果你发现集群明明还有内存却总说资源不足,第一件事是核对每个节点的可分配量配置与实际预留是否一致——配置里的数字与机器上真正交出来的数字,中间隔着所有共置进程的开销。
把图里的六步握手按时间走一遍,建立具体的时间感。客户端提交后,ResourceManager 接纳应用、在其队列上排入一个"待启动 AppMaster"的请求,通常几秒内拿到首个容器;AppMaster 启动、注册、发出第一批资源请求又是几秒。之后进入稳态循环:NodeManager 一到两秒一次心跳上供资源,调度器逐次分配,AppMaster 半秒到一秒一次心跳领走分配并驱动 NodeManager 拉起任务——每个任务从"决定调度"到"进程起来"典型耗时五到十秒,其中 JVM 启动占大头。这解释了两件事:为什么小任务在 YARN 上"起跑就慢半拍"(容器启动开销与任务时长无关,短任务被启动成本稀释),以及为什么重用执行器的引擎(Spark on YARN 的 cluster 模式、Tez session)对交互式查询至关重要——容器一次申请、反复复用,把启动开销摊薄到接近于零。读懂这段时序,作业排队与启动慢的排障就有了秒表,集群行为的时间颗粒度也就不再神秘。
四角色的容错等级不同,排障时先定位角色:NodeManager 挂——RM 标记失联,其上容器全部宣告死亡,相关 AppMaster 收到通知后重试任务(第 3 章 Map 重跑的基础);AppMaster 挂——RM 按重试次数(默认 1,生产常调大)重建它,应用内已完成任务不重算;ResourceManager 挂——全部应用暂停,靠基于 ZooKeeper 或 LevelDB 状态存储的 RM Restart 恢复。RM 是最后一个单点,重启窗口内集群"暂停但数据无损",多数平台用主备 RM(自动故障转移)把这个窗口压到分钟级。
角色认清后,下一节进入 YARN 最有实践味道的部分:调度器与队列——资源到底按什么规则分给谁。