# Yarn资源管理系统
# 核心概念:什么是Yarn
# 生活比喻与专业定义
想象一个智能工厂管理系统:
- 有很多机器设备(服务器、计算节点)
- 每天接到各种订单(计算任务、应用程序)
- 需要合理分配工人和设备(计算资源:CPU、内存)
专业定义:Yarn(Yet Another Resource Negotiator)是Hadoop生态系统中的分布式资源管理器,实现了资源管理与作业管理的分离,为多种计算框架提供统一的资源调度服务。
# 设计背景:为什么需要Yarn
# MapReduce 1.0的问题
graph TB
subgraph "MapReduce 1.0问题"
A[JobTracker单点故障] --> D[系统不稳定]
B[资源与作业管理耦合] --> D
C[只支持MapReduce模型] --> D
E[扩展性差] --> D
end
问题类型 | 具体表现 | 影响 |
---|---|---|
紧耦合架构 | JobTracker既管资源分配又管作业调度 | 职责不清,复杂度高 |
单点故障 | JobTracker故障导致整个集群不可用 | 系统可靠性差 |
框架局限 | 只支持MapReduce计算模型 | 应用场景受限 |
扩展性差 | 并发作业增加导致性能下降 | 无法支撑大规模集群 |
# Yarn的解决方案
graph TB
subgraph "Yarn改进"
A[资源管理与应用管理分离] --> D[系统稳定高效]
B[多框架支持] --> D
C[分布式ApplicationMaster] --> D
E[良好扩展性] --> D
end
改进点 | 解决方案 | 优势 |
---|---|---|
分工明确 | 资源管理与应用管理解耦 | 职责清晰,易维护 |
多元化支持 | 多框架统一资源调度 | 生态系统丰富 |
分布式管理 | ApplicationMaster分布式管理 | 消除单点故障 |
# Yarn四大核心组件
# 组织架构图
graph TB
subgraph "Yarn生态系统"
RM[ResourceManager资源管理器]
subgraph "集群节点"
NM1[NodeManager1]
NM2[NodeManager2]
NM3[NodeManager3]
end
subgraph "应用管理"
AM1[ApplicationMaster1]
AM2[ApplicationMaster2]
end
subgraph "资源容器"
C1[Container1]
C2[Container2]
C3[Container3]
C4[Container4]
end
end
RM --> NM1
RM --> NM2
RM --> NM3
RM --> AM1
RM --> AM2
NM1 --> C1
NM2 --> C2
NM3 --> C3
NM3 --> C4
AM1 --> C1
AM1 --> C2
AM2 --> C3
AM2 --> C4
# ResourceManager - 资源总管
核心职责:管理整个集群的资源分配
核心组件:
- 资源调度器(Scheduler):决定资源分配策略
- 应用管理器(Applications Manager):管理AM生命周期
主要功能:
- 接收客户端应用提交请求
- 为应用分配第一个Container启动AM
- 处理AM的资源申请请求
- 监控AM状态并处理故障
- 提供Web界面查看集群状态
# NodeManager - 节点管理器
核心职责:管理单个节点的资源和服务
主要功能:
- 监控本地资源使用情况(CPU、内存、磁盘)
- 定期向RM发送心跳报告节点状态
- 接收AM指令启动/停止Container
- 管理应用程序依赖文件下载
- 收集和聚合应用程序日志
# ApplicationMaster - 应用管理器
核心职责:负责单个应用程序的全生命周期管理
工作流程:
- 向RM注册,获得应用管理权限
- 将Application分解为具体Task
- 向RM申请执行Task需要的Container
- 与NM通信在Container中启动Task
- 监控Task执行状态和进度
- 处理Task失败并决定重试策略
- 任务完成后释放资源并向RM注销
# Container - 资源容器
核心职责:提供标准化的资源抽象
资源组成:
- CPU:处理器核心数
- Memory:内存大小
- Disk:磁盘空间
- Network:网络带宽
# 应用程序执行流程
# 完整执行流程图
sequenceDiagram
participant Client as 客户端
participant RM as ResourceManager
participant NM as NodeManager
participant AM as ApplicationMaster
Client->>RM: 1.提交Application
RM->>NM: 2.分配Container启动AM
NM->>AM: 3.启动ApplicationMaster
AM->>RM: 4.AM注册
Client->>RM: 5.查询应用状态
AM->>RM: 6.申请Task资源
RM->>AM: 7.分配Container资源
AM->>NM: 8.启动Task Container
NM->>AM: 9.Task状态汇报
AM->>RM: 10.AM注销释放资源
# 步骤详解
- 应用提交:客户端通过Yarn Client API提交ApplicationSubmissionContext
- 资源分配:RM的Scheduler为AM分配第一个Container
- AM启动:NM在Container中启动ApplicationMaster进程
- AM注册:AM调用registerApplicationMaster()建立与RM的连接
- 状态查询:客户端可通过RM查看应用执行状态
- 资源申请:AM通过allocate()向RM请求Task执行所需的Container
- 资源响应:RM根据调度策略分配Container资源给AM
- 任务启动:AM与NM通信,在Container中启动具体的Task
- 进度监控:Task定期向AM汇报执行状态和进度
- 应用结束:AM调用unregisterApplicationMaster()释放资源
# API伪代码讲解
# Yarn Client API
// Yarn客户端API伪代码
public class YarnClient {
private YarnConfiguration conf;
private ApplicationClientProtocol rmClient;
public YarnClient() {
this.conf = new YarnConfiguration();
this.rmClient = createRMProxy();
}
// 提交应用程序
public ApplicationId submitApplication(ApplicationSubmissionContext appContext)
throws YarnException, IOException {
// 1. 创建应用提交请求
SubmitApplicationRequest request = Records.newRecord(SubmitApplicationRequest.class);
request.setApplicationSubmissionContext(appContext);
// 2. 向ResourceManager提交应用
rmClient.submitApplication(request);
return appContext.getApplicationId();
}
// 获取应用状态
public ApplicationReport getApplicationReport(ApplicationId appId)
throws YarnException, IOException {
GetApplicationReportRequest request = Records.newRecord(GetApplicationReportRequest.class);
request.setApplicationId(appId);
return rmClient.getApplicationReport(request).getApplicationReport();
}
// 杀死应用程序
public void killApplication(ApplicationId appId) throws YarnException, IOException {
KillApplicationRequest request = Records.newRecord(KillApplicationRequest.class);
request.setApplicationId(appId);
rmClient.forceKillApplication(request);
}
}
# ApplicationMaster API
// ApplicationMaster核心API伪代码
public abstract class ApplicationMaster {
protected AMRMClientAsync<ContainerRequest> amRMClient;
protected NMClientAsync nmClientAsync;
protected ApplicationAttemptId appAttemptID;
// AM主要生命周期方法
public void run() throws Exception {
// 1. 初始化RM客户端
initializeAMRMClient();
// 2. 注册ApplicationMaster
registerToRM();
// 3. 申请Container资源
requestContainers();
// 4. 等待资源分配并启动任务
while (!done) {
Thread.sleep(1000);
// 处理资源分配回调
}
// 5. 注销ApplicationMaster
unregisterAM();
}
// 向ResourceManager注册
private void registerToRM() throws YarnException, IOException {
RegisterApplicationMasterResponse response = amRMClient.registerApplicationMaster(
appMasterHostname,
appMasterRpcPort,
appMasterTrackingUrl
);
// 获取集群资源信息
maxMem = response.getMaximumResourceCapability().getMemory();
maxVCores = response.getMaximumResourceCapability().getVirtualCores();
}
// 申请Container资源
private void requestContainers() {
for (int i = 0; i < numContainers; ++i) {
ContainerRequest containerRequest = new ContainerRequest(
setupContainerAskForRM(), // 资源需求
null, // 节点偏好
null, // 机架偏好
Priority.newInstance(0) // 优先级
);
amRMClient.addContainerRequest(containerRequest);
}
}
// 设置Container资源需求
private Resource setupContainerAskForRM() {
Resource capability = Records.newRecord(Resource.class);
capability.setMemory(containerMemory);
capability.setVirtualCores(containerVirtualCores);
return capability;
}
// 启动Container中的任务
private void launchContainer(Container container) {
// 创建容器启动上下文
ContainerLaunchContext ctx = Records.newRecord(ContainerLaunchContext.class);
// 设置执行命令
List<String> commands = new ArrayList<>();
commands.add("java -Xmx" + containerMemory + "m MyTask");
ctx.setCommands(commands);
// 设置环境变量
Map<String, String> env = new HashMap<>();
env.put("CLASSPATH", System.getenv("CLASSPATH"));
ctx.setEnvironment(env);
// 异步启动Container
nmClientAsync.startContainerAsync(container, ctx);
}
}
# Container管理API
// Container管理相关API伪代码
public class ContainerManager {
// Container启动上下文配置
public static ContainerLaunchContext createContainerLaunchContext(
String command, Map<String, String> env, Map<String, LocalResource> resources) {
ContainerLaunchContext ctx = Records.newRecord(ContainerLaunchContext.class);
// 1. 设置执行命令
List<String> commands = Arrays.asList(command);
ctx.setCommands(commands);
// 2. 设置环境变量
ctx.setEnvironment(env);
// 3. 设置本地资源(jar包、配置文件等)
ctx.setLocalResources(resources);
return ctx;
}
// 创建本地资源
public static LocalResource createLocalResource(FileSystem fs, Path resourcePath)
throws IOException {
LocalResource resource = Records.newRecord(LocalResource.class);
FileStatus jarStat = fs.getFileStatus(resourcePath);
resource.setResource(ConverterUtils.getYarnUrlFromPath(resourcePath));
resource.setSize(jarStat.getLen());
resource.setTimestamp(jarStat.getModificationTime());
resource.setType(LocalResourceType.FILE);
resource.setVisibility(LocalResourceVisibility.PUBLIC);
return resource;
}
}
# 资源调度API
// 资源调度相关API伪代码
public class ResourceScheduler {
// 资源请求构造
public static ContainerRequest createContainerRequest(int memory, int vcores,
String[] nodes, String[] racks, int priority) {
// 设置资源需求
Resource capability = Resource.newInstance(memory, vcores);
// 设置优先级
Priority pri = Priority.newInstance(priority);
// 创建容器请求
ContainerRequest request = new ContainerRequest(
capability,
nodes, // 节点偏好
racks, // 机架偏好
pri
);
return request;
}
// 资源分配回调处理
public class AMResourceCallback implements AMRMClientAsync.CallbackHandler {
@Override
public void onContainersAllocated(List<Container> containers) {
for (Container container : containers) {
// 启动分配到的Container
launchContainer(container);
}
}
@Override
public void onContainersCompleted(List<ContainerStatus> statuses) {
for (ContainerStatus status : statuses) {
if (status.getExitStatus() != 0) {
// 处理Container执行失败
handleContainerFailure(status);
} else {
// 处理Container成功完成
handleContainerSuccess(status);
}
}
}
@Override
public void onError(Throwable e) {
// 处理AM错误
handleAMError(e);
}
}
}
# MapReduce on Yarn API示例
// MapReduce在Yarn上运行的API伪代码
public class MRAppMaster extends ApplicationMaster {
public static void main(String[] args) throws Exception {
MRAppMaster appMaster = new MRAppMaster();
appMaster.run();
}
@Override
public void run() throws Exception {
// 1. 解析作业配置
JobConf jobConf = new JobConf();
Job job = parseJobConfiguration(jobConf);
// 2. 初始化与RM的连接
initializeResourceManager();
// 3. 注册ApplicationMaster
registerApplicationMaster();
// 4. 创建Map和Reduce任务
List<MapTask> mapTasks = createMapTasks(job);
List<ReduceTask> reduceTasks = createReduceTasks(job);
// 5. 申请Map任务资源
requestContainersForTasks(mapTasks);
// 6. 等待Map任务完成
waitForMapTasksCompletion();
// 7. 申请Reduce任务资源
requestContainersForTasks(reduceTasks);
// 8. 等待Reduce任务完成
waitForReduceTasksCompletion();
// 9. 作业完成,注销AM
unregisterApplicationMaster();
}
private void requestContainersForTasks(List<? extends Task> tasks) {
for (Task task : tasks) {
ContainerRequest containerRequest = createContainerRequest(
task.getMemoryRequirement(),
task.getVCoreRequirement(),
task.getPreferredNodes(),
task.getPreferredRacks()
);
amRMClient.addContainerRequest(containerRequest);
}
}
}
# 三种资源调度策略
# 调度器架构对比
graph TB
subgraph "FIFO Scheduler"
A1[单队列] --> A2[先进先出]
A2 --> A3[可能阻塞]
end
subgraph "Capacity Scheduler"
B1[多队列] --> B2[资源隔离]
B2 --> B3[按比例分配]
end
subgraph "Fair Scheduler"
C1[多队列] --> C2[动态共享]
C2 --> C3[公平调度]
end
# FIFO Scheduler - 先进先出调度
graph LR
A[应用1提交] --> B[应用2提交]
B --> C[应用3提交]
D[队列] --> E[应用1执行完]
E --> F[应用2开始]
F --> G[应用3开始]
特点:
- 简单公平:先来先服务
- 长任务阻塞:大应用独占资源
- 适用场景:简单批处理环境
# Capacity Scheduler - 容量调度
graph TB
subgraph "集群资源100%"
subgraph "生产队列70%"
P1[应用A]
P2[应用B]
end
subgraph "开发队列30%"
D1[应用C]
D2[应用D]
end
end
特点:
- 资源隔离:各队列有固定配额
- 可能浪费:空闲队列资源闲置
- 适用场景:多租户环境
# Fair Scheduler - 公平调度
graph TB
subgraph "动态资源分配"
A[队列1: 2个应用] --> C[各分配25%]
B[队列2: 1个应用] --> D[分配50%]
end
E[应用完成] --> F[资源重新分配]
特点:
- 动态共享:资源利用率最高
- 复杂度高:调度算法复杂
- 适用场景:混合负载环境
# 调度器对比表
特性 | FIFO | Capacity | Fair |
---|---|---|---|
队列数量 | 1个 | 多个固定 | 多个动态 |
资源分配 | 独占 | 按比例 | 公平共享 |
响应时间 | 慢 | 中等 | 快 |
适用场景 | 简单批处理 | 多租户 | 混合负载 |
配置难度 | 简单 | 中等 | 复杂 |
# 容错机制
# 故障处理架构
graph TB
subgraph "Yarn容错体系"
subgraph "ResourceManager容错"
RM1[Active RM]
RM2[Standby RM]
ZK[ZooKeeper协调]
end
subgraph "ApplicationMaster容错"
AM1[AM重启] --> AM2[状态恢复]
end
subgraph "NodeManager容错"
NM1[节点故障] --> NM2[任务迁移]
end
subgraph "Container容错"
C1[任务失败] --> C2[重新调度]
end
end
# ResourceManager故障
故障处理机制:
- 状态持久化:将RM状态存储到HDFS/ZooKeeper
- 主备切换:Active/Standby模式
- 自动恢复:新RM从持久化存储恢复状态
# NodeManager故障
故障处理机制:
- 故障检测:RM通过心跳超时检测NM故障
- 任务迁移:AM重新申请资源在其他节点执行
- 节点恢复:NM重启后重新注册
# ApplicationMaster故障
故障处理机制:
- 重启机制:RM重新启动AM Container
- 重试限制:最大重试次数可配置
- 状态恢复:框架负责恢复应用状态
# Container任务故障
故障处理机制:
- 任务重试:AM重新调度失败的Task
- 黑名单机制:避免在故障节点重试
- 推测执行:为慢任务启动备份任务
# 计算框架集成
# 多框架架构
graph TB
subgraph "Yarn统一资源管理平台"
RM[ResourceManager]
NM[NodeManager集群]
end
subgraph "计算框架"
MR[MapReduce]
Spark[Spark]
Storm[Storm]
Flink[Flink]
end
subgraph "ApplicationMaster"
MRAM[MRAppMaster]
SparkAM[Spark AM]
StormAM[Storm AM]
FlinkAM[Flink AM]
end
RM --> MRAM
RM --> SparkAM
RM --> StormAM
RM --> FlinkAM
MR --> MRAM
Spark --> SparkAM
Storm --> StormAM
Flink --> FlinkAM
# MapReduce on Yarn
架构特点:一体化应用管理
graph LR
Client[客户端] --> RM[ResourceManager]
RM --> MRAM[MRAppMaster]
MRAM --> YC1[YarnChild Map]
MRAM --> YC2[YarnChild Reduce]
执行流程:
- 客户端通过Yarn Client提交MapReduce作业
- RM启动MRAppMaster管理该作业
- MRAppMaster申请资源并启动Map/Reduce任务
- YarnChild进程执行具体的计算逻辑
# Spark on Yarn
# Client模式
graph TB
subgraph "客户端节点"
Driver[Driver程序]
end
subgraph "Yarn集群"
RM[ResourceManager]
EL[ExecutorLauncher AM]
E1[Executor1]
E2[Executor2]
end
Driver <--> RM
Driver --> EL
EL --> E1
EL --> E2
Driver <--> E1
Driver <--> E2
特点:
- Driver运行在客户端
- ExecutorLauncher只负责资源申请
- 适合交互式作业和调试
# Cluster模式
graph TB
subgraph "客户端节点"
Client[Spark Submit]
end
subgraph "Yarn集群"
RM[ResourceManager]
DAM[Driver+AM]
E1[Executor1]
E2[Executor2]
end
Client -.->|提交后断开| RM
RM --> DAM
DAM --> E1
DAM --> E2
DAM <--> E1
DAM <--> E2
特点:
- Driver与AM合并运行在集群中
- 客户端提交后可以断开
- 适合生产环境批处理作业
# 应用与作业概念
# MapReduce vs Spark概念对比
graph TB
subgraph "MapReduce应用模型"
MRApp[MapReduce应用]
MRApp --> MRJob[MapReduce作业]
MRJob --> MRApp
end
subgraph "Spark应用模型"
SparkApp[Spark应用]
SparkApp --> Job1[作业1 collect]
SparkApp --> Job2[作业2 count]
SparkApp --> Job3[作业3 save]
end
框架 | 应用(Application) | 作业(Job) | 触发机制 | 关系 |
---|---|---|---|---|
MapReduce | = Job | = Job | 程序启动 | 1:1 |
Spark | SparkContext | Action操作 | collect()、save()等 | 1:N |
关系说明:
- MapReduce:一个应用就是一个作业,是一对一关系
- Spark:一个应用可包含多个作业,通过Action操作触发
# 重要配置参数
# 资源配置
# NodeManager资源设置
yarn.nodemanager.resource.memory-mb=8192 # NM可用内存
yarn.nodemanager.resource.cpu-vcores=8 # NM可用CPU核数
# Container资源限制
yarn.scheduler.maximum-allocation-mb=4096 # Container最大内存
yarn.scheduler.minimum-allocation-mb=1024 # Container最小内存
# ApplicationMaster配置
yarn.app.mapreduce.am.resource.mb=1536 # AM内存大小
yarn.resourcemanager.am.max-attempts=3 # AM最大重试次数
# 调度器配置
# 选择调度器类型
yarn.resourcemanager.scheduler.class=
org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
# 容量调度器队列配置
yarn.scheduler.capacity.root.queues=default,production
yarn.scheduler.capacity.root.default.capacity=30
yarn.scheduler.capacity.root.production.capacity=70
# 习题讲解
# 应用与作业概念区分
Q1: 对于MapReduce和Spark而言,应用与作业是否存在区别?
答案解析:
对于MapReduce而言:
- 应用和作业没有区别
- 一个MapReduce程序就是一个应用,也是一个作业
- 是一对一的关系
对于Spark而言:
- 应用包含作业,一个应用中可以有多个作业
- 在程序上,一个应用与一个SparkContext对应
- 一个作业与一个Action操作对应(如collect、count、save等)
示例说明:
// Spark应用示例
val spark = new SparkContext() // 一个应用开始
val rdd = spark.textFile("input.txt")
val words = rdd.flatMap(_.split(" "))
// 以下每个Action都会触发一个作业
val count = words.count() // 作业1
val result = words.collect() // 作业2
words.saveAsTextFile("output") // 作业3
spark.stop() // 应用结束
# 架构设计理念对比
Q2: Spark架构与Yarn架构在设计理念上有无共同点?
graph TB
subgraph "设计理念共同点"
A[主从架构] --> D[高可用性]
B[职责分离] --> D
C[分布式管理] --> D
end
subgraph "Spark架构"
SA[Driver主节点] --> SB[Executor从节点]
SC[作业调度与执行分离]
end
subgraph "Yarn架构"
YA[ResourceManager主节点] --> YB[NodeManager从节点]
YC[资源管理与应用管理分离]
end
答案解析:
两者确实存在相同的设计理念:
- 主从架构:
- Spark:Driver作为主节点,Executor作为从节点
- Yarn:ResourceManager作为主节点,NodeManager作为从节点
- 职责分离:
- Spark:Driver负责作业调度,Executor负责任务执行
- Yarn:ResourceManager负责资源管理,ApplicationMaster负责应用管理
- 分布式管理:
- Spark:多个Executor分布式执行任务
- Yarn:多个ApplicationMaster分布式管理应用
这种设计使得作业之间可以相互独立地控制执行,提高了系统的可靠性和扩展性。
# Hadoop代际对比
Q3: 第二代Hadoop与第一代相比的优势是什么?
# 第一代MapReduce架构问题
graph TB
subgraph "MapReduce 1.0架构"
JT[JobTracker]
TT1[TaskTracker1]
TT2[TaskTracker2]
TT3[TaskTracker3]
JT --> TT1
JT --> TT2
JT --> TT3
JT --> A[资源管理]
JT --> B[作业管理]
JT --> C[任务调度]
end
主要问题:
- 紧耦合:JobTracker既负责作业管理也负责调度资源
- 高度集中化:JobTracker负责所有作业的管理,内存开销大
- 通信瓶颈:作业增加时,JobTracker通信开销增大
- 单点故障:JobTracker故障导致整个系统不可用
# 第二代MapReduce架构优势
graph TB
subgraph "MapReduce 2.0 on Yarn"
RM[ResourceManager]
NM1[NodeManager1]
NM2[NodeManager2]
AM1[MRAppMaster1]
AM2[MRAppMaster2]
RM --> NM1
RM --> NM2
RM --> AM1
RM --> AM2
AM1 --> YC1[YarnChild]
AM2 --> YC2[YarnChild]
end
关键改进:
- 职责分离:ResourceManager负责资源管理,MRAppMaster负责作业管理
- 分布式管理:每个应用有独立的MRAppMaster管理
- 消除瓶颈:多个AM分担管理压力
- 提高可靠性:单个应用故障不影响其他应用
# Yarn核心组件
Q4: Yarn的主要部件都有什么?
graph TB
subgraph "Yarn四大组件"
RM[ResourceManager]
NM[NodeManager]
AM[ApplicationMaster]
Container[Container]
subgraph "RM内部组件"
Scheduler[调度器Scheduler]
AppManager[应用管理器AppManager]
end
RM --> Scheduler
RM --> AppManager
end
答案解析:
- ResourceManager(RM):
- 调度器:负责分配Container并进行资源调度
- 应用管理器:管理所有应用,包括应用提交、与调度器协商资源启动AM、监控AM运行状态等
- NodeManager(NM):
- 定期向RM汇报本节点的资源使用情况和Container运行状态
- 接受并处理来自AM的Container启动/停止请求
- ApplicationMaster(AM):
- 与RM调度器协商获取资源(以Container表示)
- 将获取的资源分配给作业内部的任务
- 与NM通信启动/停止任务,监控任务运行状态,处理任务故障
- Container:
- 资源抽象表示,包含CPU、内存等资源
- 动态资源划分单位
- AM向RM申请资源时,RM返回Container形式的资源
# Container生命周期管理
Q5: Yarn中的Container由谁负责启动/停止?
sequenceDiagram
participant AM as ApplicationMaster
participant RM as ResourceManager
participant NM as NodeManager
participant Container as Container
AM->>RM: 1.申请Container资源
RM->>AM: 2.分配Container
AM->>NM: 3.请求启动Container
NM->>Container: 4.启动Container进程
AM->>NM: 5.请求停止Container
NM->>Container: 6.停止Container进程
AM->>RM: 7.释放资源并注销
答案解析:
Container的启动/停止是AM与NM协作完成的:
- 资源申请阶段:AM向RM请求资源
- 资源分配阶段:RM确定资源分配方案后返回Container给AM
- 容器启动阶段:AM向对应的NM通信,在Container中启动任务进程
- 容器停止阶段:任务结束时,AM通知NM停止Container
- 资源释放阶段:AM逐步释放占用的资源,最终向RM注销并关闭
关键点:NM是Container的实际管理者,但启动/停止的决策来自AM。
# 调度器对比分析
Q6: 简要分析FIFO、Capacity、Fair三种调度器的优缺点。
graph TB
subgraph "调度器特性对比"
subgraph "FIFO Scheduler"
F1[单队列] --> F2[先进先出]
F2 --> F3[可能长时间阻塞]
end
subgraph "Capacity Scheduler"
C1[多队列] --> C2[资源隔离]
C2 --> C3[可能资源浪费]
end
subgraph "Fair Scheduler"
R1[多队列] --> R2[动态共享]
R2 --> R3[复杂度高]
end
end
答案解析:
调度器 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
FIFO | 简单公平,实现容易 | 大应用独占资源,小应用等待时间长 | 简单批处理环境 |
Capacity | 资源隔离,多租户支持 | 队列资源可能闲置,利用率不高 | 多部门共享集群 |
Fair | 资源利用率高,响应时间短 | 实现复杂,进程切换开销大 | 混合工作负载 |
具体分析:
- FIFO:会造成一个应用独占资源,其他应用不断等待,导致总体执行时间变长
- Capacity:避免了资源被独占,但会让一些队列的资源空闲,不能充分利用
- Fair:最大程度避免资源浪费,但实现复杂,且进程间切换产生较大开销
# ApplicationMaster资源申请流程
Q7: 简述ApplicationMaster申请资源的过程。
sequenceDiagram
participant AM as ApplicationMaster
participant RM as ResourceManager
participant NM as NodeManager
AM->>AM: 1.应用分解为任务
AM->>RM: 2.申请Container资源
RM->>AM: 3.返回Container分配结果
AM->>AM: 4.确定资源分配方案
AM->>NM: 5.请求启动Container
NM->>AM: 6.确认Container启动
答案详解:
- 任务分解:AM将应用解析为作业并进一步分解为若干任务
- 资源申请:AM向RM申请启动这些任务所需的Container资源
- 资源分配:RM根据调度策略向AM提供Container形式的资源
- 分配决策:AM确定资源在各个任务之间的具体分配方案
- 任务启动:AM与对应的NM通信,在相应的Container中启动进程执行任务
关键要点:
- AM是资源申请的发起者和决策者
- RM负责全局资源调度和分配
- NM负责具体Container的启动和管理
- 整个过程体现了Yarn的分层管理设计
# NodeManager监控职责
Q8: NodeManager是否监控Container中任务的执行情况?
graph TB
subgraph "监控职责分工"
NM[NodeManager] --> A[监控Container进程]
NM --> B[监控节点资源]
NM --> C[不监控任务执行逻辑]
AM[ApplicationMaster] --> D[监控任务执行状态]
AM --> E[监控任务进度]
AM --> F[处理任务失败]
end
答案:NodeManager不监控Container中任务的执行情况。
职责划分:
- NodeManager职责:
- 监控Container进程的生命周期(启动、停止)
- 监控本地节点的资源使用情况
- 响应AM的Container管理请求
- ApplicationMaster职责:
- 监控任务的执行状态和进度
- 处理任务失败和重试逻辑
- 收集任务的计算结果
设计原理:这种分工体现了Yarn的分层设计思想,NM专注于资源管理,AM专注于应用逻辑管理,各司其职,提高系统的模块化和可维护性。
# ApplicationMaster容错机制
Q9: ApplicationMaster由谁监控?并写出容错恢复过程。
graph TB
subgraph "AM容错机制"
RM[ResourceManager] --> A[监控AM状态]
A --> B[AM故障检测]
B --> C[重启AM进程]
C --> D[框架状态恢复]
end
答案:ApplicationMaster由ResourceManager监控。
容错恢复过程:
sequenceDiagram
participant RM as ResourceManager
participant AM as ApplicationMaster
participant Framework as 计算框架
participant NM as NodeManager
RM->>AM: 1.定期心跳检测
Note over AM: AM故障
RM->>RM: 2.检测到AM故障
RM->>NM: 3.申请新Container
RM->>AM: 4.重启AM进程
AM->>Framework: 5.框架负责状态恢复
AM->>RM: 6.重新注册
详细步骤:
- 故障检测:RM通过心跳机制检测AM是否存活
- 重启决策:如果AM故障,Yarn会重启AM进程
- 进程重启:在新的Container中启动新的AM实例
- 状态恢复:AM重启后,不会恢复运行状态,运行状态需要由具体的计算框架(如Spark、MapReduce)负责恢复
- 重新注册:新AM向RM重新注册,继续管理应用
重要说明:Yarn只负责AM进程的重启,具体的应用状态恢复逻辑由各个计算框架自己实现。
# MapReduce独立运行问题
Q10: 为什么引入Yarn以后,MapReduce无法独立运行?
# MapReduce 1.0独立架构
graph TB
subgraph "MapReduce 1.0独立架构"
JT[JobTracker] --> A[资源管理]
JT --> B[作业管理]
JT --> C[任务调度]
TT1[TaskTracker1]
TT2[TaskTracker2]
TT3[TaskTracker3]
JT --> TT1
JT --> TT2
JT --> TT3
end
# MapReduce 2.0依赖Yarn
graph TB
subgraph "MapReduce 2.0依赖Yarn"
RM[ResourceManager] --> A[资源管理]
NM[NodeManager] --> B[节点管理]
MRAM[MRAppMaster] --> C[作业管理]
RM --> NM
RM --> MRAM
MRAM --> D[MapReduce任务]
end
答案解析:
引入Yarn后,MapReduce架构发生了根本性变化:
组件变化:
- 消失的组件:JobTracker和TaskTracker不再存在
- 新的依赖:
- 资源管理:依赖Yarn的ResourceManager和NodeManager
- 作业管理:依赖MRAppMaster
无法独立运行的原因:
- 缺少资源管理器:MapReduce本身不再包含资源管理功能
- 缺少节点管理器:无法管理集群节点和Container
- 架构重新设计:MapReduce 2.0被设计为Yarn上的一个应用框架,而不是独立系统
依赖关系:
- MapReduce应用需要通过Yarn Client提交
- MRAppMaster需要Yarn环境才能启动
- Map/Reduce任务需要在Yarn的Container中运行
因此,MapReduce 2.0完全依赖Yarn提供的资源管理和调度服务,无法独立运行。
# Spark运行模式对比
Q11: Yarn平台运行MapReduce的方式与Spark的Yarn Client和Yarn Cluster哪个更像?
# 三种运行模式对比
graph TB
subgraph "MapReduce on Yarn"
MRClient[客户端] --> MRAM[MRAppMaster]
MRAM --> MRTask[Map/Reduce任务]
MRAM --> A[管理应用]
MRAM --> B[分配资源]
end
subgraph "Spark Client Mode"
SClient[客户端Driver] --> EL[ExecutorLauncher]
EL --> SExecutor[Executor]
EL --> C[只分配资源]
SClient --> D[管理应用]
end
subgraph "Spark Cluster Mode"
SClient2[客户端] --> DAM[Driver+AM]
DAM --> SExecutor2[Executor]
DAM --> E[管理应用]
DAM --> F[分配资源]
end
答案:MapReduce on Yarn更像Spark Cluster模式。
相似性分析:
特性 | MapReduce on Yarn | Spark Client | Spark Cluster |
---|---|---|---|
应用管理 | MRAppMaster负责 | Driver负责 | Driver+AM负责 |
资源分配 | MRAppMaster负责 | ExecutorLauncher负责 | Driver+AM负责 |
管理位置 | 集群内 | 客户端 | 集群内 |
职责集中度 | 一体化管理 | 分离式管理 | 一体化管理 |
关键相似点:
- 一体化管理:MapReduce的MRAppMaster既负责应用管理,也负责资源分配,这与Spark Cluster模式的Driver+AM一体化管理方式相同
- 集群内运行:管理组件都运行在集群内部,而不是客户端
- 完整生命周期管理:从资源申请到任务执行的全程管理
与Client模式的区别:
- Client模式:ExecutorLauncher(AM)只负责分配资源和启动任务,不负责应用管理逻辑
- MapReduce模式:MRAppMaster需要管理整个MapReduce应用的生命周期,包括Map/Reduce任务的调度和监控
# 多框架运行架构
Q12: 画出Yarn同时运行MapReduce和Spark的架构图,其中Spark程序以Yarn Client模式执行。
graph TB
subgraph "客户端层"
MRClient[MapReduce客户端]
SparkDriver[Spark Driver程序]
end
subgraph "Yarn集群"
RM[ResourceManager]
subgraph "NodeManager1"
NM1[NodeManager1]
MRAM[MRAppMaster]
MapTask[Map Task]
end
subgraph "NodeManager2"
NM2[NodeManager2]
EL[ExecutorLauncher]
SparkExecutor1[Spark Executor1]
end
subgraph "NodeManager3"
NM3[NodeManager3]
ReduceTask[Reduce Task]
SparkExecutor2[Spark Executor2]
end
end
%% MapReduce连接
MRClient --> RM
RM --> MRAM
MRAM --> MapTask
MRAM --> ReduceTask
%% Spark Client模式连接
SparkDriver --> RM
SparkDriver --> EL
RM --> EL
EL --> SparkExecutor1
EL --> SparkExecutor2
SparkDriver <--> SparkExecutor1
SparkDriver <--> SparkExecutor2
%% 资源管理连接
RM --> NM1
RM --> NM2
RM --> NM3
架构说明:
MapReduce应用部分:
- MapReduce客户端向ResourceManager提交作业
- RM在某个NodeManager上启动MRAppMaster
- MRAppMaster申请资源并在不同节点启动Map/Reduce任务
- MRAppMaster直接管理Map/Reduce任务的执行
Spark Client模式部分:
- Spark Driver程序运行在客户端
- Driver向RM申请资源启动ExecutorLauncher(AM)
- ExecutorLauncher在各NodeManager上启动Executor
- Driver直接与各Executor通信进行任务调度和数据传输
关键特点:
- 资源统一管理:所有框架都通过同一个ResourceManager申请资源
- 节点资源共享:不同框架的Container可以运行在同一个NodeManager上
- 独立应用管理:每个应用有自己的ApplicationMaster,互不干扰
- 多样化部署:支持不同框架采用不同的部署模式
这种架构体现了Yarn作为统一资源管理平台的核心价值,实现了多计算框架的并存和资源的统一调度。