# 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 - 应用管理器

核心职责:负责单个应用程序的全生命周期管理

工作流程

  1. 向RM注册,获得应用管理权限
  2. 将Application分解为具体Task
  3. 向RM申请执行Task需要的Container
  4. 与NM通信在Container中启动Task
  5. 监控Task执行状态和进度
  6. 处理Task失败并决定重试策略
  7. 任务完成后释放资源并向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注销释放资源

# 步骤详解

  1. 应用提交:客户端通过Yarn Client API提交ApplicationSubmissionContext
  2. 资源分配:RM的Scheduler为AM分配第一个Container
  3. AM启动:NM在Container中启动ApplicationMaster进程
  4. AM注册:AM调用registerApplicationMaster()建立与RM的连接
  5. 状态查询:客户端可通过RM查看应用执行状态
  6. 资源申请:AM通过allocate()向RM请求Task执行所需的Container
  7. 资源响应:RM根据调度策略分配Container资源给AM
  8. 任务启动:AM与NM通信,在Container中启动具体的Task
  9. 进度监控:Task定期向AM汇报执行状态和进度
  10. 应用结束: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]

执行流程

  1. 客户端通过Yarn Client提交MapReduce作业
  2. RM启动MRAppMaster管理该作业
  3. MRAppMaster申请资源并启动Map/Reduce任务
  4. 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

答案解析

两者确实存在相同的设计理念:

  1. 主从架构
    • Spark:Driver作为主节点,Executor作为从节点
    • Yarn:ResourceManager作为主节点,NodeManager作为从节点
  2. 职责分离
    • Spark:Driver负责作业调度,Executor负责任务执行
    • Yarn:ResourceManager负责资源管理,ApplicationMaster负责应用管理
  3. 分布式管理
    • 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

答案解析

  1. ResourceManager(RM)
    • 调度器:负责分配Container并进行资源调度
    • 应用管理器:管理所有应用,包括应用提交、与调度器协商资源启动AM、监控AM运行状态等
  2. NodeManager(NM)
    • 定期向RM汇报本节点的资源使用情况和Container运行状态
    • 接受并处理来自AM的Container启动/停止请求
  3. ApplicationMaster(AM)
    • 与RM调度器协商获取资源(以Container表示)
    • 将获取的资源分配给作业内部的任务
    • 与NM通信启动/停止任务,监控任务运行状态,处理任务故障
  4. 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协作完成的:

  1. 资源申请阶段:AM向RM请求资源
  2. 资源分配阶段:RM确定资源分配方案后返回Container给AM
  3. 容器启动阶段:AM向对应的NM通信,在Container中启动任务进程
  4. 容器停止阶段:任务结束时,AM通知NM停止Container
  5. 资源释放阶段: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启动

答案详解

  1. 任务分解:AM将应用解析为作业并进一步分解为若干任务
  2. 资源申请:AM向RM申请启动这些任务所需的Container资源
  3. 资源分配:RM根据调度策略向AM提供Container形式的资源
  4. 分配决策:AM确定资源在各个任务之间的具体分配方案
  5. 任务启动: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.重新注册

详细步骤

  1. 故障检测:RM通过心跳机制检测AM是否存活
  2. 重启决策:如果AM故障,Yarn会重启AM进程
  3. 进程重启:在新的Container中启动新的AM实例
  4. 状态恢复:AM重启后,不会恢复运行状态,运行状态需要由具体的计算框架(如Spark、MapReduce)负责恢复
  5. 重新注册:新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

无法独立运行的原因

  1. 缺少资源管理器:MapReduce本身不再包含资源管理功能
  2. 缺少节点管理器:无法管理集群节点和Container
  3. 架构重新设计: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负责
管理位置 集群内 客户端 集群内
职责集中度 一体化管理 分离式管理 一体化管理

关键相似点

  1. 一体化管理:MapReduce的MRAppMaster既负责应用管理,也负责资源分配,这与Spark Cluster模式的Driver+AM一体化管理方式相同
  2. 集群内运行:管理组件都运行在集群内部,而不是客户端
  3. 完整生命周期管理:从资源申请到任务执行的全程管理

与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应用部分

  1. MapReduce客户端向ResourceManager提交作业
  2. RM在某个NodeManager上启动MRAppMaster
  3. MRAppMaster申请资源并在不同节点启动Map/Reduce任务
  4. MRAppMaster直接管理Map/Reduce任务的执行

Spark Client模式部分

  1. Spark Driver程序运行在客户端
  2. Driver向RM申请资源启动ExecutorLauncher(AM)
  3. ExecutorLauncher在各NodeManager上启动Executor
  4. Driver直接与各Executor通信进行任务调度和数据传输

关键特点

  • 资源统一管理:所有框架都通过同一个ResourceManager申请资源
  • 节点资源共享:不同框架的Container可以运行在同一个NodeManager上
  • 独立应用管理:每个应用有自己的ApplicationMaster,互不干扰
  • 多样化部署:支持不同框架采用不同的部署模式

这种架构体现了Yarn作为统一资源管理平台的核心价值,实现了多计算框架的并存和资源的统一调度。