Spark 动态资源分配配置指南:从 DRA 到 ESS

系统讲解 Spark 动态资源分配的参数配置、External Shuffle Service 依赖与各运行模式下的差异

袋鼠云
袋鼠云
经营分析

Spark 作业固定申请 Executor,低谷期资源闲置、高峰期排队,是集群利用率上不去的典型原因。开启动态资源分配(DRA)让 Executor 随负载伸缩,但很多团队一开就翻车:要么 Executor 缩容后 Shuffle 数据丢失导致任务重算甚至失败,要么 Executor 只增不减。根因在于没搞清 Executor 移除时 Shuffle 数据的归属。本文从原理与参数讲清 DRA 的正确配置路径,覆盖 External Shuffle Service(ESS)与 shuffle tracking 两条保障线。

DRA 原理与适用场景

DRA 由 Spark 调度器内的 ExecutorAllocationManager 驱动:监控待调度任务积压(backlog),积压超过阈值就按指数策略申请新 Executor;Executor 空闲超过阈值就移除。扩容看 backlog,缩容看 idle,两端都由参数控制。

适用场景:多租户共享集群、作业波峰波谷明显、资源空闲率高的集群。不适用场景:作业间强隔离要求的独占资源场景,以及需要严格预分配的场景。开启前必须回答一个问题:Executor 被移除后,它写的 Shuffle 数据怎么办?答案只有两个——交给 ESS 保存,或让 Spark 跟踪这些 Shuffle 块直到不再需要(shuffle tracking)。两者都不做,DRA 启动后必然踩 Shuffle 数据丢失。

扩容是逐轮翻倍的:积压持续超过阈值时,Spark 先申请 initialExecutors 个 Executor,之后每轮按当前规模的倍数继续申请,直到 maxExecutors;缩容则按 executorIdleTimeout 逐个回收空闲 Executor。这种指数式扩容配合阈值时间,能快速响应高峰又避免抖动。调度器通过后台线程周期性检查 pending task 队列与 Executor 空闲时间,判定粒度是秒级,所以作业提交后前几秒的资源变化是正常现象。

spark.dynamicAllocation 关键参数

核心参数清单:

spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=1
spark.dynamicAllocation.maxExecutors=100
spark.dynamicAllocation.initialExecutors=5
spark.dynamicAllocation.schedulerBacklogTimeout=5s
spark.dynamicAllocation.sustainedSchedulerBacklogTimeout=5s
spark.dynamicAllocation.executorIdleTimeout=60s
spark.dynamicAllocation.cachedExecutorIdleTimeout=infinity

参数语义与调法:

  • minExecutors/maxExecutors:设定伸缩边界。maxExecutors 要结合队列容量设置,防止单作业吃满集群;minExecutors 决定保底资源。
  • schedulerBacklogTimeout/sustainedSchedulerBacklogTimeout:任务积压多久开始扩容。默认 1s 偏激进,高峰抖动明显时可调到 5-10s。
  • executorIdleTimeout:Executor 空闲多久被回收,默认 60s。对短任务频繁提交的集群,过小会导致 Executor 反复创建销毁。
  • cachedExecutorIdleTimeout:持有缓存数据(persist/cache)的 Executor 默认永不回收(infinity)。想回收需要显式设有限值,否则这些 Executor 会成为缩不掉的钉子户。

参数组合有个常见起点:对波峰波谷明显的批作业,minExecutors 按日常低谷需求设 1-2 个,initialExecutors 设 5 左右让作业启动即有基本并行度,maxExecutors 取队列容量的四到六成,防止单作业吃满集群;对交互式查询(Thrift Server 场景),minExecutors 要设高一些保证响应,并把 executorIdleTimeout 调小让空闲连接尽快释放。参数要按作业类型沉淀成模板,而不是每个作业临时改。观察节奏上,先记录一周的 Executor 曲线,确认扩容发生在 stage 高峰、缩容发生在低谷,再决定是否调整阈值;频繁增删说明阈值过小,曲线长期平直说明负载本身平稳,DRA 收益有限,可以考虑关闭。

离线开发 BatchWorks

支持多模态、覆盖集成开发调度运维的一站式离线数据开发平台

了解产品

ESS 的依赖与配置

ESS 把 Shuffle 文件写到独立于 Executor 的常驻服务,Executor 被移除或失败后,下游仍能拉取数据。没有 ESS 或 shuffle tracking 时,DRA 移除 Executor 等于连 Shuffle 数据一起删掉。

YARN 模式下配置 ESS 的标准步骤:

  1. 把 Spark 发行包里版本号对应的 yarn-shuffle jar 复制到每台 NodeManager 的 classpath(如 $HADOOP_HOME/share/hadoop/yarn/lib),注意版本与 Spark 匹配。
  2. 在 yarn-site.xml 中把 spark_shuffle 加入 yarn.nodemanager.aux-services,并指定实现类 org.apache.spark.network.yarn.YarnShuffleService。
  3. 重启 NodeManager,确认 spark.shuffle.service.port(默认 7337)监听正常。
  4. 作业侧配置 spark.shuffle.service.enabled=true、spark.shuffle.service.port=7337。

yarn-site.xml 中对应的配置片段:

<property>
  <name>yarn.nodemanager.aux-services</name>
  <value>mapreduce_shuffle,spark_shuffle</value>
</property>
<property>
  <name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
  <value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>

注意 mapreduce_shuffle 要保留,否则影响 MR 作业;改完必须滚动重启 NodeManager。ESS 的 shuffle 服务运行在 NodeManager 进程内,会占用额外线程与内存,节点规格小时要预留资源。

另一种做法是 shuffle tracking:设置 spark.dynamicAllocation.shuffleTracking.enabled=true(Spark 3.0 起支持)。Spark 跟踪 Executor 持有的 Shuffle 块,只要还有活跃 job 需要这些数据就保留该 Executor,直到不再需要或超过 shuffleTracking.timeout(默认无限)才释放。它不需要额外部署服务,代价是带着 Shuffle 数据的 Executor 不能立即缩,缩容效率低于 ESS。Spark 文档明确:启用 DRA 需要满足 ESS、shuffle tracking、shuffle 块退役三者之一。

常见坑

  • Shuffle 数据丢失:最典型。表现为 stage 重算、FetchFailedException、Connection refused。原因就是 DRA 已开但 ESS 与 shuffle tracking 都没开,或 ESS 部署了但版本与 Spark 不匹配。排查顺序:先确认 spark.shuffle.service.enabled 与 yarn-site 的 aux-service 是否真的生效,再看 7337 端口连通性。
  • Executor 无法缩容:先查 cachedExecutorIdleTimeout 是否被 RDD 缓存卡住,再查是否一直有 task 在跑(数据倾斜或调度问题让 Executor 永远不空闲)。
  • 扩容抖动:backlog 阈值太小,Executor 频繁增删;对高峰短暂的作业,把 sustainedSchedulerBacklogTimeout 调大并设合理的 minExecutors。
  • 与 AQE 的关系:DRA 管执行资源,AQE 管执行计划(动态合并与拆分分区、动态切换 Join 策略),两者独立可同时开启。AQE 合并分区后并行度下降,配合 DRA 缩容效果更明显;反之 AQE 拆分分区会触发 DRA 扩容。混用时要先确认集群满足 DRA 的 Shuffle 保障条件。
  • 与常驻会话(如 Spark Thrift Server)搭配:常驻 SparkSession 的 DRA 语义与单作业不同,需要单独验证版本行为,别拿单作业经验直接套。
  • 配置冲突:作业里同时显式指定 spark.executor.instances 与 spark.dynamicAllocation.enabled=true 时,不同版本对固定实例数的处理有差异,规范做法是动态分配作业不写 spark.executor.instances,只依赖 min/max/initial。
  • Standalone 模式差异:需要每个 Worker 单独部署 shuffle 服务,且早期版本在 Standalone 上开启 DRA 的限制更多,升级前先确认版本支持矩阵。

除了 ESS 与 shuffle tracking,还有第三条满足 DRA 前置条件的路径:shuffle 块退役。Spark 3.1 起支持优雅退役(graceful decommission),Executor 被移除前先把 shuffle 块与缓存数据迁移到存活节点,从根上避免数据丢失,代价是退役过程需要额外时间与网络开销。三选一即可,混用时要先确认版本行为一致。

验证 DRA 是否生效也有固定套路:Spark UI 的 Executors 页看 Executor 数量曲线是否随 stage 起伏,Driver 日志里搜索 requesting executors 与 removing executor 关键字确认增删动作,配合集群资源监控看队列占用。如果 Executor 数量恒定不变,多半是参数没生效(比如提交脚本里被覆盖)或作业负载太均匀没有空闲窗口。

经营分析

围绕目标、预算、执行、异常与归因,构建经营分析、过程监控、问题定位与决策闭环。

了解解决方案

YARN 与 K8s 下的差异

YARN 模式是 DRA 最成熟的形态:ESS 有官方部署路径,Executor 作为 container 由 YARN 统一分配回收,配合队列容量管理,集群利用率提升最直接。

K8s 模式差异明显:Spark on Kubernetes 没有开箱即用的 ESS,自行以 DaemonSet 部署成本高,官方推荐直接开 shuffle tracking 来支持 DRA;Executor 的增删由 Kubernetes API 完成,扩容受集群资源配额与 Pod 调度速度影响,冷启动比 YARN 慢;K8s 上如果 shuffle tracking 关闭且无 ESS,启用 DRA 的作业会直接报错。若集群引入远程 Shuffle 服务(RSS,如 Celeborn),则不再需要 ESS 或 shuffle tracking,配置上反而要显式关掉 shuffle tracking 避免冲突。

下一步

先小范围验证:挑一个波峰波谷明显的作业,K8s 上开启 shuffle tracking,YARN 上先部署 ESS,minExecutors 设 1、maxExecutors 按队列容量一半起,观察一周的 Executor 数量曲线与任务失败率;确认无 Shuffle 丢失后,再逐步放开到核心作业,并把 maxExecutors、backlog 阈值沉淀为按作业类型的模板参数。切记先保障 Shuffle 数据安全,再谈资源利用率,顺序不能反。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信