深入 Hadoop 09 - YARN 调度器对比:FIFO、Capacity、Fair
上一篇讲了资源模型。本篇讲 ResourceManager 怎么把资源分给作业——也就是调度器的选择。
YARN 调度器常被介绍成"Hadoop 集群的资源分配策略"。这个描述对应了功能但没解释设计差异。准确的说法是:YARN 提供三种调度器——FIFO(单队列先进先出)、Capacity(层级队列,每队列有容量保证)、Fair(按权重公平分享),三种调度器在公平性、隔离性、吞吐、抢占代价上各有取舍。生产集群几乎都用 Capacity 或 Fair,FIFO 只在小集群或测试场景出现。
本篇只抓一个问题:三种调度器各自的设计取舍、什么场景应该选哪种、抢占机制是怎么工作的。
FIFO Scheduler:最简单的策略
FIFO 是 YARN 默认调度器里最简单的版本。所有作业排成一个队列,按提交顺序依次获得资源:
1 | |
FIFO 的优势是实现简单,调度器计算复杂度低(O(1) 检查队头)。劣势是单作业可以垄断集群——App-A 跑 90 秒里 App-B 完全等待。短作业(几秒的查询)被长作业(几小时的 ETL)阻塞,响应时间不可预测。
FIFO 只在小集群(单租户、作业间不冲突)、测试场景(开发同学本地集群)出现。生产集群即使只有一个团队也用 Capacity 替代——Capacity 的单根队列行为与 FIFO 类似,但保留了未来扩展为多队列的灵活性。
Capacity Scheduler:层级队列与容量保证
Capacity Scheduler(CS)是 Hadoop 默认调度器,几乎所有生产集群都用。核心思想是把集群资源切成多个队列,每个队列有保证的最小容量(capacity),最多可以用到某个上限(maxCapacity)。队列可以嵌套,形成树形层级。
1 | |
capacity 是该队列的"保证资源"——只要整个集群总资源够,这个队列一定能拿到这么多。maxCapacity 是该队列的最大上限——即使集群空闲,这个队列也不能用超过这个值。
注意 capacity 的相对性。root.prod.etl 的 capacity=70 是相对 root.prod 的——root.prod.etl 在 root.prod 这 60% 配额里占 70%。绝对容量是 60% × 70% = 42%。
Capacity Scheduler 的核心机制是"弹性借用"。root.prod 队列 capacity=60 表示它保底能拿 60% 集群资源。但如果集群总共有 100% 资源而其他队列空闲,root.prod 可以借用到 maxCapacity=80%。借用不是永久的——当其他队列(例如 root.dev)有新作业提交,CS 会立刻把借走的资源还回来。
弹性借用的具体行为:
1 | |
抢占是 Capacity Scheduler 强制容量保证的最后一招。默认配置下抢占是延迟的——CS 先等"被借方"自然释放(container 自然结束),只有等了抢占等待时间(yarn.resourcemanager.scheduler.monitor.policies.preemption.max_ignored_over_ending_apps 等配置)之后才主动抢占。
Capacity Scheduler 的层级结构让多租户集群的资源分配可以精细管理——ETL 队列保证 70%、ML 队列保证 30%、开发测试队列保证若干,互不挤占。生产集群的典型配置是按业务线 / 团队切分顶级队列,每个团队内部再按作业类型切子队列。
Fair Scheduler:公平分享与抢占
Fair Scheduler(FS)的设计目标是"每个作业获得相等的资源份额"。当集群里同时有 N 个作业时,每个作业在公平分配下应该拿到 1/N 的资源。
1 | |
公平分享的实现需要抢占——App-A 已经在跑的 container 必须被强制释放,让出 8 GB 给 App-D。这种抢占对作业有影响——container 重启的开销由作业承担。
FS 的权重和最小资源:
1 | |
weight 决定公平分配的权重——prod 权重 2.0、dev 权重 1.0,所以集群资源按 2:1 分配。minResources 是该队列的保底(类似 CS 的 capacity),maxResources 是上限。
FS 也支持层级队列(Hadoop 2.6+),但层级语义不如 CS 那么严格——FS 的层级主要影响权重继承,不像 CS 那样强制容量保证。
Fair Scheduler 适合"短作业 + 多租户"场景——交互式查询(Hive / Presto / Spark SQL)、Ad-hoc 分析、机器学习实验。这些作业短(秒到分钟)、用户多(几十到几百)、对响应时间敏感。FS 让每个作业快速获得公平份额,不被长作业阻塞。
Capacity Scheduler 适合"长作业 + 业务线划分"场景——批量 ETL、定期报表、流式计算。这些作业长(小时到天)、业务隔离严格、对资源保证敏感。CS 的容量保证让每个业务线有确定的资源份额。
抢占机制对比
两种调度器都支持抢占,但策略不同:
1 | |
Capacity Scheduler 的抢占需要显式启用:
1 | |
Fair Scheduler 的抢占默认开启,通过 yarn.scheduler.fair.preemption 控制。
抢占的代价是 container 重启。AM 收到 container 被抢占的通知后必须重试 task——MapReduce 重跑 map task、Spark 重跑 stage、Flink 重启 task。这个开销在某些场景下显著(Flink 状态恢复可能几十秒)。所以抢占在长作业 + 状态密集型作业场景应该谨慎开启。
注意一点:YARN 的抢占与 MapReduce 的 Speculative Execution 是两个机制——Speculative 是 AM 主动启动备份 task 对抗慢节点,抢占是 RM 强制释放 container 重新分配。两者都涉及"task 重跑",但触发条件和目标不同。
调度策略的精细配置
无论 CS 还是 FS,生产部署都涉及大量精细配置。这里列举几个关键项:
Capacity Scheduler 的关键配置:
1 | |
maximum-am-resource-percent 是一个容易被忽视但很关键的配置。AM 自己是 container,占用集群资源。如果 AM 占用资源没限制,集群可能被 AM 占满,没资源跑实际 task。默认 10% 意味着集群 10% 资源留给所有 AM,剩下 90% 给 task。
Fair Scheduler 的关键配置:
1 | |
user-as-default-queue 让每个用户默认有自己的队列,所有作业按用户公平分享。这对多用户交互式分析场景很合适——每个用户都拿到集群的 1/N 资源。
assignmultiple 与 max.assign 控制 RM 在 NM 心跳时一次分配几个 container。默认配置下 RM 每次心跳只分一个 container,大型集群(几千节点)调度吞吐不足。开启这两个配置可以让 RM 单次心跳分配多个 container,吞吐提升数倍。
实验:观察调度器行为
提交多个作业到不同队列,观察 RM Web UI 上 Scheduler 标签页:
Capacity Scheduler 部署的集群,Scheduler 标签页展示层级队列树:
1 | |
这个输出展示了几个事实:
集群总资源 100% 配置,实际用了 45%。
prod 队列保底 60%,实际用了 30%——没用满保底,不需要抢占。
dev.etl 用了 30% 但 dev 总配额只有 40%,说明 dev.etl 借了 dev.ad 的部分容量(dev.ad 没作业)。
prod.etl 用了 50% 但保底只有 42%(60% × 70%)——借用其他队列资源 8%。
如果某队列超出 maxCapacity,会触发抢占等待。抢占的实际发生可以通过 ApplicationMaster 的 task 失败日志看到——task 状态变成 PREEMPTED。
Fair Scheduler 部署的集群,Scheduler 标签页展示每个队列的 fairShare:
1 | |
Fair Share 是动态计算的——根据当前活跃作业数和权重,每个队列在不同时刻拿到不同 fairShare。这与 CS 的 capacity(静态配置)形成对比。
命令行层面,yarn queue -status prod 展示某个队列的当前状态:
1 | |
输出该队列的当前 capacity、used、maxCapacity、活跃 container 数、活跃 application 数。
模式提炼
YARN 调度器体现的设计模式:
1 | |
这个模式不只是 YARN。Mesos 的 DRF(Dominant Resource Fairness)调度器是 FS 的多维资源扩展版本。Borg 的"ELOQ 算法 + 优先级"调度器是 CS 的另一种实现。Kubernetes 的 PriorityClass + ResourceQuota + LimitRange 三件套是 CS + FS 的功能合并——PriorityClass 表达作业优先级,ResourceQuota 表达队列容量保证,LimitRange 表达最小粒度。
数据库领域的对应物是"工作负载管理"(WLM)。Snowflake 的 multi-cluster warehouse、AWS Redshift 的 query queue、Oracle 的 Resource Manager 都用类似机制——把集群资源切成多个池,每个池有容量保证,作业提交到对应池。差别是数据库的资源单位通常是 query concurrency(并发查询数),不是 CPU/Memory 向量。
工程迁移表
| YARN 概念 | Mesos | Kubernetes | Borg | Snowflake WLM |
|---|---|---|---|---|
| 层级队列 | role | namespace + ResourceQuota | job class | warehouse |
| 容量保证 | resource guarantee | ResourceQuota hard | min alloc | min concurrency |
| 弹性借用 | dynamic reservation | LimitRange + borrowing | elastic | auto-scale |
| 抢占 | offer withdrawal | PriorityClass preemption | kill | queue wait |
| 公平算法 | DRF | weighted fair | ELOQ | priority + queue |
| ACL | framework ACL | RBAC | policy | grant |
注意 Kubernetes 这一列的差异。Kubernetes 用 namespace + ResourceQuota + PriorityClass 组合实现类似 CS 的功能,但语义不如 CS 严格——ResourceQuota 是 namespace 级别的总量限制,不像 CS 队列那样有"保底 + 借用"两套机制。Kubernetes 的设计假设是"多个团队共享集群,按 namespace 隔离",CS 的假设是"多业务线共享集群,按队列分配"——前者强调隔离,后者强调配额保证。
常见误解
误解一:“Capacity Scheduler 比 Fair Scheduler 慢”。两种调度器的调度吞吐主要取决于 assignmultiple 等配置,不取决于调度器选型。CS 的层级队列计算比 FS 的 fairShare 计算略复杂,但在生产集群规模下差异不显著(毫秒级)。选 CS 还是 FS 应该看场景——多业务线配额管理选 CS,多用户交互分析选 FS。
误解二:“公平分享意味着每个作业拿到相等资源”。FS 的公平分享基于权重,不是绝对相等。weight=2.0 的作业拿到的资源是 weight=1.0 的两倍。配置权重后,"公平"是按权重比例分配,不是按作业数均分。
误解三:“抢占是无害的”。抢占让被抢占作业的 container 重启,作业延迟增加。Flink 状态恢复可能几十秒,Spark stage 重跑可能几分钟。生产环境应该谨慎开启抢占——只在确实需要保证配额的场景开,业务线之间能容忍延迟的就不开。
误解四:“FIFO 调度器没有用”。FIFO 在单租户开发集群、测试场景仍然有用——配置简单,调试方便。生产集群几乎不用 FIFO,但理解 FIFO 的行为有助于理解 CS 和 FS 的设计动机。
误解五:“Capacity 调度器一旦配置就不能动态调整”。Capacity Scheduler 支持运行时动态调整——yarn rmadmin -refreshQueues 命令重新加载队列配置。这让运维可以动态增加队列容量、调整 maxCapacity,不需要重启 RM。Fair Scheduler 同样支持动态刷新。
练习
-
在 Hadoop 集群上运行
yarn queue -status root和yarn scheduler -list,观察当前队列树和调度器类型。提交两个作业到不同队列,观察 RM Web UI 上 Scheduler 标签页的队列资源使用变化。 -
在
apache/hadoop源码里找到CapacityScheduler.java、FairScheduler.java、LeafQueue.java、FSAppAttempt.java(hadoop-yarn-server-resourcemanager 模块),观察两种调度器的核心分配循环。 -
配置一个测试集群的 Capacity Scheduler 队列树(root.dev、root.prod),让 dev 队列 maxCapacity=30%。提交一个大作业到 dev 队列,观察作业是否被限制在 30% 资源以内。然后修改 dev 队列 maxCapacity=50%,刷新配置,观察作业是否立刻获得更多资源。
-
思考题:如果让 YARN 同时支持 Capacity 和 Fair(不是单独选一种,而是混合),调度器的设计需要怎么改造?这种"混合调度器"在哪些场景有价值?
系列导航
| 序号 | 主题 | 状态 |
|---|---|---|
| 00-06 | HDFS 存储层(00 导读 / 01-06 HDFS 各主题) | 第一阶段(已完成) |
| 07 | YARN 架构:ResourceManager、NodeManager、ApplicationMaster 的三方契约 | |
| 08 | 资源模型:Resource、Container 与 NodeLabel | 上一篇 |
| 09 | 调度器对比:FIFO、Capacity、Fair 的设计取舍 | 本篇 |
| 10 | 应用程序生命周期:提交、调度、启动、运行、完成 | 下一篇 |
| 11 | YARN HA 与 Federation | |
| 12 | YARN Timeline Service v2:通用的应用历史与指标 |
参考资料
- Vinod Kumar Vavilapalli et al. Apache Hadoop YARN: Yet Another Resource Negotiator. SOCC 2013. Section 4.4 “Schedulers” 描述了 FIFO / Capacity / Fair 的早期设计。
- Matei Zaharia, Dhruba Borthakur. Fair Scheduler for Hadoop.(Facebook 工程博客,2010 年 Fair Scheduler 设计动机)
- Apache Hadoop 官方文档:Capacity Scheduler. https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/CapacityScheduler.html
- Apache Hadoop 官方文档:Fair Scheduler. https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html
- Apache Hadoop 源码:
CapacityScheduler.java、FairScheduler.java、ProportionalCapacityPreemptionPolicy.java. https://github.com/apache/hadoop - Tom White. Hadoop: The Definitive Guide. O’Reilly, 4th Edition 2015. Chapter 4 详细对比了 Capacity 和 Fair 调度器。
