Spark 集群部署与调优 -- 动态资源分配(Dynamic Resource Allocation, DRA)

Spark集群动态资源分配部署与调优

Spark 集群部署中的动态资源分配(Dynamic Resource Allocation, DRA),这是生产环境提升集群资源利用率和作业吞吐量的关键机制。

一、 动态资源分配的核心价值

  • 痛点: 在静态资源配置下(spark.executor.instances 固定):
    • 资源浪费: Spark 应用在启动时会一次性申请所有 Executor,即使任务初期或末期不需要那么多资源(如小任务、任务收尾阶段),导致集群资源闲置。
    • 资源不足: 对于长任务或数据量突增的任务,初始分配的 Executor 可能不够用,无法动态扩展,导致任务变慢甚至失败。
    • 队列阻塞: 大应用长时间占用大量资源,阻塞了其他小应用的提交。
  • 解决方案: 动态资源分配 (DRA)
    • 按需伸缩: Spark 应用在运行期间根据当前负载(主要是待处理的 Task 数量)动态地申请(Scale Up)和释放(Scale Down)Executor 资源
    • 核心目标:
      • 最大化集群资源利用率: 让空闲资源能被其他应用使用。
      • 提升作业吞吐量: 集群可以同时运行更多应用。
      • 应对负载波动: 自动适应任务不同阶段对资源的需求变化。

二、 动态资源分配的工作原理与关键组件

  1. 依赖基础:External Shuffle Service (ESS)

    • 为什么必需? 这是 DRA 能释放 Executor 的前提!
    • 问题: Executor 退出时,其持有的由 Map Task 产生的 Shuffle 数据也会消失。Reduce Task 后续需要这些数据时就会失败。
    • 解决方案:ESS
      • 一个长期运行在每个 Worker 节点上的独立服务(通常与 NodeManager 同节点)。
      • Map Task 将 Shuffle 数据写入 ESS 管理的存储(通常是磁盘),而不是 Executor 的本地目录。
      • Reduce Task 直接从 ESS 读取 Shuffle 数据。
      • 关键好处: Executor 可以安全退出(即使它持有过 Map Task 的输出),因为 Shuffle 数据已由 ESS 托管。Reduce Task 后续拉取数据时只需找 ESS,不依赖特定的 Executor。
    • 部署: 需要在集群所有 Worker 节点上部署并启动 ESS。YARN 模式下通常是 spark-<version>-yarn-shuffle.jar,Standalone 模式有对应的 shuffle service。
  2. 核心流程:申请与释放 Executor

  • 角色:
    • Spark Driver: 决策者。根据策略判断是否需要申请或移除 Executor。
    • 集群管理器 (Cluster Manager): 资源提供者。YARN ResourceManager 或 Standalone Master。负责实际分配/回收容器(Container)或 Worker。
    • External Shuffle Service (ESS): 保障者。确保 Shuffle 数据在 Executor 移除后依然可用。
  • Executor 申请 (Scale Up):
    1. Spark Scheduler 检测到待调度(Pending)的 Task 数量积压。
    2. 积压量超过阈值(spark.dynamicAllocation.schedulerBacklogTimeout 触发后,且积压持续存在)。
    3. Driver 向 Cluster Manager 申请新的 Executor。
    4. Cluster Manager 在可用节点上启动新的 Container/Worker,并启动 Executor。
    5. 新 Executor 向 Driver 注册,开始领取 Task 执行。
  • Executor 释放 (Scale Down):
    1. Executor 处于空闲状态(没有运行 Task,且其 Shuffle 数据已被 ESS 托管或不再需要)。
    2. 空闲时间持续超过设定的阈值(spark.dynamicAllocation.executorIdleTimeout)。
    3. Driver 标记该 Executor 为可移除。
    4. Driver 通知 Cluster Manager 释放该 Executor 占用的资源(Container/Worker)。
    5. Executor 优雅退出。即使它曾持有 Map 输出,Reduce Task 后续也能通过 ESS 获取数据。
  • Shuffle 数据生命周期管理:
    • Driver 会跟踪每个 Executor 上产生的 Shuffle 数据(通过 BlockManager)。
    • 当确定所有依赖于某 Executor 上 Map 输出的 Reduce Task 都已完成后,Driver 会通知 ESS 可以安全删除该 Executor 产生的过期 Shuffle 数据(通过 spark.dynamicAllocation.shuffleTracking.timeout 控制保守删除)。

三、 关键配置参数详解 (spark-defaults.conf 或 spark-submit --conf)

  1. 基础开关:

    • spark.dynamicAllocation.enabled: true (启用 DRA)
  2. Executor 数量范围:

    • spark.dynamicAllocation.initialExecutors初始 Executor 数量。可以设置得较小(如 1 或 2),让应用快速启动。如果设置了 spark.executor.instances,它会覆盖此值作为初始值,但 DRA 仍会调整。
    • spark.dynamicAllocation.minExecutors最小 Executor 数量。即使空闲,也不会低于这个数。避免频繁申请小量 Executor 的开销。重要! 通常 >=1,确保 Driver 有 Executor 可用。
    • spark.dynamicAllocation.maxExecutors最大 Executor 数量。资源申请的上限,防止单个应用占用整个集群。必须设置! 根据队列配额和总资源设定。
  3. Scale Up (申请) 相关:

    • spark.dynamicAllocation.schedulerBacklogTimeout首次积压触发申请等待时间。当有待处理任务出现后,等待多久才触发第一次申请 Executor?默认:1s。适用于初始启动或突发负载。
    • spark.dynamicAllocation.sustainedSchedulerBacklogTimeout持续积压触发后续申请等待时间。在已有积压且已有申请发出后,如果积压仍然存在,等待多久后触发申请下一个 Executor?默认:schedulerBacklogTimeout (通常也是 1s)。控制申请速度。
  4. Scale Down (释放) 相关:

    • spark.dynamicAllocation.executorIdleTimeoutExecutor 空闲超时时间。一个 Executor 没有运行任何 Task 且其 Shuffle 数据可安全移除(或已不再需要)后,等待多久会被释放?默认:60s关键调优点!
      • 调大 (如 120s, 300s): 减少 Executor 因短暂空闲被频繁启停的开销(启动耗时、JVM 预热),适用于 Task 执行时间波动较大或有批次间隔的应用(如 Structured Streaming)。代价是资源释放稍慢。
      • 调小 (如 30s): 更快释放空闲资源,提高集群整体利用率。适用于 Task 密集且执行时间较短的应用。可能增加启停开销。
    • spark.dynamicAllocation.cachedExecutorIdleTimeout持有缓存的 Executor 空闲超时时间。如果一个空闲 Executor 的节点上还缓存了 RDD 分区(通过 persist()cache()),则使用此超时(通常远大于 executorIdleTimeout默认:infinity - 永不释放)。避免因释放 Executor 导致缓存失效。如果确定缓存不重要或可重建,可以设置一个有限值。
  5. Shuffle 数据跟踪与清理:

    • spark.dynamicAllocation.shuffleTracking.enabled是否启用 Shuffle 数据跟踪默认:true (强烈推荐)。Driver 精确跟踪 Executor 产生的 Shuffle 数据被哪些 Reduce Task 使用,确保数据在被依赖时不会因 Executor 退出而丢失。这是安全释放 Executor 的关键保障。
    • spark.dynamicAllocation.shuffleTracking.timeoutShuffle 数据保留超时。在最后一个依赖某 Block(Shuffle 数据块)的 Task 完成后,Driver 会等待此超时时间再通知 ESS 删除该 Block。这是一个保守的机制,防止因消息延迟等导致删除过早。默认:executorIdleTimeout * 2 (通常 120s)。一般无需修改,除非遇到非常极端的环境。
  6. 与集群管理器集成:

    • YARN:
      • 确保 yarn.nodemanager.aux-services 包含 spark_shuffleyarn.nodemanager.aux-services.spark_shuffle.class 指向 org.apache.spark.network.yarn.YarnShuffleService。这是部署 ESS 的配置。
      • spark.shuffle.service.enabled必须设置为 true。告诉 Spark 使用 ESS。
      • spark.yarn.shuffle.stopOnFailure:ESS 失败时是否停止 NodeManager(默认 false,生产环境通常保持 false)。
    • Standalone:
      • spark-env.sh 中配置 SPARK_DAEMON_MEMORYSPARK_WORKER_OPTS 启动 Worker 进程内的 Shuffle Service。
      • 同样需要设置 spark.shuffle.service.enabled=true
      • spark-defaults.conf 中配置 spark.shuffle.service.port(默认 7337)。

四、 部署、调优步骤与最佳实践

  1. 部署 External Shuffle Service (ESS):

    • YARN: 按照 Spark 官方文档配置 yarn-site.xml 并分发。确保所有 NodeManager 重启后加载了新的 Shuffle Service。检查 NodeManager 日志确认 ESS 启动成功。
    • Standalone:spark-env.sh 中配置好,重启 Worker 进程。
    • K8s: Spark 3.0+ 支持,配置更复杂,需部署 spark-shuffle DaemonSet 并使用 K8s 的 init-containersidecar 模式。
  2. 配置 Spark 应用启用 DRA:

    • spark-defaults.conf (集群级) 或 spark-submit --conf (应用级) 设置核心参数:
      spark.dynamicAllocation.enabled true
      spark.shuffle.service.enabled true # 必须开启 ESS
      spark.dynamicAllocation.minExecutors 1     # 推荐 >=1
      spark.dynamicAllocation.maxExecutors 100   # 必须设置,根据实际情况调整
      spark.dynamicAllocation.initialExecutors 2 # 可调整
      spark.dynamicAllocation.executorIdleTimeout 60s # 关键调优参数
      # 其他参数根据需要调整
      
  3. 关键调优点:

    • executorIdleTimeout
      • 观察点: 在 Spark UI 的 Executors 页,看 Executor 的 “Active Tasks” 和 “Removed Reasons”。如果看到大量 Executor 因为 idle 被移除,但很快又有新任务需要启动 Executor,且移除和新启动之间的间隔很短(小于 JVM 启动+任务调度开销),说明 executorIdleTimeout 可能设得太小了,导致“抖动”。
      • 建议: 结合 Task 的平均执行时间和批次间隔(对于流处理)来设置。如果 Task 执行时间在几秒到几十秒,60s 通常是合理的起点。如果 Task 执行时间非常短(<1s)且密集,可以尝试更小(如 30s),但需监控启停开销。如果 Task 执行时间长或存在明显批次空闲期(如 Streaming 的微批次间隔),可以增大(如 120s, 300s)。
    • minExecutors/maxExecutors
      • minExecutors:保证 Driver 至少有一个 Executor 可用(用于运行 Driver 的 Task 或其他必须任务),推荐 >=1。对于对延迟敏感的小作业,可以设大点避免冷启动。
      • maxExecutors严格设置! 根据 YARN 队列配额、集群总资源、应用优先级来设定。防止单个应用耗尽资源。
    • schedulerBacklogTimeout/sustainedSchedulerBacklogTimeout
      • 控制申请速度。如果集群资源充足且希望快速响应积压,保持默认 1s 即可。如果担心申请太快冲击集群,可以适当增大(如 2s, 5s),但这会稍微增加处理积压的延迟。
  4. 最佳实践:

    • 必开 ESS: 没有 ESS,DRA 无法安全释放 Executor,几乎等同于无效或极其危险。
    • 明确资源边界: 务必设置 minExecutorsmaxExecutors
    • 结合监控调优 idleTimeout 利用 Spark UI 观察 Executor 生命周期和移除原因,避免频繁启停抖动。目标是让 Executor 在真正长时间空闲时才释放。
    • 理解缓存影响: 默认持有缓存的 Executor 不会因 idleTimeout 释放。如果应用大量缓存且希望释放资源,需权衡:要么接受缓存占用资源,要么调低 cachedExecutorIdleTimeout(可能导致缓存失效和重算开销)。
    • AQE 协同: Spark 3.0+ 的 Adaptive Query Execution (AQE) 可以动态调整 Shuffle 分区数。DRA 和 AQE 是互补的:DRA 调整 Executor 数量(计算资源),AQE 调整 Task 数量(并行度)。强烈建议同时开启 AQE (spark.sql.adaptive.enabled=true)
    • 测试验证:
      • 提交一个长任务(如循环处理数据),观察 Executor 数量是否随负载变化。
      • 观察 Spark UI Executors 页的 “Added” / “Removed” 原因和时间戳。
      • 检查集群管理器(YARN RM Web UI)的资源使用情况,看资源是否被及时释放和复用。
    • 日志排查: 关注 Driver 日志中关于 DRA 的决策信息(申请/释放 Executor 的原因)。Executor 日志关注与 ESS 的连接和 Shuffle 读写情况。

五、 注意事项与限制

  1. 非 Shuffle 阶段: DRA 主要根据 Shuffle 边界后的待调度 Task 数量 来决策。对于完全没有 Shuffle 的 Stage(如 map -> collect),DRA 可能不会申请超过 initialExecutors 的 Executor,因为 Driver 看到的是单个 Stage 的 Task,且没有后续 Stage 的积压。对于这种纯 Map 作业,如果希望并行度超过 initialExecutors * executor-cores,可能需要手动设置 spark.executor.instances 或使用 coalesce/repartition 并配合较大的 minExecutors
  2. 流式处理 (Structured Streaming): DRA 同样适用。关键调优点是 executorIdleTimeout 需要大于微批次间隔 (trigger)。例如,间隔 1min,idleTimeout 至少设 2min 或更多,防止 Executor 在批次间隔期间因短暂空闲被移除,导致下个批次需要冷启动 Executor。spark.streaming.dynamicAllocation.enabled (旧 DStream) 和 Structured Streaming 的 DRA 行为略有不同,需注意文档。
  3. Executor 启动开销: 申请新 Executor 涉及 JVM 启动、网络连接、类加载、注册等过程,需要一定时间(秒级到十秒级)。如果负载增长非常快且需要极低延迟响应,频繁申请可能带来额外延迟。适当增大 minExecutors 或调高 idleTimeout 可以减少申请频率。
  4. 资源碎片化: 在集群负载极高时,DRA 可能因为申请不到满足资源需求(如特定内存大小)的 Container 而无法扩展,即使集群有总量足够的零散资源。
  5. 外部依赖: DRA 需要集群管理器(YARN/K8s/Standalone)的支持。Mesos 对 DRA 的支持相对较弱。

总结

Spark 动态资源分配 (DRA) 是提升大型集群资源利用率和作业吞吐量的核心机制。其核心在于按需伸缩 Executor,依赖 External Shuffle Service (ESS) 保障 Shuffle 数据的可用性以实现 Executor 的安全释放。

成功部署调优 DRA 的关键步骤:

  1. 正确部署并启用 ESS(非可选!)。
  2. 开启 DRA (spark.dynamicAllocation.enabled=true)
  3. 设置合理的资源范围 (minExecutors, maxExecutors)maxExecutors 必须设。
  4. 精细调优 executorIdleTimeout,平衡资源释放速度与 Executor 启停开销,利用 Spark UI 监控指导。
  5. 结合 AQE 使用 获得最佳效果。
  6. 理解应用特点(批处理/流处理、有无 Shuffle、缓存重要性)进行针对性配置。
  7. 充分测试验证 决策逻辑和资源变化是否符合预期。

通过合理配置和调优 DRA,可以让 Spark 集群的资源如水一般流动起来,最大化利用效率,支撑更高并发和更复杂的混合负载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值