当前位置:首页 > 技术 > 正文内容

使用cgroups精细化管控PySpark进程内存资源

访客 技术 2026年10月14日 1
在生产环境中运行PySpark作业时,仅依赖Spark内置JVM内存参数(如 spark.driver.memory)往往无法实现对Python层内存使用的有效约束。尤其在Standalone模式下,PySpark会启动多层级进程:JVM侧的Driver/Executor与Python侧的Py4J网关、Worker子进程(pyspark.daemon 和 python worker),二者内存空间相互独立。若不对Python进程驻留内存(RSS)施加硬性限制,易导致系统OOM Killer介入或资源争抢,影响集群稳定性。 本文基于 PySpark 3.1.2 + Linux cgroups v1 环境,提出一种绕过JVM虚拟内存限制、直接管控物理内存占用的实践方案。

PySpark内存结构再梳理

PySpark任务实际由四类关键进程构成,需分别设限:
  • JVM Driver(spark-submit):承载SparkContext、DAG调度逻辑,受 spark.driver.memory 控制;
  • Python Driver(python主进程):执行用户Python脚本(如sc.parallelize()、df.collect()),其内存不受Spark配置影响;
  • JVM Executor(CoarseGrainedExecutorBackend):执行Shuffle、序列化等JVM任务,由 spark.executor.memory 限定;
  • Python Worker(python -m pyspark.worker):执行map、filter等UDF,其RSS内存增长不可控,是资源泄漏高发区。
其中,spark.executor.pyspark.memory 虽可触发 resource.setrlimit(RLIMIT_AS),但该机制限制的是虚拟地址空间(VIRT),而非实际物理内存(RSS)。实测表明:当设置为 2g 时,VIRT可能达2GB而RSS仅300MB,造成内存配额严重闲置。

cgroups:精准限制RSS内存的核心手段

Linux cgroups v1 的 memory 子系统提供 memory.limit_in_bytes 接口,可对进程组的RSS+Cache总和实施硬性上限,并在超限时触发OOM Killer终止违规进程——这正是我们需要的物理内存围栏。

分层控制策略

为解耦管理,建立三级cgroup目录结构:
/sys/fs/cgroup/memory/pyspark/
├── driver/      # 约束 Python Driver 进程(含 spark-submit JVM)
└── executor/    # 约束所有 Python Worker 子进程
操作步骤如下:
  1. 创建分组:
    sudo mkdir -p /sys/fs/cgroup/memory/pyspark/{driver,executor}
  2. 设定内存上限(以16GB物理内存机器为例):
    # Driver组:预留4GB(含JVM+Python Driver)
    sudo sh -c "echo $((4 * 1024 * 1024 * 1024)) > /sys/fs/cgroup/memory/pyspark/driver/memory.limit_in_bytes"
    
    # Executor组:预留10GB(覆盖全部Worker进程)
    sudo sh -c "echo $((10 * 1024 * 1024 * 1024)) > /sys/fs/cgroup/memory/pyspark/executor/memory.limit_in_bytes"
  3. 注入进程PID:
    • 启动PySpark前,先获取 spark-submit 主进程PID(如 160224),加入driver组:
    • echo 160224 | sudo tee /sys/fs/cgroup/memory/pyspark/driver/cgroup.procs
    • Executor Worker进程由Spark动态派生,需在作业启动后捕获其PID。可通过以下命令实时监控并注入:
    • # 监听新生成的 python worker 进程(过滤 pyspark.worker)
      sudo ps aux --forest | grep "pyspark\.worker" | grep -v grep | awk '{print $2}' | \
        xargs -r -I{} sh -c 'echo {} | sudo tee -a /sys/fs/cgroup/memory/pyspark/executor/cgroup.procs'

自动化注入脚本示例

为避免手动追踪PID,可编写轻量级守护脚本,在Executor启动后自动归集:
#!/usr/bin/env python3
import os
import time
import subprocess

EXECUTOR_CGROUP = "/sys/fs/cgroup/memory/pyspark/executor/cgroup.procs"
WORKER_PATTERN = "pyspark.worker"

def get_worker_pids():
    try:
        result = subprocess.run(
            ["pgrep", "-f", WORKER_PATTERN],
            capture_output=True, text=True, check=True
        )
        return [int(pid.strip()) for pid in result.stdout.splitlines() if pid.strip()]
    except subprocess.CalledProcessError:
        return []

def inject_to_cgroup(pids):
    with open(EXECUTOR_CGROUP, "a") as f:
        for pid in pids:
            try:
                f.write(f"{pid}\n")
                print(f"Injected PID {pid}")
            except OSError as e:
                if "No such process" not in str(e):
                    print(f"Failed to inject {pid}: {e}")

if __name__ == "__main__":
    seen = set()
    while True:
        current = set(get_worker_pids())
        new_pids = current - seen
        if new_pids:
            inject_to_cgroup(list(new_pids))
            seen.update(new_pids)
        time.sleep(2)
将此脚本作为后台服务运行,即可持续保障所有Worker进程落入cgroup管控。

验证与调试

检查当前组内存使用:
cat /sys/fs/cgroup/memory/pyspark/driver/memory.usage_in_bytes
cat /sys/fs/cgroup/memory/pyspark/executor/memory.usage_in_bytes
查看被OOM Killer终结的进程记录:
dmesg | grep -i "killed process"
若发现Worker频繁被kill,可调高 memory.limit_in_bytes 值,或检查Python UDF是否存在内存泄漏(如未释放大对象引用、全局缓存无界增长等)。

注意事项

  • cgroups v1 在容器化环境(Docker/K8s)中需额外启用 --memory 参数,且需确保内核支持 CONFIG_MEMCG;
  • 同一进程不能同时属于多个memory cgroup,迁移PID时需先从原组移除;
  • Spark Standalone的Executor重启后会产生新PID,必须重新注入cgroup,否则失去管控;
  • 该方案不替代JVM参数,应与 spark.driver.memory、spark.executor.memory 协同配置,形成JVM堆+Python RSS双维度防护。
标签: cgroupsPySpark
返回列表

上一篇:Web REST API 远程代码执行漏洞深度解析

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。