4.2 YARN 资源调度与容器分配 本节摘要:YARN 的心跳式分配协议决定"资源如何从 NodeManager 流向应用";调度器决定"先分给谁"。本节拆解三种调度器(FIFO、Capacity、Fair)的语义差异,给出 Capacity 多级队列的完整配置实战、Fair 的动态池与抢占机制,以及生产队列规划的判断标准。 心跳式分配:资源是"推"给调度器的 先补齐协议细节。NodeManager 通过心跳向 RM 汇报:"我这台有 8GB 空闲、4 个 vCore"。调度器此刻手里握着一批应用的请求队列,做一次撮合:按调度器规则挑一个应用的请求,把这台机器的资源记到它名下。下一轮 AppMaster 心跳时领走分配结果。
本节摘要:YARN 的心跳式分配协议决定"资源如何从 NodeManager 流向应用";调度器决定"先分给谁"。本节拆解三种调度器(FIFO、Capacity、Fair)的语义差异,给出 Capacity 多级队列的完整配置实战、Fair 的动态池与抢占机制,以及生产队列规划的判断标准。
先补齐协议细节。NodeManager 通过心跳向 RM 汇报:"我这台有 8GB 空闲、4 个 vCore"。调度器此刻手里握着一批应用的请求队列,做一次撮合:按调度器规则挑一个应用的请求,把这台机器的资源记到它名下。下一轮 AppMaster 心跳时领走分配结果。要点是调度器不主动找节点,节点心跳到达时才决策——资源分配的粒度与节奏由心跳驱动,这也是为什么节点上千时 RM 的调度吞吐(每秒可完成数千次容器分配)是硬指标。
请求本身可以带三个约束:优先级(先满足谁)、资源量(内存与 vCore)、位置偏好(节点名 → 机架 → 任意,放宽有超时惩罚——久等不满足的请求会被强制放宽到任意节点,防止资源空转,这正是第 3 章"跨架任务最后才出现"的机制)。
FIFO:所有应用排一条队,先来先服务。简单、吞吐高,但一个大报表作业能把后面所有交互式查询堵死——除了极小集群,生产基本不用,价值在于作为理解基线。
Capacity Scheduler(Hadoop 默认):把集群切成多个队列,每个队列保底容量 + 上限。保底是该队列任何时刻可独占的份额,上限(maximum-capacity)防止它吃掉别人。队列空闲时资源可临时借给别人用,等本队列来需求时再逐步收回(不是立刻抢占,而是新分配不再外借)。
Fair Scheduler:同样是队列(池),语义换成"在活跃应用间公平分享"。默认每个池内所有运行中的应用均分池资源;池间可设置权重。需要时支持抢占:等不及的应用可以从超配者那里杀死部分容器强行收回资源。
| 维度 | Capacity | Fair |
|---|---|---|
| 资源语义 | 队列容量定额 | 公平份额动态计算 |
| 队列结构 | 多级子队列 | 层次池 |
| 抢占 | 可配置 | 原生支持更成熟 |
| 适合 | 组织边界清晰的多租户 | 混合负载延迟敏感 |
我的经验倾向:组织租户多、层级深 → Capacity;同一平台混跑长批处理与短交互查询 → Fair。两者能力早已互相靠拢,选型差异没有传言那么大,真正的功夫在队列规划。
一个典型数仓场景:三条业务线(etl、adhoc、realtime)+ 运维保留队列。配置文件 capacity-scheduler.xml:
<property> <name>yarn.scheduler.capacity.root.queues</name> <value>etl,adhoc,realtime,ops</value> </property> <!-- etl:夜批主力 保底50% 可弹性到70% --> <property> <name>yarn.scheduler.capacity.root.etl.capacity</name> <value>50</value> </property> <property> <name>yarn.scheduler.capacity.root.etl.maximum-capacity</name> <value>70</value> </property> <!-- 子队列:etl下再分关键链路与补数 --> <property> <name>yarn.scheduler.capacity.root.etl.queues</name> <value>critical,backfill</value> </property> <property> <name>yarn.scheduler.capacity.root.etl.critical.capacity</name> <value>70</value> </property> <!-- adhoc:分析师交互查询 保底30% 但单应用限流 --> <property> <name>yarn.scheduler.capacity.root.adhoc.capacity</name> <value>30</value> </property> <property> <name>yarn.scheduler.capacity.root.adhoc.maximum-applications</name> <value>20</value> <!-- 防一人开百个查询占满队列 --> </property> <property> <name>yarn.scheduler.capacity.root.adhoc.user-limit-factor</name> <value>2</value> <!-- 单用户最多用到队列容量的2倍名义份额 --> </property> <!-- realtime:保底15% 可被抢占保障 --> <property> <name>yarn.scheduler.capacity.root.realtime.capacity</name> <value>15</value> </property> <property> <name>yarn.scheduler.capacity.root.realtime.maximum-capacity</name> <value>25</value> </property>
几条规划心法。容量之和等于 100:保底之和必须精确为 100,否则配置校验失败(新人最常踩的坑)。上限留弹性:capacity=50、maximum=70 的组合意味着"平时半数、忙时可借用"。**user-limit-factor 防单人洪水**:adhoc 队列不设的话,一个分析师循环提交 200 个查询就能把同事全堵死。**ACL 按 队列控制提交与管理权限**,与第 6 章的 Kerberos 身份打通。
提交与验证:
yarn application -submit -queue etl.critical ... # 显式指定队列 yarn queue -status etl # 典型输出: # Queue Information: # Current Capacity : 0.62 ← 正在弹性区间内 # Maximum Capacity : 0.70 # Applications : 8
公平分配的分配文件 allocation.xml:
<allocations> <queue name="prod"> <minResources>51200 mb, 25 vcores</minResources> <weight>3.0</weight> <!-- 权重3:与默认队列竞争时占3份 --> </queue> <queue name="sandbox"> <maxRunningApps>10</maxRunningApps> <weight>1.0</weight> </queue> <queueMaxAMShareDefault>0.5</queueMaxAMShareDefault> <!-- 抢占:缺额超半份且等了60秒即动手 --> <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout> <fairSharePreemptionThreshold>0.5</fairSharePreemptionThreshold> </allocations>
抢占的杀伤链:sandbox 借用了 prod 的资源,prod 新应用来了 → 低于公平份额 → 等 60 秒没自然归还 → RM 发出 preempt 通知,AppMaster 优雅收缩(优先杀未完成的 Map,第 3 章的容错语义保证损失可控)→ 超时未执行则强杀。抢占是"温和收租",不是"破门而入"——它给了应用保住进度的机会。即便如此,抢占要节制:阈值过敏感会让集群在批处理与交互负载间来回震荡,形成"资源乒乓"。
结合 4.1 的资源账与本章语义,生产队列规划可以收敛成三条:
按超时敏感度分层,不按部门平分。夜批 T+1 晚半小时无感,交互查询慢 30 秒就会被投诉。队列的容量差异应映射 SLA 差异,而不是组织架构差异——后者用 ACL 表达归属就够了。
AM 资源单独设闸。每个应用要先占一个容器跑 AppMaster。大量小应用会把 AM 堆满队列(默认 AM 最多占队列 50%),表现为"队列有资源但新作业卡在 ACCEPTED 不跑"。maxAMShare 调到 0.3–0.5 之间是常见解。
弹性靠 maximum-capacity,保底靠 capacity,公平靠权重。三个参数各管一段:保底给确定性、弹性给利用率、权重给优先级。只调一个参数解决不了三个问题。
调度器就位后,下一节站到开发者视角:怎么把一个自己的应用跑上 YARN,以及标签调度等进阶玩法。