考察点
这题考察你对 Spark 运行时架构的整体把握,不是背流程图。面试官想听:driver 和 ApplicationMaster 在 client/cluster 两种模式下分别跑在哪、挂了会怎样;资源申请到任务执行的完整链路;以及生产上为什么长期任务用 cluster 模式。追问常往「driver OOM 在哪台机器上排查」「为什么 client 模式提交机断网 job 就挂」「动态资源分配怎么和 YARN 配合」方向走。
参考答案
先理清角色
Spark on YARN 涉及四个角色,先把它们摆清楚:
- ResourceManager (RM):YARN 的资源总管,负责分配容器(container)。
- ApplicationMaster (AM):每个 Spark 应用一个,向 RM 申请容器、和 NodeManager 通信启动 executor。
- Driver:Spark 应用的大脑,运行 SparkContext、DAG 调度、task 分发。它的位置是两种模式的核心区别。
- Executor:干活的,跑 task、存 cache 数据,跑在 NodeManager 的容器里。
Cluster 模式的完整流程
生产模式,driver 跑在集群里。一步步走:
- 提交:
spark-submit --master yarn --deploy-mode cluster把应用 jar 和依赖上传到 HDFS(staging 目录),向 RM 提交应用。 - RM 分配 AM 容器:RM 找一台 NodeManager 启动 AM 进程。cluster 模式下,这个 AM 进程里直接运行 driver(AM 和 driver 合一,是同一个 JVM)。
- AM/driver 向 RM 申请 executor 容器:按 spark.executor.instances、executor.cores、executor.memory 申请一批容器。
- NodeManager 启动 executor:RM 分配容器后,AM 通知对应的 NM 启动 executor JVM。executor 启动后反向注册到 driver(拿到 driver 的 host:port 主动连)。
- 任务执行:driver 的 DAGScheduler 按 stage 切 task,TaskScheduler 把 task 分发到 executor(考虑数据本地性),executor 执行后把结果/shuffle 数据回传 driver 或 MapOutputTracker。
- 结束:driver 退出,AM 通知 RM 释放所有容器,清理 staging 目录。
提交机(跑 spark-submit 的机器)只负责上传和提交,之后可以关机——job 的生命周期完全在 YARN 集群内部。
Client 模式的区别
--deploy-mode client 时,driver 跑在提交机上,AM 只负责申请资源(不运行 driver)。流程和 cluster 类似,但 driver 的 SparkContext 在提交机启动,executor 注册连的是提交机的 IP。
带来三个实际影响:
- 提交机挂了 job 就挂:driver 在提交机,断网/关机/进程被杀,executor 群龙无首,整个应用失败。
- 提交机网卡是瓶颈:executor 和 driver 之间的所有通信(task 分发、shuffle 位置汇报、结果收集)都走提交机网络。大 shuffle 或 collect 大结果时提交机网卡先爆。
- driver 内存压力在提交机:collect、广播 collect 阶段的 driver 内存是提交机的内存,不是 YARN 容器内存。
选型与生产实践
- 交互式/调试(spark-shell、pyspark、notebook):client 模式。driver 输出直接到本地,日志和 REPL 交互方便。
- 生产批任务/长期任务:cluster 模式。driver 在集群里,YARN 会重启失败的 AM(由 yarn.resourcemanager.am.max-attempts 控制),提交机只是跳板。
- driver 资源:cluster 模式下 driver 内存就是 AM 容器的内存,由 spark.driver.memory 控制,RM 按这个分配 AM 容器。client 模式下 spark.driver.memory 是提交机 JVM 的堆。
动态资源分配
开 spark.dynamicAllocation.enabled 后,executor 数不再固定:task 排队超过阈值就申请更多 executor(指数增长),executor 空闲超过 spark.dynamicAllocation.executorIdleTimeout(默认 60s)就释放。这是和 YARN 配合最实用的特性——大 job 抢资源、小 job 快速释放。
但必须配合外部 shuffle service(spark.shuffle.service.enabled):executor 被回收后它的 shuffle 文件由 NM 上的 ESS 进程代管,下游还能拉取。不开 ESS 的动态资源等于自残——executor 一回收,下游全是 FetchFailed。
排查视角
出问题时知道去哪看:
- driver 日志:cluster 模式在 AM 容器的日志里(yarn logs -applicationId),client 模式在提交机 stdout。
- executor 日志:各 NM 容器的日志,yarn logs 汇总。
- 资源申请卡住:看 RM UI 里应用是不是 ACCEPTED 但迟迟没 RUNNING,通常是资源不够或队列限制。
- FetchFailed 大面积出现:查 ESS 是否开启、executor 是否被异常回收。
可能的追问
- AM 挂了会怎样? YARN 会按 max-attempts 重启 AM。cluster 模式下 AM 就是 driver,重启等于 driver 重启——整个 job 从头再来(Spark 的 AM 重启不恢复中间状态)。client 模式 AM 重启只影响资源申请,driver 还在提交机上。
- 为什么 executor 是反向注册到 driver? executor 启动时不知道 driver 在哪(driver 可能先在跑),启动参数里带 driver 的地址,启动后主动连接注册。这也让 executor 可以在 driver 之后启动、失败重启后重连。
- yarn-cluster 和 yarn-client 的写法现在还有效吗? 旧写法(--master yarn-cluster / yarn-client)已废弃,统一用
--master yarn --deploy-mode cluster/client。面试里说新写法,顺便提一句旧写法说明你知道演进。