如何优雅地使用Linux系统OOM ( Out Of Memory Killer)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优雅地使用Linux系统OOM ( Out Of Memory Killer)相关的知识,希望对你有一定的参考价值。

参考技术A Linux内核根据服务器上当前运行应用的需要来分配内存。因为这通常是预先发生的,所以应用并不会使用所有分配的内存。这将会导致资源浪费,Linux内核允许超分内存以提高内存使用效率。Linux内核允许超分内存,比如总共8G内存,可以分给10个进程各1G,这通常没问题。但问题发生在太多应用一起占用内存,有8个进程各占了1G,剩下两个进程要喝西北风了。

由于内存不足,服务器有崩溃的风险。The  server runs the risk of crashing because it  ran out of memory。为了防止服务器到达这个临近状态,内核中有一个OOM Killer杀手进程。To prevent the server from reaching  that critical state, the kernel also contains a process known as the OOM Killer。内核利用这个杀手进程开始屠杀那些非必要进程,以便服务器正常运行。The kernel uses this  process to start killing non-essential  processes so the server can remain  operational.

当你认为这一切都不是问题时,因为OOM Killer只杀掉那些非必要的,不是用户需要的进程。举例,两个应用(Apache和mysql)通常先被杀掉,因为占用大量的内存。但这将导致一个web网站立马瘫痪了。

当尝试找到为什么一个应用程序或进程被OOM killer杀掉时,有很多地方可以找到一个进程如何被杀掉以及被杀掉的原因。

$ grep -i kill /var/log/messages*

host kernel: Out of Memory: Killed process 5123 (exampleprocess)

The capital K in Killed tells you that the process was killed with a -9 signal, and this typically is a good indicator that the OOM Killer is to blame.

$ free -lh

The -l switch shows high and low memory  statistics, and the -h switch puts the output  into gigabytes for easier human readability. You can change this to the -m switch if you  prefer the output in megabytes.

同时该命令会给出Swap内存使用信息。注意:free命令给出某个时刻得数据,需要多执行几次才能知道内存动态的占用情况。

$ vmstat -SM 10 20

20次,每次间隔10秒给出内存使用情况。

top 默认输出CPU的使用情况,不过你可以在top后再按下shift + M,你将得到内存的使用情况。

配置文件/etc/sysctl.conf:

sysctl vm.panic_on_oom=1

sysctl kernel.panic=X

echo “vm.panic_on_oom=1” >> /etc/sysctl.conf

echo “kernel.panic=X” >> /etc/sysctl.conf

大多数情况下,内存不足时每次都重启是不合适的。

既可以保护一些重要进程不被OMM killer杀掉,又可以让不重要的进程更容易杀掉:

echo -15 > /proc/(PID)/oom_adj (不被杀)

echo 10 > /proc/(PID)/oom_adj (更易杀)

pstree -p | grep "process" | head -1

在某些情况下,豁免进程可能导致意外的行为变化,取决于系统和资源配置。假如内核无法杀死一个占用大量内存的进程,将杀死其他进程,包括那些重要的操作系统进程。

由于OOM killer可调节的有效范围在-16到+15之间,设置为-17将豁免一个进程,因为在OOM killer调节范围之外。通常的规则是这个参数越大越容易被杀死豁免一个进程的命令是

echo -17 > /proc/(PID)/oom_adj

警告:不建议用于生产环境。

假如重启,修改进程优先级,豁免一个进程不足够好,有个风险的选项:将oom killer 功能关闭。

这一选项参数将有如下影响:

4.1) 严重的内核恐慌kernel panic

4.2) 系统挂住system hang-up

4.3) 一个完整的系统崩溃system crash

为什么关闭有风险呢呢?该功能避免自己因资源而跑飞了。如果你关闭此功能,将不能避免内存耗尽。考虑此项时请极度慎重。

sysctl vm.overcommit_memory=2

echo “vm.overcommit_memory=2” >> /etc/sysctl.conf

out of memory:OOM

 

这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃。如果检查相关的日志文件(/var/log/messages)就会看到下面类似的 Out of memory: Kill process 信息

 

技术图片

 

 

Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。

 

内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。

解决这个问题最简单的办法就是增加内存,优化服务(如Mysql),还可以优化系统,让系统尽可能使用少的内存以便应用程序(如 MySQL) 能使用更多的内存,还有一个临时的办法就是调整内核参数,让 MySQL 进程不容易被 OOM killer 发现。

 

配置 OOM killer

我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统。

# sysctl -w vm.panic_on_oom=1
vm.panic_on_oom = 1

# sysctl -w kernel.panic=10
kernel.panic = 10

# echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
# echo "kernel.panic=10" >> /etc/sysctl.conf

 

 

从上面的 oom_kill.c 代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。比如,如果不想 MySQL 进程被轻易杀掉的话可以找到 MySQL 运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):

# ps aux | grep mysqld
mysql    2196  1.6  2.1 623800 44876 ?        Ssl  09:42   0:00 /usr/sbin/mysqld

# cat /proc/2196/oom_score_adj
0
# echo -15 > /proc/2196/oom_score_adj

 

当然,如果需要的话可以完全关闭 OOM killer(不推荐用在生产环境):

# sysctl -w vm.overcommit_memory=2

# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf

 

找出最有可能被 OOM Killer 杀掉的进程

我们知道了在用户空间可以通过操作每个进程的 oom_adj 内核参数来调整进程的分数,这个分数也可以通过 oom_score 这个内核参数看到,比如查看进程号为981的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3:

# cat /proc/981/oom_score
18

# echo -15 > /proc/981/oom_score_adj
# cat /proc/981/oom_score
3

 

下面这个 bash 脚本可用来打印当前系统上 oom_score 分数最高(最容易被 OOM Killer 杀掉)的进程:

# vi oomscore.sh
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex /proc/[0-9]+); do
    printf "%2d %5d %s
"         "$(cat $proc/oom_score)"         "$(basename $proc)"         "$(cat $proc/cmdline | tr ‘‘ ‘ ‘ | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10

 

执行结果:

# chmod +x oomscore.sh
# ./oomscore.sh
18   981 /usr/sbin/mysqld
 4 31359 -bash
 4 31056 -bash
 1 31358 sshd: root@pts/6
 1 31244 sshd: vpsee [priv]
 1 31159 -bash
 1 31158 sudo -i
 1 31055 sshd: root@pts/3
 1 30912 sshd: vpsee [priv]
 1 29547 /usr/sbin/sshd -D

 

2、解决方法
a:  释放内存(临时操作)
echo 3 > /proc/sys/vm/drop_caches

 

说明:配置文件/proc/sys/vm/drop_caches。这个文件中记录了缓存释放的参数,默认值为0,也就是不释放缓存。他的值可以为0~3之间的任意数字,代表着不同的含义:
 0 – 不释放
 1 – 释放页缓存
 2 – 释放dentries和inodes
 3 – 释放所有缓存

 

b: 关闭 OOM killer (临时关闭),(默认为1,即激活) echo 0 > /proc/sys/vm/oom_dump_tasks
永久生效 修改/etc/sysctl.conf,增加: vm.oom-kill = 0 重启或执行sysctl -p即可生效。

 

 

参考:https://blog.csdn.net/keketrtr/article/details/70174286

https://www.cnblogs.com/aquester/archive/2012/07/24/9891794.html

https://blog.csdn.net/w892824196/article/details/100878736

 https://blog.csdn.net/ukgikgb/article/details/80107350

以上是关于如何优雅地使用Linux系统OOM ( Out Of Memory Killer)的主要内容,如果未能解决你的问题,请参考以下文章

如何优雅地使用win10的Linux子系统

out of memory:OOM

Linux系统下如何优雅地关闭Java进程?

Linux OOM Killer

假如淘宝JVM炸了(OOM),怎么优雅的逛双十一?

Linux OOM-killer机制(out of memory)