4.3 YARN 应用开发与部署 4.3 YARN 应用开发与部署详解 4.3.1 YARN 应用开发概述 YARN 的出现打破了 Hadoop 1.0 中 MapReduce 独占资源管理的局面,实现了资源管理与计算框架的解耦。这意味着开发者不再局限于 MapReduce 编程模型,可以根据自身业务需求选择更合适的计算框架,并利用 YARN 提供的资源管理能力高效运行。 YARN 应用的核心思想在于将资源管理和应用管理分离。 ResourceManager (RM) 负责集群资源的统一管理和调度,而 ApplicationMaster (AM) 则作为每个应用的管理者,负责向 RM 申请资源,并将资源分配给应用内部的各个任务。
YARN 的出现打破了 Hadoop 1.0 中 MapReduce 独占资源管理的局面,实现了资源管理与计算框架的解耦。这意味着开发者不再局限于 MapReduce 编程模型,可以根据自身业务需求选择更合适的计算框架,并利用 YARN 提供的资源管理能力高效运行。
YARN 应用的核心思想在于将资源管理和应用管理分离。 ResourceManager (RM) 负责集群资源的统一管理和调度,而 ApplicationMaster (AM) 则作为每个应用的管理者,负责向 RM 申请资源,并将资源分配给应用内部的各个任务。NodeManager (NM) 驻留在集群的每个节点上,负责管理本节点的资源和容器,并执行 AM 分配的任务。
开发一个 YARN 应用,核心目标是构建一个能够与 YARN 框架交互的应用程序,使其能够:
向 YARN 注册并请求资源: 应用需要通过某种方式告知 YARN 自身的需求,例如所需的 CPU 核数、内存大小、容器数量等。
管理应用的生命周期: 包括应用的启动、运行、监控和结束等环节。
在 YARN 分配的容器中执行任务: 应用需要在分配到的容器中执行实际的计算任务。
与 YARN 框架进行通信: 例如心跳汇报、状态更新、资源释放等。
理解 YARN 应用开发的关键在于理解 ApplicationMaster (AM) 的角色。AM 是 YARN 应用的核心控制组件,它运行在 YARN 容器中,负责与 ResourceManager 交互,协调应用的资源需求和任务执行。开发者需要编写 AM 的逻辑,使其能够有效地管理应用的生命周期,并利用 YARN 提供的资源完成计算任务。
为了更好地理解 YARN 应用的开发与部署,我们首先需要深入了解 YARN 应用的架构组成,以及各个组件之间的交互关系。下图使用 Mermaid 的 graph TD 图形化展示了 YARN 应用的基本架构:
图中主要组件及其作用如下:
Client (客户端): 用户提交 YARN 应用的入口。客户端负责将应用的相关信息(如 AM 程序、应用配置、所需资源等)提交给 ResourceManager。
ResourceManager (RM, 资源管理器): YARN 集群的中央资源管理器,负责整个集群的资源管理和调度。RM 主要包含两个组件:
Scheduler (调度器): 根据应用的资源请求和集群的资源状况,分配资源(以 Container 的形式)。YARN 支持多种调度器,如 FIFO Scheduler, Capacity Scheduler, Fair Scheduler 等,以满足不同场景的需求。
ApplicationsManager (应用管理器): 负责管理集群中运行的所有应用,包括接受应用的提交请求、启动 AM、监控 AM 的运行状态等。
NodeManager (NM, 节点管理器): 运行在集群的每个节点上,负责管理本节点的资源(CPU, 内存, 磁盘, 网络等)和 Container。NM 会定期向 RM 汇报节点资源使用情况和 Container 运行状态,并接受 RM 的指令执行 Container 的启动、停止等操作。
ApplicationMaster (AM, 应用管理器): 每个 YARN 应用都有一个对应的 AM 实例,负责管理该应用的生命周期和资源协调。AM 的主要职责包括:
向 RM 注册应用并协商资源: AM 启动后首先向 RM 注册,并根据应用的需求向 RM 申请资源(Container)。
将任务分配到 Container: AM 获取到 Container 资源后,会将应用的各个任务分配到不同的 Container 中执行。
监控任务执行状态: AM 会监控 Container 中任务的执行状态,并处理任务失败、重试等情况。
与 RM 保持心跳: AM 需要定期向 RM 发送心跳,汇报自身状态,以便 RM 监控应用的运行情况。
应用完成或失败后向 RM 注销: 当应用完成或失败后,AM 会向 RM 注销,释放占用的资源。
Container (容器): YARN 中的资源分配单位,封装了 CPU、内存、磁盘、网络等资源。NM 负责创建和管理 Container,应用的任务在 Container 中运行。Container 提供了资源隔离和限制的功能,确保不同应用之间不会相互干扰。
Application Task (应用任务): 应用中实际的计算任务,例如 MapReduce 中的 Map Task 和 Reduce Task,Spark 中的 Task 等。应用任务运行在 YARN 分配的 Container 中。
YARN 应用的运行流程大致如下:
客户端提交应用: 用户通过 YARN 客户端提交应用,包括应用的 JAR 包、配置文件、启动命令等。
RM 接收应用请求: ResourceManager 接收到客户端的请求后,将应用信息放入调度队列。
RM 调度资源并启动 AM: Scheduler 根据调度策略和集群资源状况,为应用分配资源,并在某个 NodeManager 上启动 AM Container。
AM 向 RM 注册并申请资源: AM 启动后,向 RM 注册自身,并根据应用的需求向 RM 申请 Container 资源。
RM 分配 Container 资源: Scheduler 根据 AM 的资源请求和集群资源状况,为 AM 分配 Container 资源。
AM 在 Container 中启动任务: AM 获取到 Container 资源后,将应用的各个任务分配到不同的 Container 中执行。
Container 执行任务并向 AM 汇报状态: Container 中的任务执行过程中,会定期向 AM 汇报任务执行状态。
AM 监控任务状态并与 RM 保持心跳: AM 监控所有任务的执行状态,并定期向 RM 发送心跳,汇报应用状态。
应用完成或失败: 当所有任务执行完成或应用发生错误时,AM 通知 RM 应用完成或失败。
RM 注销应用并回收资源: ResourceManager 注销应用,并回收应用占用的所有资源。
为了更直观地理解 YARN 应用的开发过程,我们以一个简单的示例应用 "HelloYARN" 为例进行代码实践。该应用的功能非常简单,即在 YARN 集群中运行一个任务,打印 "Hello YARN!" 信息。
1. 项目结构:
首先,我们需要创建一个 Maven 项目,并添加 Hadoop YARN 相关的依赖。项目结构如下:
hello-yarn/ ├── pom.xml └── src/ └── main/ └── java/ └── com/example/ ├── HelloYARNApplicationMaster.java └── HelloYARNTask.java
2. pom.xml 文件:
在 pom.xml 文件中,添加 Hadoop YARN 客户端的依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>hello-yarn</artifactId> <version>1.0-SNAPSHOT</version> <properties> <hadoop.version>3.3.0</hadoop.version> <!-- 请替换为你的 Hadoop 版本 --> </properties> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>${hadoop.version}</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-yarn-client</artifactId> <version>${hadoop.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.example.HelloYARNApplicationMaster</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>assemble-all</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
注意: 请将 <hadoop.version> 属性替换为你实际使用的 Hadoop 版本。
3. HelloYARNTask.java 文件:
HelloYARNTask.java 文件定义了在 Container 中执行的任务逻辑,这里我们简单地打印 "Hello YARN!" 信息。
package com.example; public class HelloYARNTask { public static void main(String[] args) { System.out.println("Hello YARN!"); } }
4. HelloYARNApplicationMaster.java 文件:
HelloYARNApplicationMaster.java 文件是应用的核心,负责与 YARN 框架交互,申请资源,启动任务等。
package com.example; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.yarn.api.ApplicationConstants; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse; import org.apache.hadoop.yarn.api.records.*; import org.apache.hadoop.yarn.client.api.AMRMClient; import org.apache.hadoop.yarn.client.api.NMClient; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.util.ConverterUtils; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class HelloYARNApplicationMaster { public static void main(String[] args) throws Exception { Configuration conf = new YarnConfiguration(); // 1. 创建 AMRMClient 和 NMClient AMRMClient<AllocateResponse> amRmClient = AMRMClient.createAMRMClient(); amRmClient.init(conf); amRmClient.start(); NMClient nmClient = NMClient.createNMClient(); nmClient.init(conf); nmClient.start(); // 2. 向 RM 注册 AM RegisterApplicationMasterResponse appMasterResponse = amRmClient.registerApplicationMaster( "localhost", // host 0, // rpcPort "" // trackingUrl ); System.out.println("ApplicationMaster registered successfully!"); // 3. 申请 Container 资源 Resource capability = Resource.newInstance(256, 1); // 256MB 内存, 1 vcore Priority priority = Priority.newInstance(0); ResourceBlacklistRequest blacklistRequest = ResourceBlacklistRequest.newInstance(Collections.emptyList(), Collections.emptyList()); amRmClient.addContainerRequest(new ContainerRequest(capability, null, null, priority, false, blacklistRequest)); AllocateResponse allocateResponse = amRmClient.allocate(0); while (allocateResponse.getAllocatedContainers().isEmpty()) { Thread.sleep(100); allocateResponse = amRmClient.allocate(0); } Container container = allocateResponse.getAllocatedContainers().get(0); System.out.println("Container allocated: " + container.getId()); // 4. 在 Container 中启动任务 Map<String, String> env = new HashMap<>(); env.put("CLASSPATH", "./*"); // 设置 classpath,确保能找到 HelloYARNTask 类 LocalResource appJar = LocalResource.newInstanceAsRequired( ConverterUtils.getYarnUrlFromPath(ConverterUtils.getPathToUri( args[0])), // 应用 JAR 包路径,通过命令行参数传入 LocalResourceType.FILE, LocalResourceVisibility.APPLICATION, LocalResourceInitializationType.AT_CLIENT, 1, 1); Map<String, LocalResource> localResources = new HashMap<>(); localResources.put("app.jar", appJar); String command = ApplicationConstants.Environment.JAVA_HOME_DIR.$() + "/bin/java " + HelloYARNTask.class.getName() + " 1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout" + " 2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr"; ContainerLaunchContext ctx = ContainerLaunchContext.newInstance( localResources, env, Collections.singletonList(command), null, null, null ); nmClient.startContainer(container, ctx); System.out.println("Container started!"); // 5. 监控 Container 运行状态 ContainerStatus containerStatus = nmClient.getContainerStatus(container.getId(), container.getNodeId()); while (containerStatus.getState() == ContainerState.RUNNING) { Thread.sleep(1000); containerStatus = nmClient.getContainerStatus(container.getId(), container.getNodeId()); } System.out.println("Container finished with state: " + containerStatus.getState()); System.out.println("Container diagnostics: " + containerStatus.getDiagnostics()); // 6. 向 RM 注销 AM amRmClient.unregisterApplicationMaster(FinalApplicationStatus.SUCCEEDED, "HelloYARN finished", ""); System.out.println("ApplicationMaster unregistered!"); // 7. 关闭客户端 amRmClient.stop(); nmClient.stop(); } }
代码详解:
步骤 1: 创建 AMRMClient 和 NMClient: AMRMClient 用于与 ResourceManager 通信,NMClient 用于与 NodeManager 通信。
步骤 2: 向 RM 注册 AM: amRmClient.registerApplicationMaster() 方法向 ResourceManager 注册 ApplicationMaster。
步骤 3: 申请 Container 资源: amRmClient.addContainerRequest() 方法向 ResourceManager 发送 Container 资源请求。amRmClient.allocate() 方法轮询 ResourceManager,直到获取到分配的 Container。
步骤 4: 在 Container 中启动任务:
设置 Container 的环境变量 CLASSPATH,确保 Container 中可以找到 HelloYARNTask 类。
创建 LocalResource 对象,指定应用的 JAR 包路径。LocalResource 用于将应用所需的资源(如 JAR 包、配置文件等)分发到 Container 中。
构建 Container 启动命令 command,使用 Java 命令运行 HelloYARNTask 类。
创建 ContainerLaunchContext 对象,包含 Container 启动所需的资源、环境变量、命令等信息。
nmClient.startContainer() 方法在指定的 Container 中启动任务。
步骤 5: 监控 Container 运行状态: nmClient.getContainerStatus() 方法获取 Container 的运行状态,循环检查 Container 状态,直到 Container 运行结束。
步骤 6: 向 RM 注销 AM: amRmClient.unregisterApplicationMaster() 方法向 ResourceManager 注销 ApplicationMaster,释放占用的资源。
步骤 7: 关闭客户端: 关闭 AMRMClient 和 NMClient。
5. 打包应用:
使用 Maven 打包应用,生成包含依赖的 JAR 包。在项目根目录下执行命令:
mvn clean package
打包成功后,会在 target 目录下生成 hello-yarn-1.0-SNAPSHOT-jar-with-dependencies.jar 文件。
1. 上传应用 JAR 包:
将打包好的 hello-yarn-1.0-SNAPSHOT-jar-with-dependencies.jar 文件上传到 Hadoop 集群的 HDFS 文件系统中,例如 /user/yourusername/hello-yarn.jar。
2. 提交 YARN 应用:
使用 yarn jar 命令提交 YARN 应用。在 Hadoop 集群的客户端节点或配置了 Hadoop 客户端环境的机器上执行以下命令:
yarn jar /user/yourusername/hello-yarn.jar com.example.HelloYARNApplicationMaster /user/yourusername/hello-yarn.jar
命令详解:
yarn jar: YARN 客户端命令,用于提交 JAR 应用。
/user/yourusername/hello-yarn.jar: 应用的 JAR 包在 HDFS 上的路径。
com.example.HelloYARNApplicationMaster: ApplicationMaster 类的完整类名。
/user/yourusername/hello-yarn.jar: 作为命令行参数传递给 ApplicationMaster 的应用 JAR 包路径,用于 LocalResource 的创建。
3. 查看应用运行状态:
应用提交后,可以通过 YARN ResourceManager Web UI (通常地址为 http://<ResourceManagerHost>:<ResourceManagerWebAppPort>) 查看应用的运行状态、日志等信息。也可以使用 yarn application -list 命令在命令行查看正在运行的应用列表。
4. 查看应用日志:
应用的日志信息通常存储在 NodeManager 节点的日志目录下。可以通过 YARN ResourceManager Web UI 或命令行工具查看应用的日志。例如,可以使用 yarn logs -applicationId <application_id> 命令查看指定应用的日志。
5. 应用运行结果:
如果应用运行成功,可以在 Container 的标准输出日志中看到 "Hello YARN!" 的信息。日志文件路径通常在 NodeManager 节点的日志目录下,例如 hadoop-yarn-nodemanager/container-logs/container_<timestamp>_<application_id>_<container_id>/userlogs/stdout。
YARN 应用的部署策略可以根据实际应用场景和需求进行选择和调整。以下是一些常见的 YARN 应用部署策略:
本地模式部署: 适用于开发和测试环境,所有 YARN 组件(RM, NM, AM, Container)都运行在同一个 JVM 进程中。配置简单,但性能有限,不适合生产环境。
伪分布式模式部署: 适用于学习和实验环境,RM 和 NM 运行在同一台机器的不同进程中,模拟分布式环境。
全分布式模式部署: 生产环境常用的部署模式,RM 和 NM 分布式部署在不同的机器上,实现高可用和高性能。
容器化部署: 使用 Docker 等容器技术对 YARN 应用进行打包和部署,可以提高应用的隔离性、可移植性和部署效率。
云平台部署: 将 YARN 集群部署在云平台上,利用云平台的弹性伸缩和按需付费等特性,降低运维成本,提高资源利用率。
选择合适的部署策略需要考虑以下因素:
应用规模和性能需求: 对于大规模、高并发的应用,需要选择全分布式或云平台部署模式,以满足性能和扩展性需求。
环境要求: 开发测试环境可以选择本地模式或伪分布式模式,生产环境必须选择全分布式或云平台部署模式。
运维成本: 云平台部署模式可以降低运维成本,但可能会增加云服务费用。
安全性要求: 生产环境部署需要考虑安全性,例如 Kerberos 认证、授权等。
YARN 作为 Hadoop 生态系统的核心组件,为各种分布式计算框架提供了统一的资源管理平台。掌握 YARN 应用开发与部署技术,对于构建基于 Hadoop 的大数据应用至关重要。随着大数据技术的不断发展,YARN 将继续发挥重要作用,并不断演进和完善,以适应更多样化的应用场景和需求。未来,YARN 将更加注重资源利用率的提升、调度策略的优化、以及与新兴技术的融合,例如容器化、Serverless 等,为大数据应用提供更加高效、灵活、可靠的运行平台。