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 处理的数据量远超其他,应优先解决倾斜问题
  • 如果频繁出现节点级别内存紧张,建议联系运维排查集群资源分配是否合理
  • 以上参数调整后建议观察几个任务周期的运行情况,根据实际效果微调
相关推荐
小赖同学啊1 小时前
可信数据空间设计
大数据
青春喂了后端2 小时前
Go Sidecar Status 性能优化
开发语言·性能优化·golang
就改了2 小时前
Windows Elasticsearch 完整上手教程
大数据·windows·elasticsearch
yyuuuzz2 小时前
独立站运营的几个技术层面常见问题
大数据·运维·服务器·网络·数据库·aws
XIAOYU6720133 小时前
高中物理成绩优异,适合报考大数据哪个细分专业数学成绩偏弱,还适合填报大数据相关专业吗
大数据
2601_954971133 小时前
大数据需要掌握哪些主流大数据工具框架
大数据
不喝水就会渴3 小时前
HarmonyOS惰性加载性能优化技术详解(喵屿项目案例)
华为·性能优化·harmonyos
Urbano3 小时前
工装标准缝纫流程及自动化升级提质增产方案
大数据·人工智能·算法
wanghowie3 小时前
35. 从AI客服到AI运营助手:Workflow、Single Agent、Multi-Agent、Agent Native 的架构选型实践
大数据·人工智能·架构