写点什么

OOM 杀进程 or 应用卡顿?该如何抉择

  • 2025-08-27
    北京
  • 本文字数:3015 字

    阅读完需:约 10 分钟

大小:1.48M时长:08:35
OOM 杀进程 or 应用卡顿?该如何抉择

背景


近期,大量用户反馈系统在运行过程中出现 CPU 利用率与系统负载(load)突发性飙升,甚至引发系统短时卡顿(持续数秒至数十秒)的问题;对于业务来说,轻则导致几百毫秒的抖动,重则连机器都无法 ssh 上去。经分析发现,此类异常现象普遍存在一个显著特征:均发生在系统内存占用率接近阈值(90%-95%)时。用户就发出了灵魂拷问:


“水位这么高了,为什么内核不触发 OOM 杀掉一些进程来释放内存?"

“我宁愿内核 OOM 把我业务进程杀了,我也不希望应用卡顿和系统夯机影响我其他业务!”


其实这个现象的核心原因就是:内核想确保应用实在没内存用了才 OOM。


思想总体是正确的,但是一条思想要满足所有场景也是非常难的。

为什么还不 OOM?


内存水位这么高,为什么还不 OOM!其实主要是由于系统进入了 Near-OOM 状态。现在,我们先回顾一下 Linux 的内存回收机制,如下图所示:

(图/Linux 内存水线)


Linux 划分了 high、low、min 三个内存水线。当系统剩余内存低于 low 水线后,内核会唤醒 kswapd 进程会被唤醒开始进行异步的内存回收,此时对系统没什么影响;系统剩余内存低于 min 水线后,内核会阻塞要分配的内存进程尝试尽可能地回收所有可回收的内存(主要是文件缓存以及一些内核结构体缓存),回收的过程中可能涉及到将文件缓存写入磁盘或遍历一些内核结构体,从而导致系统负载飙高、应用被阻塞。如果能成功回收内存并满足申请需求则不触发 OOM。


更糟糕的是,系统可能进入一种 Near-OOM 的活锁状态,即内核一边在尝试回收文件缓存;但是应用运行过程中从磁盘加载代码段等行为也在不断产生文件缓存,那么就会使整个系统负载持续飙高,甚至发生夯机。


所以,内核 OOM 的策略在业务延时敏感的场景,还是太保守了。


那么如果我们希望宁愿 OOM 把我业务进程杀了,我也不希望应用卡顿和系统夯机影响我其他业务?还有什么办法呢?

新 OOM 方案


为了应对 Near-OOM 现象,核心就是“快” OOM,在内核还在犹豫要不要 OOM 的时候,我们就替他做出决定!目前业界已有的方案主要是通过用户态提前杀死相关进程来提前释放内存,比如应用较为广泛的是 Facebook(Meta)推出的 oomd。oomd 目前已经集成于 systemd 中成为 systemd-oomd,且从 Ubuntu 22.04 开始集成于 Ubuntu 中。但是 oomd 方案存在以下问题:


  • 与 cgroupV2 以及 Linux 内核的 PSI(Pressure Stall Information)特性深度绑定。但 cgroupV1 目前仍然是云计算中主流 cgroup 版本,且由于 PSI 功能有一定的性能开销,在大部分云计算场景中都是默认关闭的。

  • 只支持以 cgroup 为粒度杀进程,配置 cgroup 级别的杀进程策略。


所以 oomd 在适用性和灵活性上仍有欠缺。


为了解决上述问题,阿里云操作系统控制台推出了 FastOOM 功能,支持节点以及 Pod 级别的用户态 OOM 配置,通过提前介入杀进程的方式避 Near-OOM 导致的抖动夯机。


FastOOM 同样采取用户态提前杀进程的形式来避免系统进入 Near-OOM 状态,主要分为采集预测模块和 kill 模块:采集预测模块会从阿里云自研操作系统中读取内存压力相关的指标,通过统计学方法对 OOM 发生的概率进行实时预测并判定当前节点或 pod 是否达到相应的内存压力或即将进入 Near-OOM 状态。kill 模块会根据用户配置的杀进程策略,选取对应的进程杀死。


最终所有由 FastOOM 执行的 kill 操作事件都会上报到控制台中心端进行展示,让用户方便了解底层的实际操作(不用担心 FastOOM 偷偷杀死了其他进程)。



cgroup版本支持

内存压力检测方法

支持粒度

杀进程粒度

FastOOM

cgroupV1

内存用量,iowait、sys等指标

节点、cgroup、pod

单进程或进程组

systemd-oomd

cgroupV2

主要基于内核PSI(pressure stall information),内存用量。

cgroup

杀整个cgroup

(表/FastOOM 与 oomd 对比)

使用 FastOOM 避免 Near-OOM 系统夯机抖动

场景一:配置节点级别策略解决系统 Near-OOM 抖动夯机问题


客户遇到的问题


某汽车行业发现某实例上业务长时间无响应、登录实例也十分卡顿。通过监控发现客户实例使用的内存在某个时间点开始徒增,接近系统的总内存(即 available 非常低),但没有超过系统总内存。



通过 top 命令可以看到系统的 CPU sys 利用率和 iowait 利用率和系统负载都持续飙高,kswapd0 线程占用非常高的 CPU 进行内存回收。



通过操作系统控制台的系统概览可以看到,在发生 OOM 夯(即处于 Near-OOM 状态)的同时,也发生了用户态收包延时,业务发生了抖动。



解决方案


通过配置开启节点级别的 FastOOM 功能,由于业务是实验较为敏感的业务,内存压力选择中,且设置业务程序(以 python 启动,进程名包含 python 子串)为避免被 OOM 进程且设置无关的日志程序优先杀死。



开启后,当节点内存水位处于 Near-OOM 状态时,用户态提前介入,根据配置杀死了如下进程,从而释放了部分内存避免系统进入了夯机状态。通过操作系统控制台的系统概览可以看到 FastOOM 介入的相关记录。


如下图所示,由于 kube-rbac-proxy 和 node_exporter 等进程 oom_score_adj 被设置为接近 999,FastOOM 会匹配内核策略优先杀死这些进程,但是由于杀死这些进程后释放内存较小,仍处于 Near-OOM;因此 FastOOM 杀死了配置优先杀死的 logcollect 进程。



由于用户态及时介入杀死进程释放出内存,使系统避免进入了 Near-OOM 的抖动状态。

案例二:配置 Pod 级别策略解决 Pod 应用抖动夯机问题


客户遇到的问题


在 Kubernetes 环境中,我们是可以为 Pod 中的容器配置对应的内存限制的,和节点 OOM 同理,如果 Pod 中的内存使用接近限制时,内核也会尝试回收 Pod 中所有可回收内存,才触发 OOM,这时候也会导致 Pod 内业务进程的延时阻塞。


某大数据客户会部署一些延时敏感的业务 Pod(即 Pod 中运行了多个业务进程)。业务时不时会存在响应长尾延时,但是网络相关指标一切正常。


后面我们接手问题后,通过 Alibaba Cloud Linux 自研指标(该指标反映容器由于内存回收阻塞的时长)发现,存在非常高的内存回收延时,且时间节点和抖动时间匹配:



推荐客户配置 Pod 级别的 FastOOM 后,通过提前杀死 Pod 中的相关内存占用进程,避免了内存回收延时的发生,抖动也不再出现。


解决方案


操作系统控制台提供较为灵活的 Pod 级别的 OOM 杀进程策略配置,可以灵活配置 Pod 中容器内发生 OOM 时,避免和优先杀死的进程。


假设在集群中通过名为 test-alinux 的 daemonset 在每一个节点部署了对应的 Pod。


在操作系统控制台中设置 Pod 级别 FastOOM 策略:


  • 为了匹配对应的 Pod,Pod 名称填写 test-alinux(正则表达式会匹配不同节点上的 test-alinux-xxx pod),命名空间为 default。

  • 由于只是希望控制 OOM 时的杀进程策略,将内存压力级别设置为高,则触发用户态 OOM 的时机会近似于内核 OOM 的时机。

  • 对于杀进程策略:配置优先杀死特定进程和避免杀死业务进程和 Pod 中的 1 号进程,从而避免 Pod 重启或影响业务,设置完成后下发至特定节点。


将配置下发到对应节点后,当 Pod 中容器内存使用超过容器 limit 后,发生 OOM;可以通过操作系统控制台系统概览看到 FastOOM 事件记录,可以看到 FastOOM 根据策略杀死了对应的进程,也避免了特定进程被杀死。


总结


人无完人,内核的 OOM 其实也不是万能的。为了能尽可能的回收内存,内核在发生 OOM 前会阻塞申请内存的进程,并尝试回收内存,这对于延时敏感的业务的影响是非常大的;如果内存持续保持在接近 OOM 的水位,还会进入 Near-OOM 的活锁状态导致整机夯机。阿里云操作系统控制台的 FastOOM 功能,通过相关指标,支持节点/容器/Pod 级策略,可精准杀指定进程,轻松弥补了内核 OOM 带来的延时卡顿问题。


联系我们 


您在使用操作系统控制台的过程中,有任何疑问和建议,可以扫描下方二维码或搜索群号:94405014449 加入钉钉群反馈,欢迎大家扫码加入交流。

操作系统控制台钉钉交流群


阿里云操作系统控制台 PC 端链接:

https://alinux.console.aliyun.com/

2025-08-27 15:246

评论

发布
暂无评论

面经手册 · 第9篇《队列是什么?什么是双端队列、延迟对列、阻塞队列,全是知识盲区!》

小傅哥

数据结构 小傅哥 队列 ArrayDeque

Java创建对象的方法有哪些?

奈学教育

Java

Java中强、软、弱、虚四种引用详解

奈学教育

Java

为什么阿里巴巴的程序员成长速度这么快,看完他们的内部资料我明白了

Java迁哥

Java 阿里巴巴 程序员 成长 笔记

LeetCode题解:84. 柱状图中最大的矩形,双循环暴力,JavaScript,详细注释

Lee Chen

大前端 LeetCode

ArCall远比你想象的要强大的多

anyRTC开发者

WebRTC 在线教育 直播 RTC 安卓

合约跟单系统开发,数字货币合约跟单软件搭建

13530558032

vivo商城前端架构升级-总览篇

vivo互联网技术

node.js Vue 大前端 架构设计

Docker 网络模式详解及容器间网络通信

哈喽沃德先生

Docker 容器 微服务

【API进阶之路】破圈,用一个API代替10人内容团队

华为云开发者联盟

内容 编辑 API 华为云 文本摘要

花两个半月吃透这份Java手打面经,成功从外包上岸到京东

Java迁哥

Java 学习 腾讯 面试 资料

鲲鹏迁移第一批吃螃蟹的人,践行技术国际化

华为云开发者联盟

鲲鹏920 服务器 华为云 ARM芯片 X86

数字货币钱包系统定制开发,区块链钱包源码

13530558032

2019年我最喜欢的三款数码产品。

徐说科技

手机 苹果

9块钱,构建个私有网盘,关键不限速

华为云开发者联盟

网站 OBS 在线网盘 华为云 云存储

拥抱K8S系列-01-CentOS7安装docker

张无忌

Docker centos 运维

实战案例丨使用云连接CC和数据复制服务DRS实现跨区域RDS迁移和数据同步

华为云开发者联盟

迁移 灾备 数据复制 云连接 数据同步

Java创建对象的方法有哪些?

古月木易

Java

Java中强、软、弱、虚四种引用详解

古月木易

Java

week12 homework

burner

区块链交易所开发源码,数字货币交易所app开发

13530558032

区块链支付系统源码开发,USDT承兑支付平台

13530558032

JDK8 Unsafe.java 源码

Darren

源码 并发 CAS 代码注释 unsafe

分析HiveQL 生成的MapReduce执行程序

任小龙

程序员如何获取一份高薪工作?阿里P8大牛给你一些中肯的建议

Java迁哥

Java 华为 程序员 面试 资料

JVM中unsafe.cpp源码

Darren

c++ 源码 JVM unsafe

一条龙!CI / CD 、打造小团队前端工程化服务

久违

Vue 大前端 jenkins React

【运维探讨】RPA落地实践,提升IT运维工作效能!

嘉为蓝鲸

RPA 运维自动化 标准化 系统运维 流程

高效程序员的45个习惯:敏捷开发修炼之道(7)

石云升

敏捷开发 晨会

架构设计复杂度来源

escray

学习 从零开始学架构 架构师预科班

35K成功上岸华为商城事业部,只因学透了这几个开源的商城项目

Java迁哥

Java 华为 源码 资料 商城项目

OOM 杀进程 or 应用卡顿?该如何抉择_操作系统_肖杰韬_InfoQ精选文章