大数据系列(六) YARN:集群资源调度大管家
YARN:集群资源调度"大管家"
大数据系列第 6 篇:Spark 和 Flink 要跑起来,得有人给它们分配资源。YARN 就是这个"大管家"。
从一个"抢资源"的故事说起
假设你们公司有 100 台机器组成的大数据集群,同时有几个团队在用:
- 数据仓库团队:每天晚上跑批处理作业,把原始数据清洗成报表
- 算法团队:白天跑机器学习模型训练,需要大量 GPU 资源
- 实时计算团队:7×24 小时跑 Flink 作业,处理实时数据流
- 临时查询团队:分析师们时不时提交个 Ad-hoc 查询
问题来了:这 100 台机器怎么分?
如果没有统一的管理,可能就是"谁抢到算谁的":
- 数据仓库晚上一跑,占了 80% 的资源,实时计算直接卡死
- 算法团队提交了个大作业,把内存全吃光了,其他作业全 OOM
- 有人写了个死循环,CPU 跑满,整个集群瘫痪
这时候就需要一个"大管家"来统筹资源分配——YARN 就是这个角色。
YARN 是什么?解决了什么问题?
YARN(Yet Another Resource Negotiator)是 Hadoop 2.x 引入的集群资源管理系统。
在 Hadoop 1.x 时代,MapReduce 自己管资源、自己跑计算,JobTracker 既是"资源调度员"又是"作业监督员"。结果:
- 集群规模大了(超过 4000 台),JobTracker 扛不住
- 只能跑 MapReduce,Spark、Storm 等其他框架没法用这套集群
- 资源隔离做得差,一个作业把资源吃光,其他作业全挂
YARN 的做法是:把"资源管理"和"作业执行"彻底分开。
┌─────────────────────────────────────────────────────────────────┐ │ YARN 的设计思想 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Hadoop 1.x(紧耦合): │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ JobTracker │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ 资源管理 │ │ 作业调度 │ │ │ │ │ │ + 任务监控 │ │ + 任务分配 │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ (一个人干所有活,累死) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ Hadoop 2.x + YARN(解耦): │ │ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ ResourceManager │ │ ApplicationMaster │ │ │ │ (资源大管家) │ │ (作业小管家) │ │ │ │ │ │ │ │ │ │ • 管全局资源 │ │ • 向 RM 申请资源 │ │ │ │ • 分配 Container│ │ • 和 NM 一起跑 Task │ │ │ │ • 不 care 你跑啥│ │ • 监控 Task 状态 │ │ │ └─────────────────┘ │ • 失败了自己申请重试 │ │ │ └─────────────────────────────┘ │ │ │ │ 好处: │ │ • RM 只管资源,不管业务逻辑,职责清晰 │ │ • 不同框架(MapReduce/Spark/Flink)都有自己的 AM │ │ • 一套集群跑多种框架,资源统一调度 │ │ │ └─────────────────────────────────────────────────────────────────┘YARN 的核心组件
YARN 的架构就三种角色,咱们一个个聊:
ResourceManager(RM):资源大管家
RM 是整个集群的"一把手",负责:
- 接收作业提交
- 根据调度策略,把资源分配给各个应用
- 监控 NodeManager 的健康状态
RM 内部有两个关键模块:
Scheduler(调度器):纯资源分配器,只负责"把资源给谁",不跟踪应用的状态。它是可插拔的,有三种实现(后面细说)。
ApplicationsManager(应用管理器):负责接收作业、启动第一个 Container 来跑 ApplicationMaster,以及 AM 挂了之后的重启。
NodeManager(NM):节点小管家
每台工作机器上都有一个 NM,负责:
- 管理这台机器的资源(CPU、内存、磁盘)
- 启动和监控 Container(资源容器)
- 定期向 RM 汇报:“我还活着,资源用了多少”
ApplicationMaster(AM):作业小管家
每个应用(Job)都有自己的 AM,负责:
- 向 RM 申请资源(“我需要 10 个 4G 内存的 Container”)
- 和 NM 协作,在申请到的 Container 里启动 Task
- 监控 Task 的执行,失败了申请新资源重试
不同框架有自己的 AM 实现:
- MapReduce → MRAppMaster
- Spark → Spark Driver(内部实现了 AM 接口)
- Flink → JobManager(在 YARN 模式下就是 AM)
Container:资源容器
Container 是 YARN 的资源抽象,封装了:
- 多少内存(MB)
- 多少 CPU 核(vCores)
- 运行在哪个 NodeManager 上
你可以把 Container 理解成一个"资源包",RM 把这个包分配给 AM,AM 在这个包里跑自己的 Task。
作业提交流程:一个 Spark 作业是怎么跑起来的?
┌─────────────────────────────────────────────────────────────────┐ │ YARN 上跑 Spark 作业的流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 你(客户端) │ │ │ │ │ │ 1. 提交作业:spark-submit --master yarn ... │ │ ▼ │ │ ResourceManager │ │ │ │ │ │ 2. RM 找个空闲的 NodeManager │ │ ▼ │ │ NodeManager X │ │ │ │ │ │ 3. 在这个 NM 上启动 ApplicationMaster │ │ │ (Spark Driver 就是 AM) │ │ ▼ │ │ ApplicationMaster(Spark Driver) │ │ │ │ │ │ 4. AM 向 RM 申请资源:"我需要 10 个 Executor" │ │ ▼ │ │ ResourceManager │ │ │ │ │ │ 5. RM 根据调度策略,分配 10 个 Container │ │ ▼ │ │ NodeManager A ──→ 启动 Container 1 ──→ 运行 Spark Executor │ │ NodeManager B ──→ 启动 Container 2 ──→ 运行 Spark Executor │ │ NodeManager C ──→ 启动 Container 3 ──→ 运行 Spark Executor │ │ ... │ │ NodeManager J ──→ 启动 Container 10 ──→ 运行 Spark Executor │ │ │ │ │ │ 6. Spark Driver 把 Task 分发给各个 Executor 执行 │ │ ▼ │ │ 作业运行中... │ │ │ │ │ │ 7. Executor 定期向 Driver 汇报进度 │ │ │ Driver 定期向 RM 汇报心跳 │ │ ▼ │ │ 作业完成,Container 释放,资源归还集群 │ │ │ └─────────────────────────────────────────────────────────────────┘关键点:
- 双层调度:RM 把资源分配给 AM,AM 再决定怎么用这些资源
- AM 是应用级别的:每个作业有自己的 AM,互不干扰
- Container 是临时资源:作业完成后,Container 被回收,资源释放
三种调度器:资源怎么分才公平?
YARN 提供了三种调度器,适用于不同的场景:
FIFO Scheduler:先来后到
最简单的调度器,作业按提交顺序排队,前一个跑完了才跑下一个。
队列:Job1 ──→ Job2 ──→ Job3 ──→ Job4 │ ▼ 资源:███████████████████████████████████████████████ Job1 独占所有资源 特点: • 简单,不用配置 • 大作业阻塞小作业 • 不适合多租户 • 生产环境基本不用问题很明显:如果 Job1 是个跑 6 小时的大作业,Job2、Job3、Job4 就得干等着。这在多团队共享的集群里显然不可接受。
Capacity Scheduler:按容量分配
这是 CDH(Cloudera)的默认调度器。思路是:把集群资源划分成多个队列,每个队列有固定的容量,队列之间资源隔离。
┌─────────────────────────────────────────────────────────────────┐ │ Capacity Scheduler(容量调度器) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 队列结构: │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Root(根队列) │ │ │ │ (100% 集群资源) │ │ │ ├────────────────────┬────────────────────┬───────────────┤ │ │ │ Production │ Development │ Test │ │ │ │ (生产队列) │ (开发队列) │ (测试队列) │ │ │ │ 60% 容量 │ 30% 容量 │ 10% 容量 │ │ │ │ │ │ │ │ │ │ ┌─────┐┌─────┐ │ ┌─────┐┌─────┐ │ ┌─────┐ │ │ │ │ │ETL ││报表 │ │ │实验A││实验B│ │ │单元测试│ │ │ │ │ │团队 ││团队 │ │ │ ││ │ │ │ │ │ │ │ │ └─────┘└─────┘ │ └─────┘└─────┘ │ └─────┘ │ │ │ └────────────────────┴────────────────────┴───────────────┘ │ │ │ │ 关键特性: │ │ • 容量保证:每个队列至少获得配置的容量 │ │ • 弹性共享:本队列空闲时,资源可被其他队列借用 │ │ • 最大容量限制:防止单个队列独占集群 │ │ • 用户限制:单个用户不能用光整个队列的资源 │ │ • ACL 权限控制:谁能往哪个队列提交作业 │ │ │ │ 配置示例: │ │ production 队列:60% 容量,最大可用 80% │ │ development 队列:30% 容量,最大可用 50% │ │ test 队列:10% 容量,最大可用 20% │ │ │ └─────────────────────────────────────────────────────────────────┘Capacity Scheduler 适合企业环境:不同部门/团队有自己的资源配额,保证核心业务(生产队列)有资源可用,同时允许非高峰期借用空闲资源。
Fair Scheduler:公平共享
这是 HDP(Hortonworks)的默认调度器。思路是:所有作业平分资源,大作业不能饿死小作业。
┌─────────────────────────────────────────────────────────────────┐ │ Fair Scheduler(公平调度器) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 场景:集群总资源 100 GB,同时运行多个作业 │ │ │ │ t1 时刻:只有 Job1 提交 │ │ 资源:███████████████████████████████████████████████ │ │ Job1: 100 GB(公平份额: 100 GB) │ │ │ │ t2 时刻:Job2 提交 │ │ 资源:████████████████████ ████████████████████████ │ │ Job1: 50 GB Job2: 50 GB(各分一半) │ │ │ │ t3 时刻:Job3 提交 │ │ 资源:████████████████ ████████████████ ████████████ │ │ Job1: 33 GB Job2: 33 GB Job3: 33 GB(各分 1/3) │ │ │ │ 关键特性: │ │ • 公平共享:资源按作业数量平均分配 │ │ • 权重支持:重要作业可设置更高权重,获得更多资源 │ │ • 抢占机制:资源不足的队列可抢占超额分配的队列 │ │ • 最小份额保证:队列可设置最小资源,确保基本运行 │ │ │ │ 公平份额计算: │ │ 每个队列/作业的公平份额 = 总资源 × (自身权重 / 总权重) │ │ │ └─────────────────────────────────────────────────────────────────┘Fair Scheduler 适合多用户共享环境:比如大学的计算集群、公有云的共享集群,保证每个用户的作业都能获得公平的资源份额。
三种调度器对比
| 特性 | FIFO | Capacity | Fair |
|---|---|---|---|
| 设计目标 | 简单顺序 | 多租户容量保证 | 公平共享 |
| 队列支持 | 无 | 多层次队列 | 层次化队列池 |
| 资源隔离 | 无 | 容量限制 + ACL | 权重 + 最小份额 |
| 弹性共享 | 无 | 支持 | 支持 |
| 抢占 | 无 | 不支持 | 支持 |
| 适用场景 | 测试环境 | 企业多部门 | 多用户共享 |
Container 隔离:怎么防止"邻居"搞破坏?
多租户环境下,一个作业的 Task 不能把其他作业的资源吃光。YARN 通过以下机制实现隔离:
内存隔离
NodeManager 监控每个 Container 的内存使用量,如果超出申请值,直接kill -9干掉。简单粗暴,但有效。
CPU 隔离
Linux 环境下,YARN 通过cgroup限制 Container 的 CPU 使用:
- 软限制(默认):允许弹性共享,空闲时可以用更多 CPU
- 硬限制:严格按申请的核数分配,超了就被限制
磁盘隔离
每个 Container 有独立的本地工作目录,防止磁盘空间被单个 Container 占满。
┌─────────────────────────────────────────────────────────────────┐ │ YARN Container 资源隔离 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ NodeManager 上的 Container 隔离: │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ NodeManager │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ Container 1 │ │ Container 2 │ │ Container 3 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 内存: 2GB │ │ 内存: 4GB │ │ 内存: 2GB │ │ │ │ │ │ CPU: 1核 │ │ CPU: 2核 │ │ CPU: 1核 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ cgroup 限制 │ │ cgroup 限制 │ │ cgroup 限制 │ │ │ │ │ │ • memory.limit│ │ • memory.limit│ │ • memory.limit│ │ │ │ │ │ • cpu.cfs_quota│ │ • cpu.cfs_quota│ │ • cpu.cfs_quota│ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ │ 超内存 → kill │ │ │ │ 超 CPU → cgroup 限制(硬限制)或降级(软限制) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘YARN vs Kubernetes:谁才是未来?
这几年 Kubernetes(K8s)火得不行,很多人问:YARN 会不会被 K8s 取代?
| 维度 | YARN | Kubernetes |
|---|---|---|
| 设计定位 | 大数据资源管理 | 通用容器编排 |
| 资源抽象 | Container(JVM 进程) | Pod(容器组) |
| 调度模型 | 请求-分配 | 声明式调度 |
| 大数据生态 | 深度集成(HDFS、Hive 等) | 需额外适配 |
| 服务发现 | 有限支持 | 原生 Service + Ingress |
| 自动扩缩容 | 有限 | HPA / VPA 原生支持 |
| 云原生 | 较弱 | 原生设计 |
| 安全认证 | Kerberos 集成成熟 | RBAC + 其他方案 |
现状是:
- 传统 Hadoop 生态企业:YARN 仍是主流,迁移成本高
- 云原生新架构:Spark on K8s、Flink on K8s 越来越流行
- 趋势:大数据正在逐步向 K8s 迁移,但 YARN 短期内不会消失
Spark on K8s 的例子:
# 以前跑在 YARN 上spark-submit--masteryarn--deploy-mode cluster...# 现在可以直接跑在 K8s 上spark-submit\--masterk8s://https://<k8s-apiserver-host>:443\--deploy-mode cluster\--confspark.kubernetes.container.image=spark:3.4.0\...K8s 的优势:统一的基础设施、更好的弹性伸缩、更丰富的生态(服务发现、配置管理、监控)。
YARN 的优势:大数据生态成熟、Kerberos 安全集成、队列调度精细。
小结
今天咱们聊了 YARN:
- 设计思想:资源管理和作业执行解耦,一套集群跑多种框架
- 核心组件:ResourceManager(资源大管家)、NodeManager(节点小管家)、ApplicationMaster(作业小管家)
- 双层调度:RM 分配资源给 AM,AM 决定怎么跑 Task
- 三种调度器:FIFO(简单但不公平)、Capacity(企业多租户)、Fair(多用户共享)
- 资源隔离:内存限制(超了就杀)、CPU 限制(cgroup)、磁盘隔离
- 与 K8s 的关系:大数据正在向 K8s 迁移,但 YARN 短期内仍是主流
YARN 的价值在于:它让多种计算框架(MapReduce、Spark、Flink)能够共享同一套集群资源,避免了"每个框架一套集群"的资源浪费。理解 YARN 的调度机制,对于排查资源争抢、作业排队等问题非常有帮助。
你们公司的集群用的是 YARN 还是 K8s?有没有遇到过资源争抢导致的作业失败?欢迎聊聊~
