Spark 集群中的队列到底管什么?容量配置是否意味着资源被预留?本文从 Spark on YARN 架构出发,讲清队列层级、容量保障、弹性共享、ACL 租户隔离。
Spark on YARN 整体架构
Spark 负责应用内部的计算,YARN 负责跨应用的资源管理。下图以 cluster 模式为例展示核心组件及其关系:

- ResourceManager 管理整个集群,并由内部 Scheduler 按队列规则分配 Container
- NodeManager 运行在各工作节点上,负责启动和监控 ApplicationMaster、Executor 等 Container
- ApplicationMaster 代表单个 Spark 应用申请 Executor 资源,
cluster模式下同时承载 Driver - Driver 拆分并调度 Task,Executor 负责实际计算
队列处在架构的什么位置
队列位于 ResourceManager 的 Scheduler 中,控制的是不同应用对 YARN Container 的竞争关系:
text
ResourceManager / Scheduler
│
├── 分配首个 Container → NodeManager 启动 ApplicationMaster
│
└── 响应 ApplicationMaster 的资源请求
↓
分配 Executor Container
↓
NodeManager 启动 Executor
资源申请与任务执行的顺序如下:
- ApplicationMaster 自身先运行在 YARN 分配的 AM Container 中
- ApplicationMaster 根据应用需求,向 ResourceManager 申请 Executor Container
- Scheduler 按队列规则检查资源上限,并决定是否为应用分配这些 Container
- 获得分配后,ApplicationMaster 通知相应的 NodeManager 启动 Executor
- Driver 再把 Spark Task 分发给 Executor 执行
因此,队列约束的是整个应用占用的 YARN 资源,包括 ApplicationMaster 和 Executor 使用的 Container;队列不直接调度 Spark Task
需要特别区分 Spark 内部调度与集群资源调度:
| 概念 | 作用范围 | 常见配置 |
|---|---|---|
| YARN 队列 | 多个应用之间的集群资源分配 | spark.yarn.queue、CapacityScheduler 队列配置 |
| Spark 作业调度 | 同一个 SparkContext 内多个 Job 之间的调度 |
spark.scheduler.mode=FIFO/FAIR |
两者都可能出现 FIFO 或 Fair 等词语,但不是同一层的调度
YARN 如何通过队列实现多租户
YARN 多租户通常把团队、业务或环境映射到一棵层级队列树中,例如:
text
root
├── prod 生产任务
│ ├── etl 离线加工
│ └── report 报表任务
└── dev 开发与临时查询
只有叶子队列接收应用,父队列主要负责聚合和约束其子队列的资源
YARN 调度器对比
| 调度器 | 核心方式 | 适用场景 |
|---|---|---|
| FIFO Scheduler | 主要按应用提交顺序调度,治理能力较弱 | 单用户、测试或非常简单的集群 |
| CapacityScheduler | 使用层级队列、容量保障、最大容量、ACL 和弹性共享 | 多团队共享的生产集群 |
| Fair Scheduler | 通过权重和最小份额,让活跃应用或队列趋向公平共享资源 | 已采用 Fair Scheduler 的存量集群 |
生产环境通常重点关注 CapacityScheduler。具体默认值和可用能力受 Hadoop 版本及发行版影响,应以当前集群配置为准
CapacityScheduler 的关键参数
以下参数位于 capacity-scheduler.xml
| 参数 | 含义 |
|---|---|
yarn.scheduler.capacity.<queue-path>.capacity |
队列相对于父队列的保证容量 |
yarn.scheduler.capacity.<queue-path>.maximum-capacity |
队列弹性扩张后的最大容量 |
yarn.scheduler.capacity.<queue-path>.acl_submit_applications |
允许提交应用的用户和用户组 |
yarn.scheduler.capacity.<queue-path>.acl_administer_queue |
允许管理队列及其中应用的用户和用户组 |
yarn.scheduler.capacity.<queue-path>.minimum-user-limit-percent |
根据活跃用户数计算单用户资源限制时使用的下限 |
yarn.scheduler.capacity.<queue-path>.user-limit-factor |
单用户最多可使用队列保证容量的倍数 user-limit-factor=2 的含义不是"每个用户固定获得两倍资源",而是单个用户在资源空闲且其他约束允许时,最多可以使用队列保证容量的 2 倍 |
yarn.scheduler.capacity.<queue-path>.state |
队列状态,可设为 RUNNING 或 STOPPED |
容量配置模式
CapacityScheduler 的四种基础容量配置方式都使用参数:capacity 表示队列的保证容量,或者计算保证容量的依据。
text
yarn.scheduler.capacity.<queue-path>.capacity
限制队列弹性扩张后的资源上限,则统一使用 maximum-capacity:
text
yarn.scheduler.capacity.<queue-path>.maximum-capacity
调度器根据参数值的格式判断容量模式:
| 模式 | capacity 示例 |
含义 | maximum-capacity 写法 |
|---|---|---|---|
| 百分比模式 | 30 |
队列获得父队列 30% 的保证容量,同一父队列下的子队列容量之和必须为 100% | 使用百分比,例如 90 |
| 权重模式 | 2w |
根据该队列权重占所有兄弟队列权重之和的比例计算容量,新增队列时不需要重新计算所有百分比 | 仍使用百分比,不支持"最大权重" |
| 绝对资源模式 | [memory=10240,vcores=12] |
为队列配置固定的内存和 vCore 保证量,集群扩容后不会按比例自动增加 | 使用绝对资源值,例如 [memory=20480,vcores=24] |
| Universal Capacity Vector | [memory=50%,vcores=2w,gpu=1] |
不同资源维度可以分别使用百分比、权重或绝对资源值 | 同样使用混合向量,按资源维度分别设置上限 |
较新的 Hadoop 版本支持 Universal Capacity Vector。它不是新的基础模式,而是允许不同资源维度在同一个 capacity 或 maximum-capacity 参数中混合使用上述三种方式:
-
启用传统的 legacy queue mode 时,同一父队列下的子队列需要使用一致的容量模式
-
关闭后可以更灵活地混合配置。具体能力受 Hadoop 版本及发行版影响,修改前应先确认当前集群版本
xml
<property>
<name>yarn.scheduler.capacity.legacy-queue-mode.enabled</name>
<value>false</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>[memory=50%,vcores=2w,gpu=1]</value>
</property>
该配置表示内存使用父队列的 50%,vCore 按权重 2 分配,并为队列配置 1 个 GPU
一个最小配置示例
示例使用传统的百分比容量模式
下面定义 prod 和 dev 两个队列,保证容量之和为 100%,同时允许 prod 在资源空闲时扩张到整个集群的 90%
xml
<configuration>
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>prod,dev</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>70</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.maximum-capacity</name>
<value>90</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.acl_submit_applications</name>
<value>alice,bob data-prod</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.capacity</name>
<value>30</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.maximum-capacity</name>
<value>100</value>
</property>
</configuration>
下图展示了该配置中的队列层级、租户准入与资源边界:

YARN ACL 的格式是"逗号分隔的用户列表 + 一个空格 + 逗号分隔的用户组列表"。因此,示例中的 alice,bob data-prod 表示允许用户 alice、bob 以及用户组 data-prod
修改基于文件的队列配置后,可执行:
bash
yarn rmadmin -refreshQueues
该命令会重新加载队列 ACL、状态和调度器属性。部分厂商发行版可能通过管理平台或配置中心接管这些配置
如何把任务提交到指定队列
Spark on YARN
推荐在 spark-submit 时显式指定队列:
bash
spark-submit \
--master yarn \
--deploy-mode cluster \
--queue prod \
--class com.example.Main \
app.jar
等价的 Spark 配置项是:
text
spark.yarn.queue=prod
如果没有指定,Spark 默认提交到名为 default 的 YARN 队列
MapReduce on YARN
MapReduce 使用下面的配置项指定队列:
text
mapreduce.job.queuename=prod
可以通过 Hadoop 通用参数、作业配置或代码设置。命令行中 -D 参数的具体位置取决于作业入口是否正确使用了 Hadoop ToolRunner
Hive、Flink 等引擎
Hive、Flink、Spark 和 MapReduce 只要运行在 YARN 上,最终都受 YARN 队列约束,但提交参数由各引擎决定
- Hive 使用 MapReduce 引擎时通常设置
mapreduce.job.queuename - Hive 使用 Tez 引擎时通常设置
tez.queue.name - Spark on YARN 使用
spark.yarn.queue - Flink on YARN 使用对应版本的 YARN 队列提交参数
空闲队列是否占用 CPU 或内存
不占用。队列的容量是调度规则,不是提前创建出来的一块物理 CPU 或内存
| 状态 | 实际资源行为 |
|---|---|
| 队列无应用 | 没有因为该队列启动的 Container,CPU 和内存占用为零 |
| 队列有应用且未超过保证容量 | 调度器按规则为应用分配 Container |
| 其他队列空闲 | 活跃队列可以在 maximum-capacity 等约束内借用空闲资源 |
| 原队列重新产生需求 | 是否主动收回已借出的资源取决于是否启用了抢占策略 |
这里最容易混淆的是"容量保障"和"资源预留":
capacity表示资源紧张时调度器希望保障的份额,不代表空闲时永久占住这些资源maximum-capacity表示队列能扩张到的上限,不代表队列一定会获得这么多资源- 未启用抢占时,借出的资源通常要等已有 Container 释放后才能重新分配
- 启用 CapacityScheduler 抢占策略后,调度器才可能通过回收 Container 逐步恢复容量保障
因此,"集群满载后各队列一定立刻回归保证容量"是不准确的,资源回归速度受抢占配置、自然释放、应用资源请求和调度周期共同影响
Spark on Kubernetes 有没有队列
原生 Kubernetes
Spark on Kubernetes 默认把 Driver 和 Executor 作为 Pod 交给 Kubernetes 调度器。原生 Kubernetes 提供以下治理手段:
- Namespace 用于组织和权限隔离
- ResourceQuota 和 LimitRange 用于限制资源创建与使用
- PriorityClass 和抢占用于表达 Pod 优先级
- Node Selector、Affinity、Taint 和 Toleration 用于控制 Pod 放置位置
这些能力并不直接等价于 YARN 的层级队列。特别是 ResourceQuota 主要负责限制和拒绝超额资源请求,本身不提供完整的作业级排队、公平共享和队列容量借用语义
引入批处理调度组件
如果需要接近 YARN 队列的体验,可以使用额外组件:
| 组件 | 作用 |
|---|---|
| Volcano | 为批处理负载提供队列、优先级、资源预留和 Gang Scheduling 等能力 |
| Apache YuniKorn | 提供层级队列、公平性、最小或最大容量和灵活排序策略 |
| Kueue | 在作业创建 Pod 前执行准入与排队,使用 ClusterQueue、LocalQueue 和 ResourceFlavor 管理配额 |
Apache Spark 从 3.3.0 开始支持把 Volcano 作为自定义 Kubernetes 调度器,也支持对接 Apache YuniKorn。它们都需要单独安装和显式配置,并不是 Spark on Kubernetes 的默认调度器
YARN 队列与 Kubernetes 方案对照
| YARN | Kubernetes 侧近似概念 | 说明 |
|---|---|---|
| 层级队列 | YuniKorn Queue、Kueue ClusterQueue 与 LocalQueue、Volcano Queue | 由额外调度或准入组件提供 |
| 队列容量 | ResourceQuota 或批处理调度器配额 | ResourceQuota 只有硬限制,不等于弹性队列 |
| YARN Container | Pod | 实际承载 CPU、内存等资源的运行单元 |
| Node Label | Node Label、Affinity、Taint | 控制工作负载可使用的节点或资源类型 |
| 应用抢占 | Pod Priority、Preemption 或批处理调度器抢占 | 行为和粒度因调度器而异 |
核心结论
集群中的"队列"不是 MapReduce 或 Spark 执行引擎自身提供的计算能力,而是资源管理器或批处理调度器提供的资源治理能力
- Spark on YARN 和 MapReduce on YARN 的队列由 YARN ResourceManager 中的调度器管理
- 队列是逻辑资源容器,用于定义容量、上限、权限、优先级和应用排序规则
- 空闲队列不启动 Container,因此不会因为"配置了容量"就占用 CPU 或内存
- 空闲队列的容量通常可以被其他队列弹性使用,但是否以及何时收回借出的资源,取决于上限和抢占配置
- Spark on Kubernetes 默认由 Kubernetes 调度 Pod,原生调度机制不等同于 YARN 队列
- Spark on Kubernetes 如需作业排队、公平共享和队列配额,可引入 Volcano、Apache YuniKorn、Kueue 等批处理调度或准入组件
参考资料
- Apache Hadoop CapacityScheduler
- Apache Hadoop ResourceManager REST APIs
- Apache Spark:Running Spark on YARN
- Apache Spark:Running Spark on Kubernetes
- Kubernetes:Introducing Kueue
✨ 微信公众号【凉凉的知识库】同步更新,欢迎关注获取最新最有用的知识 ✨