Spark 应用由 Driver 和专属 Executor 组成。集群管理器分配进程所需资源,Driver 决定运行哪些 Task,Executor 读写业务数据。部署时要分别确认进程位置、控制连接、数据路径和故障后的恢复责任。

本文采用 Apache Spark 当前部署文档描述的 Standalone、YARN 和 Kubernetes 原生运行方式;具体参数应与实际安装版本对齐。Mesos 已在 Spark 4.0 移除,不属于当前可选部署后端。

生产部署拓扑与组件职责

Driver 保存 SparkContext、调度器和运行时元数据。Executor 执行 Task、缓存分区,并提供 Shuffle 数据。集群管理器既不理解 SQL 算子,也不代替 Driver 调度 Task。

图 1:资源分配、任务调度与业务数据的三条路径

flowchart TB
  CM[集群管理器] -->|分配资源| E[Executor 进程]
  D[Driver 与 SparkContext] -->|Task 与控制消息| E
  E -->|状态与任务结果| D
  E <-->|分区数据| S[(HDFS 或对象存储)]
  E <-->|Shuffle 数据| P[其他 Executor]

图 1 的业务数据通常直接在 Executor、存储系统和其他 Executor 之间流动。collect 会把结果送回 Driver;普通文件写出不会先把全部数据汇集到 Driver。Driver 的内存与网络容量因此不能用 Executor 总容量替代。

Standalone 的 Master 和 Worker 是 Spark 自带的资源管理组件。Worker 启动 Executor;cluster 模式下也可启动 Driver。Worker 不执行用户的 Task,Task 在线程池中的 Executor 内运行。

图 2:Standalone 中 Master、Worker 与应用进程的关系

flowchart TB
  S[spark-submit] --> M[Standalone Master]
  M --> W1[Worker 节点一]
  M --> W2[Worker 节点二]
  W1 --> D[Driver:cluster 模式]
  W1 --> E1[Executor 一]
  W2 --> E2[Executor 二]
  D -->|Task| E1
  D -->|Task| E2

图 2 区分常驻的集群服务与随应用结束的进程。Standalone 的 Master 高可用和 Driver 重启是不同配置;Master 切换不会保存并恢复 Driver 的全部内存状态。Python 应用是否支持所选 deploy mode,也要按对应版本的提交文档检查。

YARN 的 Client 与 Cluster 模式

deploy mode 决定 Driver 的位置,并影响日志、网络和生命周期。client 模式的 Driver 在提交端,ApplicationMaster 单独运行并申请 Executor Container;cluster 模式把 Driver 放进 ApplicationMaster 的 Container。ResourceManager 分配资源,NodeManager 启动并监管所在节点的 Container。

图 3:YARN 两种部署模式中 Driver 与 ApplicationMaster 的位置

flowchart TB
  subgraph C[Client 模式]
    CD[提交端 Driver] <-->|资源请求与状态| CA[集群内 ApplicationMaster]
    CA --> CR[ResourceManager]
    CA -->|请求 NodeManager 启动| CE[Executor Container]
    CD -->|Task| CE
  end
  subgraph K[Cluster 模式]
    KS[提交端] --> KR[ResourceManager]
    KR -->|分配 AM Container| KA[ApplicationMaster 内的 Driver]
    KA -->|申请 Executor 资源| KR
    KA -->|请求 NodeManager 启动| KE[Executor Container]
    KA -->|Task| KE
  end

图 3 中 Executor 始终向 Driver 注册并接受任务。client 模式需要稳定的提交端进程及 Executor 到 Driver 的可达网络;关闭终端是否导致退出,取决于 Driver 是否随终端结束,不能把终端连接本身当作 Spark 的存活条件。cluster 模式的提交进程默认可以等待应用结束,但不承担 Driver 的执行责任。

Kubernetes 的原生提交链路

cluster 模式下,spark-submit 经 Kubernetes API 创建 Driver Pod。Driver 使用 ServiceAccount 与 API Server 交互,申请 Executor Pod;Kubernetes 调度器将 Pod 放到节点,kubelet 启动容器,随后 Executor 连接 Driver。原生提交不要求安装 Spark Operator。

图 4:Kubernetes 从提交到 Executor 注册的控制链路

flowchart TB
  S[spark-submit 与提交端凭据] -->|创建 Driver Pod| A[Kubernetes API Server]
  A --> K[调度器与 kubelet]
  K --> D[Driver Pod]
  D -->|ServiceAccount 请求 Executor Pod| A
  K --> E[Executor Pod]
  E -->|注册与心跳| D
  D -->|Task| E

图 4 的两组凭据分别属于提交端和 Driver,权限需求不同。Driver 通常需要操作本 namespace 的 Pod、Service 等资源;权限不足会表现为 Pod 无法创建,不能用增加 executor-memory 修复。Executor 还需要访问数据源的权限。

镜像需要提供匹配的 Spark 运行环境,用户代码可以预装在镜像中,也可采用官方支持的依赖分发方式。local:/// 表示容器内预先存在的路径。Pod 的 CPU request、limit、内存限制和 namespace 配额共同约束资源;namespace 本身不保证网络隔离或性能隔离。

Shuffle、缓存与可靠存储

Executor 的本地目录保存 Shuffle 和 spill 等中间数据;RDD 缓存也可能使用本地磁盘。挂载 PVC 只改变存储生命周期,不能自动让其他 Executor 找回 Shuffle 输出。Kubernetes 的 PVC Shuffle 恢复需要对应插件、目录和复用配置。

图 5:Executor 本地数据与持久化恢复数据的生命周期

flowchart TB
  E[Executor] --> L[(本地目录:Shuffle 与 spill)]
  E --> C[内存或磁盘缓存]
  E --> P[(可选 PVC)]
  E --> R[(可靠存储:输入、输出、checkpoint)]
  H[History Server] --> V[(持久化 Event Log)]
  D[Driver] --> V

图 5 中 Event Log 用于重建监控视图,不能替代流查询 checkpoint。ESS 可在 Executor 进程退出后继续服务仍存在的本地 Shuffle 文件,但无法恢复损坏的节点磁盘。Kubernetes 原生部署不提供内置 ESS;动态分配可使用 shuffle tracking 等满足条件的方案。

重试、重启与恢复边界

Task 重试由 Spark 调度器处理,Executor 丢失后可申请替代执行资源。Driver 丢失会结束当前应用执行,重新运行 Driver 是否恢复业务进度,取决于应用代码、持久化数据和提交平台的重启策略。Structured Streaming 可从兼容的 checkpoint 恢复;普通批作业需要自行处理输出重复与提交边界。

图 6:Task、Executor 与 Driver 故障的恢复责任

flowchart TB
  F[故障位置] --> T[Task 失败]
  F --> E[Executor 丢失]
  F --> D[Driver 退出]
  T --> TR[调度器按阈值重试 Task]
  E --> ER[补充 Executor 并重算丢失分区]
  D --> DR[提交平台或人工重新提交]
  DR --> CP{有兼容 checkpoint 或应用恢复逻辑}
  CP -->|有| R[恢复可持久化的进度]
  CP -->|无| N[重新执行并检查输出幂等]

图 6 中重新提交不等于恢复原来的 Driver 进程。原生 Kubernetes 提交不会自动提供一套无限重启应用的控制器;Driver Pod 的重启与业务恢复策略需要在交付环境中验证。

提交与排查入口

下面的命令是参数模板,JAR、镜像、namespace、ServiceAccount 和存储权限需要先在目标环境准备好。

1
2
3
4
5
6
7
8
9
spark-submit --master yarn --deploy-mode cluster \
--class example.Main --num-executors 3 --executor-memory 2g app.jar

spark-submit --master k8s://https://API_SERVER --deploy-mode cluster \
--class example.Main \
--conf spark.kubernetes.namespace=spark-jobs \
--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-driver \
--conf spark.kubernetes.container.image=REGISTRY/spark-app:VERSION \
--conf spark.executor.instances=3 local:///opt/spark/app.jar

提交后先区分资源未分配、进程启动失败和 Task 执行失败:YARN 查看 Application 状态、Container 日志和队列容量;Kubernetes 查看 Pod 状态、事件与 Driver 日志。Spark UI 的 Jobs、Stages 和 Executors 页面用于追踪已经开始执行的计算。启用 Event Log 并配置 History Server 后,应用结束仍可复查任务指标;进程 stdout/stderr 需要另行保留。

Executor 的容器内存还包含 memory overhead、显式堆外内存及适用的 PySpark 内存预算,不能把 --executor-memory 直接视作 Pod 或 Container 的总内存。对 YARN 和 Kubernetes 的性能比较,应控制镜像、CPU 限额、磁盘、网络及存储连接器;不能从资源管理器名称推断固定的启动或执行速度。

系列导航

序号 主题 状态
00 导读:为什么 Spark 的核心是一张 DAG 已完成
01 RDD:弹性分布式数据集的五大属性 已完成
02 宽依赖与窄依赖:Stage 是怎样划分出来的 已完成
03 DAG Scheduler 与 Task Scheduler:从逻辑计划到物理执行 已完成
04 Shuffle 机制:数据跨分区交换的代价与优化 已完成
05 内存管理与 Tungsten:堆外内存、序列化与代码生成 已完成
06 存储体系:Block Manager、广播变量与累加器 已完成
07 容错机制:Lineage、Checkpoint 与推测执行 已完成
08 Spark SQL 与 Catalyst:从 SQL 文本到物理算子树 已完成
09 DataFrame 与 Dataset:类型安全与性能的折中 已完成
10 Adaptive Query Execution:运行时改写执行计划 已完成
11 数据源 API 与谓词下推:让存储层帮忙过滤 已完成
12 Structured Streaming:把流当成无界表 已完成
13 水位线与状态管理:流处理的时间语义 已完成
14 Spark on YARN / K8s:资源管理与部署模式 本篇
15 动态资源分配与作业调度:多租户集群的资源博弈 下一篇
16 性能诊断:Spark UI、Event Log 与调优思路
17 Spark 与 Flink:两种计算引擎的设计选择

参考资料