YARN 是如何为 Application 启动第一个 Container(即 ApplicationMaster 所在的容器)的?
在 YARN(Yet Another Resource Negotiator)中,启动应用程序的第一个 Container(即运行 ApplicationMaster (AM) 的 Container)是整个作业生命周期中最关键的一步。也就是所谓的“鸡生蛋,蛋生鸡”的问题中的“第一只鸡”。
这个过程由 Client(客户端)、ResourceManager (RM) 和 NodeManager (NM) 协同完成。以下是 YARN 为 Application 启动第一个 Container 的详细步骤:
第一阶段:客户端提交应用程序 (Client Submission)
- 申请 Application ID:
客户端(例如提交 Spark 或 MapReduce 任务的机器)首先向 ResourceManager (RM) 发送 RPC 请求(getNewApplication),要求分配一个新的 Application ID。 - 构建上下文 (Context):
客户端在本地准备应用程序的启动信息,最核心的是构建ApplicationSubmissionContext。其中包含了启动第一个 Container 所需的所有“配方”,这个配方叫做ContainerLaunchContext(CLC)。
CLC 包含了:- Local Resources(本地资源):需要下载到运行节点的 JAR 包、配置文件等。
- Environment Variables(环境变量):AM 运行所需的环境变量。
- Commands(启动命令):实际启动 AM 进程的命令行(例如
java -Xmx1024m org.apache.spark.deploy.yarn.ApplicationMaster ...)。 - Resource 需求:AM 需要的 CPU 和内存大小。
- 正式提交:
客户端向 RM 发送submitApplication请求,将上述 Context 提交给 RM。
第二阶段:ResourceManager 分配资源 (RM Allocation)
- 接收与校验:
RM 接收到请求后,会进行权限校验、配额检查,并将该 Application 放入状态机中(状态变为NEW->SUBMITTED->ACCEPTED)。 - 调度器分配资源:
RM 内部的 Scheduler(调度器,如 Capacity / Fair Scheduler) 会将该应用加入到指定的队列中等待。
当集群中有足够的资源时,调度器会为这个 Application 分配第一个 Container,并选定一台 NodeManager 节点来运行它。 - 准备启动 AM:
RM 内部有一个名为ApplicationMasterLauncher的服务。当调度器分配好资源后,ApplicationMasterLauncher会接管任务,准备与被选中的那个 NodeManager 通信。
RM 会生成一个ContainerToken(安全令牌)和一个AMRMToken(用于后续 AM 和 RM 通信的凭证),连同客户端最初提供的ContainerLaunchContext一起打包。
第三阶段:NodeManager 启动第一个 Container (NM Launch)
- RM 通知 NM 启动:
RM 的ApplicationMasterLauncher作为客户端,通过ContainerManagementProtocol协议,直接向选定的 NodeManager 发送startContainersRPC 请求。 - 资源本地化 (Localization):
NodeManager 接收到请求并验证 Token 通过后,第一件事是资源本地化。它会从 HDFS 下载ContainerLaunchContext中指定的 JAR 包、配置文件等,存放到该节点本地磁盘的特定目录下。 - 创建隔离环境:
NodeManager 根据资源需求(CPU、内存),通过底层的隔离机制(如 Linux Cgroups)为该 Container 创建一个隔离的运行环境。 - 执行启动命令:
NodeManager 解析出ContainerLaunchContext中的启动命令(Commands),在准备好的目录和隔离环境中执行该脚本/命令。
至此,ApplicationMaster 的进程被成功拉起,第一个 Container 正式运行!
第四阶段:ApplicationMaster 初始化与注册 (AM Registration)
AM 进程启动:
ApplicationMaster 进程启动后,它做的第一件事是初始化自身的服务。向 RM 注册:
AM 通过ApplicationMasterProtocol协议向 ResourceManager 发送registerApplicationMaster请求。- 这一步非常重要!AM 会把自己的 RPC 端口、Tracking URL(提供给用户看日志的 Web UI 地址)告诉 RM。
- RM 收到注册后,Application 的状态会从
ACCEPTED变为RUNNING。
后续工作(接管控制权):
注册成功后,AM 就正式接管了该应用程序。接下来,AM 会计算应用还需要多少资源(多少个 Map/Reduce task 或者 Spark Executor),并自己向 RM 申请后续的 Container,然后亲自去对应的 NM 上启动它们。
总结流程图
可以把这个过程简单想象成以下对话:
- Client -> RM: "我要跑个任务,这是运行总控程序(AM)需要的内存、JAR包和命令行,请帮我找台机器启动它。"
- RM (Scheduler): "排队中... 好的,NodeManager-A 资源够用,决定是你了。"
- RM -> NM-A: "这是启动配置和令牌,立刻在你那里启动这个 AM 容器!"
- NM-A: "收到,正在下载 JAR 包,限制好内存,执行
java -jar AM.jar... 启动成功!" - AM (在 NM-A 上) -> RM: "老大我活了!这是我的地址,接下来申请其他干活的 Container 就由我来跟你交涉了。"
核心区别提示:
- 第一个 Container(AM 所在的 Container) 是由 ResourceManager 主动要求 NodeManager 启动的。
- 后续的所有 Container(实际干活的 Task/Executor) 都是由 ApplicationMaster 申请资源后,由 AM 去要求 NodeManager 启动的。