OOM Killer发送 SIGKILL 导致进程无fatal日志“凭空消失”:通常是内存不足

来源:互联网 时间:2026-11-03

凌晨三点,监控报php-fpm进程数少了两个,5分钟后自己补了回来。PHP错误日志里没有fatal,nginx那边只留了几条502。页面在那几分钟里打不开,负载却不高。重启之后一切正常,看不出毛病,也就没人再追。

内存不够时内核会挑一个进程杀掉腾地方,这一步就是OOM killer。挑谁看oom_score,算法按进程的RSS、swap和页表占机器总内存的比例算分,再叠加oom_score_adj,跟跑了多久、是不是root无关。判断内存够不够要看/proc/meminfo里的MemAvailable,不是MemFree,页缓存算在可用里,MemFree常年只剩几百兆属于正常。MemAvailable掉到接近零,内核才开始动手,从触发到杀进程往往只有一两秒,swap开着的话会先换出一部分页,机器会明显变慢,拖到换不动了才开始杀。中间没有预警。

你先跑dmesg -T | grep -i killed,能看到被杀进程的pid、名字和当时的rss。日志被环形缓冲冲掉的,去/var/log/kern.log或者journalctl -k里翻,时间点跟进程消失的时刻能对上。按小时聚合的监控看不到这一下,进程数掉下去又弹回来,中间那段要靠日志补。

那一行里最有用的是anon-rss,它是进程实际占用的物理内存。total-vm是虚拟地址空间,通常大好几倍,拿它算账会把自己吓一跳。rss占机器内存八成以上的,基本就是它。

SIGKILL捕获不了,进程收到就没了,PHP来不及写fatal,也不会执行register_shutdown_function。日志干净不代表没出事,进程凭空消失反而更像被内核杀的。php-fpm的slowlog只记执行慢的请求,进程被杀不在里面。这条也常被当成程序bug。

容器有自己的内存上限,cgroup是内核的资源配额组,到顶触发的是cgroup OOM,dmesg里同样有记录。docker inspect看State里的OOMKilled,是true就实锤。k8s里Pod被杀、退出码137,也是同一个来源。容器的内存上限在docker run的-m参数或者compose的mem_limit里,k8s写在resources.limits.memory。上限设成比进程实际需要还小,容器里稍微跑点批量任务就触顶。这类被杀在容器里最容易被漏掉。

按单进程常驻内存乘进程数算。一个php-fpm跑着框架常驻60兆,25个子进程就是1.5G,再加MySQL的buffer pool、Redis和系统本身,4G的机器很容易见底。这两块是常驻的,不会自己缩。buffer pool按机器内存的四分之一到一半配,Redis要看maxmemory有没有设上限,没设的话它会一直涨到把机器吃满。

关键进程可以把/proc/进程号/oom_score_adj调到-1000,让它不被选中。数据库和sshd可以这么干,php-fpm不建议,杀了能重启,数据库恢复要几分钟。调完用cat /proc/进程号/oom_score_adj核对,-1000就是永不选中。这个值重启会丢,要写进systemd的unit或者启动脚本。

加swap能拖一阵,但换进来的页还是要读盘,响应时间会先烂掉,只能当缓冲不能当容量。调完连着看一周MemAvailable曲线,最低点离零还有一成以上才算稳。swap文件比swap分区灵活,云主机上一般用文件。

相关文章

A5创业网 版权所有

返回顶部