Spark Executor 因节点内存超限被杀的分析与应对

1. 问题现象

复制代码
ERROR YarnScheduler: Lost executor 27 on 6.39.40.60: 
Container container_e327_1778748497801_10300557_01_000028 on host: 6.39.40.60 was aborted. 
Exit status: -100. 
Diagnostics: The physical resource utilization threshold for NM service has been exceeded. 
EXCEEDED_RESOURCE_TYPE_MEM.

关键信息:

  • Exit status: -100:容器被 YARN NodeManager 强制终止
  • EXCEEDED_RESOURCE_TYPE_MEM:物理内存使用超限
  • Lost executor 27:第 27 号 Executor 被丢失

2. 两层内存监控机制

YARN 的 NodeManager 有两层内存监控,理解它们的区别是定位问题的关键:

层级 触发条件 典型报错 性质
容器级别 单个容器自身内存超过 executor.memory + memoryOverhead Container is running beyond physical memory limits 自己超限,怪自己
节点级别 物理机整体内存超过 NM 服务阈值 The physical resource utilization threshold for NM service has been exceeded 被邻居连坐

本文讨论的是节点级别的情况------你的 Executor 自身内存使用正常,但因为同节点上其他容器占用了大量内存,导致物理机整体内存吃紧,NM 为了自保杀掉了部分容器,你的 Executor 不幸被选中。

3. 常见的错误应对:加大内存

很多人的第一反应是增大 spark.executor.memory,但这是一种浪费:

  • 问题不在你的 Executor 自身内存超限,而是节点整体资源紧张
  • 加大内存反而增加了单容器对节点资源的占用,可能加剧问题
  • 你的任务实际需要的内存并没有变,多分配的部分完全浪费

4. 正确策略:分散 + 容错

既然根因是节点级别的资源争抢,应对思路应该是降低被连坐概率增强自动恢复能力

4.1 减小 executor.cores

properties 复制代码
spark.executor.cores=2

每台机器能容纳的 Executor 数量减少 → 同节点上并发容器更少 → 节点内存压力降低。内存总量不变,但减少了"邻居"密度。

4.2 开启动态分配

properties 复制代码
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=20
spark.dynamicAllocation.executorIdleTimeout=60s

空闲时自动释放 Executor,减少占坑时间,降低对集群资源的持续占用。

4.3 增大任务重试次数

properties 复制代码
spark.task.maxFailures=6

默认值为 4。当 Executor 被杀后,运行在其上的 Task 会自动重试,增大重试次数可以提高最终成功率,让任务有机会被调度到其他健康的节点上。

4.4 启用黑名单机制

properties 复制代码
spark.blacklist.enabled=true
spark.blacklist.maxTaskAttemptsPerNode=2
spark.blacklist.maxBlacklistFraction=0.5

当某个节点上连续出现 Executor 被杀的情况,将该节点加入黑名单,后续任务调度时自动避开,不再分配到问题节点。

5. 推荐配置组合

properties 复制代码
# 不加内存,分散+容错
spark.executor.cores=2
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=20
spark.dynamicAllocation.executorIdleTimeout=60s
spark.task.maxFailures=6
spark.blacklist.enabled=true

核心逻辑:问题在节点不在你,策略是"分散+容错"而非"加内存硬扛"。

6. YARN 内存监控机制详解

6.1 三种内存控制模式

YARN 提供了三种递进的内存控制模式,由不同的配置参数组合决定:

模式 配置 机制 优缺点
Level 0:无控制 P=false, V=false, CG=false, E=false 不监控内存 容器可以无限制使用内存,危险
Level 1:轮询监控(Legacy) P=true 或 V=true NM 线程定期采样容器内存,超限则杀 有延迟,可能导致节点 OOM
Level 2:cgroups 严格控制 CG=true, C=true, P=true 或 V=true 内核 OOM killer 直接杀超限容器 即时生效,但容器不能 burst
Level 3:cgroups 弹性控制 CG=true, E=true, P=true 或 V=true 允许 burst,仅节点整体超限时才杀 利用率最高,但实现复杂

参数含义:

  • P = yarn.nodemanager.pmem-check-enabled(物理内存检查)
  • V = yarn.nodemanager.vmem-check-enabled(虚拟内存检查)
  • C = yarn.nodemanager.resource.memory.enforced(cgroups 严格内存执行)
  • E = yarn.nodemanager.elastic-memory-control.enabled(弹性内存控制)
  • CG = cgroups 前置条件(LinuxContainerExecutor + memory.enabled)

6.2 轮询监控的工作流程(最常见模式)

大多数 YARN 集群默认使用 Level 1 轮询监控 ,核心由 ContainersMonitorImpl 实现:

复制代码
┌─────────────────────────────────────────────────────────────┐
│  ContainersMonitorImpl.monitoringThread                      │
│  (每隔 monitoringInterval 毫秒执行一次)                      │
│                                                              │
│  1. 遍历所有 trackingContainers                              │
│  2. 对每个 Container:                                        │
│     a. 读取 /proc/<pid>/stat 构建进程树                       │
│     b. 计算进程树物理内存 (RSS) 和虚拟内存 (VSS)               │
│     c. 与 Container 内存上限比较                               │
│     d. 判断是否超限 → 决定是否杀除                              │
│  3. 汇总所有 Container 内存使用量                              │
│  4. 更新 NM 节点指标                                          │
│  5. sleep(monitoringInterval)                                │
│  6. 重复步骤 1-5                                              │
└─────────────────────────────────────────────────────────────┘
判杀规则:两轮确认机制

YARN 不会仅凭一次采样就杀容器,因为进程内存使用有波动。它引入了进程年龄概念来避免误杀:

  • 进程刚启动时年龄 = 1
  • 每次监控轮次,年龄 +1
  • 判杀标准:
条件 判杀标准
年龄 > 0 的进程 进程树总内存 > 上限值 × 2 才杀(容忍瞬间波动)
年龄 > 1 的进程 进程树总内存 > 上限值就杀(已稳定运行,不应波动)

也就是说:刚启动的进程有一轮缓冲期(可以用到2倍上限),第二轮开始就必须严格在上限以内。

内存上限的计算
  • 物理内存上限 = executor.memory + spark.yarn.executor.memoryOverhead
  • 虚拟内存上限 = 物理内存上限 × yarn.nodemanager.vmem-pmem-ratio(默认 2.1)

6.3 cgroups 严格控制的工作流程

当启用 cgroups 严格控制(Level 2)时:

  1. NM 为每个 Container 创建一个 cgroup 子组
  2. 在 cgroup 中设置 memory.limit_in_bytes = 容器内存上限
  3. 当容器进程尝试使用超过上限的内存时,Linux 内核的 OOM killer 直接杀掉该容器进程
  4. 容器退出码为 137(128 + 9,即 SIGKILL)
  5. 可以在 /var/log/messages 中验证 OOM 原因

优势 :即时生效,没有轮询延迟;劣势:容器不能 burst 使用更多内存。

6.4 cgroups 弹性控制的工作流程(你遇到的情况)

当启用弹性控制(Level 3)时,这正是你遇到的场景:

  1. NM 在所有 Container 的父 cgroup 上设置一个内存上限 = yarn.nodemanager.resource.memory-mb(节点总可用内存)
  2. 单个 Container 的 cgroup 不设限制,允许 burst
  3. 当节点整体内存使用量达到父 cgroup 上限时:
    • 内核冻结所有 Container 进程
    • 内核通过 cgroup 事件通知 NM
    • NM 的 OOMHandler 选择一个 Container 进行 preempt(杀除)
  4. 杀掉一个后检查 OOM 条件是否解除,若未解除则继续杀下一个
默认 OOMHandler 的选择逻辑

DefaultOOMHandler 的杀容器策略:

  1. 优先杀:最近一次超出自身内存限制的 Container(即 burst 了的那一个)
  2. 兜底杀 :如果没有 burst 的 Container,则杀最近启动的 Container(最小化损失)
  3. 持续杀除直到节点 OOM 条件解除

关键特性:如果你的 Container 没有 burst(内存使用在自身限制内),理论上不会被优先选中。但在兜底策略下,如果它是最近启动的,仍然可能被杀。

6.5 关键配置参数速查表

参数 默认值 作用 配置位置
yarn.nodemanager.pmem-check-enabled true 是否检查物理内存 yarn-site.xml
yarn.nodemanager.vmem-check-enabled true 是否检查虚拟内存 yarn-site.xml
yarn.nodemanager.vmem-pmem-ratio 2.1 虚拟内存与物理内存比例阈值 yarn-site.xml
yarn.nodemanager.resource.memory-mb 8192 NM 节点可为容器分配的总内存 yarn-site.xml
yarn.nodemanager.resource.memory.enforced false 是否启用 cgroups 严格内存执行 yarn-site.xml
yarn.nodemanager.elastic-memory-control.enabled false 是否启用弹性内存控制 yarn-site.xml
yarn.nodemanager.container-monitor.interval-ms 3000 轮询监控间隔(毫秒) yarn-site.xml

所有这些参数都是 YARN 集群级别配置,无法通过单个 Spark 任务修改。

6.6 你能控制什么

层面 你能控制的 你不能控制的
集群配置 --- NM 杀容器的阈值、节点内存上限、cgroups 设置
Spark 任务参数 executor.memorymemoryOverheadexecutor.cores NM 的 pmem-check-enabled、vmem-check-enabled 等
任务容错 maxFailuresblacklistdynamicAllocation NM 选择杀哪个容器的策略

7. 补充说明

  • 如果任务确实存在数据倾斜,某个 Executor 处理的数据量远超其他,应优先解决倾斜问题
  • 如果频繁出现节点级别内存紧张,建议联系运维排查集群资源分配是否合理
  • 以上参数调整后建议观察几个任务周期的运行情况,根据实际效果微调
相关推荐
TDengine (老段)5 小时前
TDengine 线程模型 — 网络、调度、执行
大数据·数据库·物联网·制造·时序数据库·tdengine·涛思数据
智购科技自动贩卖机7 小时前
自动售货机嵌入式状态机设计实战:从45个事件源到层次型状态机的工程重构
大数据·人工智能·stm32·物联网·重构·硬件架构
乐迪信息7 小时前
智慧港口船舶AI算法实现在线状态监测
大数据·人工智能·深度学习·算法·计算机视觉
Raas1008 小时前
MAI Gateway(魔芋企业级AI网关)对比分析:AI网关和API网关区别?企业级能力差距一览
大数据·人工智能·数据挖掘·mai gateway·企业级产品
Neighbor_OldY8 小时前
云上安全配置审计与误配置修复实战:从安全组、OSS、RAM到数据库的全栈排查复盘
大数据·运维·安全·云计算
工业甲酰苯胺9 小时前
AI低代码破局:制造业数智转型落地实战
大数据·人工智能·低代码·制造
大大大大晴天10 小时前
每天认识一个组件:流式存储Apache Fluss
大数据
熊野君10 小时前
第 4 章 技术产品经理核心能力模型
大数据·人工智能·产品经理
财复视界10 小时前
光智科技从“光学元件”到“稀散金属材料平台”的进化逻辑
大数据·人工智能·科技
牛企老板俱乐部11 小时前
2026年珠海精锐增材智造金属小批量交付可提供SGS与RoHS材质证明
大数据