如何处理Linux实例中的OOM问题?
背景信息
OOM(Out of Memory)指系统内存已用完,在Linux系统中,如果内存用完会导致系统无法正常工作,触发系统panic或者OOM Killer。OOM Killer是Linux内核的一个机制,该机制会监控那些占用内存过大的进程,尤其是短时间内消耗大量内存的进程,在系统的内存即将不够用时结束这些进程从而保障系统的整体可用性。
问题现象
ECS实例在运行过程中日志中有类似于如下所示信息:
[ 327.199955] 5202 total pagecache pages [ 327.200965] 0 pages in swap cache [ 327.201950] Swap cache stats: add 0, delete 0, find 0/0 [ 327.203656] Free swap = 0kB [ 327.204580] Total swap = 0kB [ 327.205491] 2096926 pages RAM [ 327.206375] 0 pages HighMem/MovableOnly [ 327.207509] 94837 pages reserved [ 327.208746] [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name [ 327.210727] [ 352] 0 352 9764 616 22 0 0 systemd-journal [ 327.212897] [ 372] 0 372 11370 150 23 0 -1000 systemd-udevd [ 327.214662] [ 410] 0 410 13883 102 27 0 -1000 auditd [ 327.216276] [ 498] 81 498 14558 164 33 0 -900 dbus-daemon [ 327.217880] [ 499] 0 499 5385 62 16 0 0 irqbalance [ 327.219369] [ 500] 999 500 153060 1350 63 0 0 polkitd [ 327.221158] [ 502] 0 502 243884 3537 36 0 0 uniagent [ 327.222916] [ 503] 0 503 137033 1033 84 0 0 NetworkManager [ 327.225002] [ 505] 0 505 6596 77 19 0 0 systemd-logind [ 327.226841] [ 508] 998 508 29452 105 30 0 0 chronyd [ 327.228427] [ 529] 0 529 26123 488 21 0 0 uniagentd [ 327.230120] [ 532] 0 532 143572 2883 96 0 0 tuned [ 327.231779] [ 563] 0 563 25753 514 51 0 0 dhclient [ 327.233396] [ 608] 0 608 54632 688 42 0 0 rsyslogd [ 327.235004] [ 839] 0 839 39781 400 42 0 0 hostwatch [ 327.236505] [ 904] 0 904 335577 1654 112 0 0 [kworker/0:0H] [ 327.237967] [ 1059] 0 1059 22452 253 45 0 0 master [ 327.239371] [ 1074] 89 1074 22478 250 44 0 0 pickup [ 327.240758] [ 1075] 89 1075 22495 252 45 0 0 qmgr [ 327.242087] [ 1416] 0 1416 28251 259 57 0 -1000 sshd [ 327.243474] [ 1418] 0 1418 6477 51 18 0 0 atd [ 327.244773] [ 1420] 0 1420 31597 152 18 0 0 crond [ 327.246133] [ 1422] 0 1422 24842 166 51 0 0 login [ 327.247893] [ 1423] 0 1423 27552 32 10 0 0 agetty [ 327.249319] [ 2359] 0 2359 197550 4903 48 0 0 containerserver [ 327.250828] [ 7726] 0 7726 63736 535 22 0 0 uniagentd [ 327.252278] [ 7727] 0 7727 98075 305 26 0 0 uniagentd [ 327.253688] [ 7773] 0 7773 28886 101 13 0 0 bash [ 327.254906] [ 7840] 0 7840 225333 3183 34 0 0 telescope [ 327.256240] [ 7922] 0 7922 39390 374 83 0 0 sshd [ 327.257496] [ 7924] 0 7924 28887 102 13 0 0 bash [ 327.258769] [ 7943] 0 7943 18088 190 39 0 0 sftp-server [ 327.260041] [ 8076] 0 8076 1958387 1904796 3739 0 0 python3 [ 327.261301] Out of memory: Kill process 8076 (python3) score 924 or sacrifice child [ 327.262504] Killed process 8076 (python3), UID 0, total-vm:7833548kB, anon-rss:7619112kB, file-rss:72kB, shmem-rss:0kB
可能原因
如果您在Linux实例上运行程序时,频繁发生OOM,可能是以下原因。
- Linux实例的配置过低,无法满足程序运行所需的内存资源,引发OOM。
- 应用程序代码对内存的使用不当,引发OOM。
处理方法
关于OOM的问题,请参考以下步骤进行处理:
- 执行以下命令,查看OOM相关日志。
sudo less /var/log/messages # CentOS sudo less /var/log/syslog # Ubuntu
在日志中通过oom或者kill作为关键字进行搜索,以查看OOM问题的相关日志信息。
日志中会存在OOM Killer评估进程时的标准输出标题行如下图,其中可通过对rss列(单位4KB)进行排序,查看内存消耗最大的进程。
如果是业务进程占用的内存高导致的内存OOM,建议您可以进一步通过业务应用日志定位内存占用高的原因。
- 根据您排查的结果,可以选择对应的处理方案。
问题原因
处理建议
Linux实例的配置过低,无法满足程序运行所需的内存资源。
升级实例内存配置:变更ECS规格。
注意:更改实例规格的过程中,您需要停止并重新启动ECS实例,建议您在非业务高峰期时执行该操作,减少对您业务造成的影响。
应用程序代码对内存的使用不当。
优化应用程序代码,如对批量查询请求进行分页等。
参考信息
- OOM Killer评估进程时的标准输出标题行,各列具体详解如表1 OOM Killer评估进程时的标准输出标题行所示。
表1 OOM Killer评估进程时的标准输出标题行 列名
全称
含义解释
pid
Process ID
进程的操作系统内部ID。
uid
User ID
启动该进程的用户ID。0代表root用户。
tgid
Thread Group ID
线程组ID。
- 对于单线程进程,这与pid相同。
- 对于多线程程序,所有线程共享同一个tgid。
total_vm
Total Virtual Memory
进程占用的总虚拟内存大小(以4KB的页为单位)。这个值很大,因为它包括共享库、映射的文件等,不是衡量内存压力的最佳指标。
rss
Resident Set Size
驻留集大小(以4KB的页为单位)。这是进程当前在物理内存中占用的、非交换的内存总量。这是 OOM Killer 最重要的考量因素之一,它直接反映了进程对物理内存的消耗。
nr_ptes
Number of Page Table Entries
页表项数目。进程使用的虚拟内存越多,需要的页表项就越多。这项本身也消耗内存,所以也是 OOM 评分的一个因素。
swapents
Swap Entries
被换出到交换分区(Swap)的内存大小(以4KB的页为单位)。值越高,说明该进程的一些内存当前不在物理RAM中。
oom_score_adj
OOM Score Adjust
OOM调整值(范围从-1000到1000)。这是用户态可以设置的最重要的参数,用于主动影响 OOM Killer的决策。
name
Process Name
进程的可执行文件名称。
- OOM相关参数如表2 OOM相关参数表所示。
表2 OOM相关参数表 参数名称
参数说明
取值
修改方式
panic_on_oom
panic_on_oom参数是控制系统遇到OOM时如何反应的。当系统遇到OOM的时候,通常会有两种选择:
- 触发系统panic,可能会出现频繁宕机的情况。
- 选择一个或者几个进程,触发OOM Killer,结束选中的进程,释放内存,让系统保持整体可用。
可以通过以下命令查看参数取值:
cat /proc/sys/vm/panic_on_oom或者
sysctl -a | grep panic_on_oom
- 值为0:内存不足时,触发OOM Killer。
- 值为1:内存不足时,根据具体情况可能发生kernel panic,也可能触发OOM Killer。
- 值为2:内存不足时,强制触发系统panic,导致系统重启。
例如:将参数设置为0,可用以下两种方式:
oom_kill_allocating_task
当系统选择触发OOM Killer,试图结束某些进程时,oom_kill_allocating_task参数会控制选择哪些进程,有以下两种选择:
- 触发OOM的进程。
- oom_score得分最高的进程。
可以通过以下命令查看参数取值:
cat /proc/sys/vm/oom_kill_allocating_task或者sysctl -a | grep oom_kill_allocating_task
- 值为0:选择oom_score得分最高的进程。
- 值为非0:选择触发OOM的进程。
例如:将该参数设置成1,可用以下两种方式:
oom_score
指进程的得分,主要有两部分组成:
- 系统打分,主要是根据该进程的内存使用情况由系统自动计算。
- 用户打分,也就是oom_score_adj,可以自定义。
可以通过调整oom_score_adj的值进而调整一个进程最终的得分。通过以下命令查看参数取值:
cat /proc/进程id/oom_score_adj
- 值为0:不调整oom_score。
- 值为负值:在实际打分值上减去一个折扣。
- 值为正值:增加该进程的oom_score。
例如:将进程id为2939的进程oom_score_adj参数值设置为1000,可用以下命令:
echo 1000 > /proc/2939/oom_score_adj
oom_dump_tasks
oom_dump_tasks参数控制OOM发生时是否记录系统的进程信息和OOM Killer信息。
例如dump系统中所有的用户空间进程关于内存方面的一些信息,包括:进程标识信息、该进程使用的内存信息、该进程的页表信息等,这些信息有助于了解出现OOM的原因。
可以通过以下命令查看参数取值:
cat /proc/sys/vm/oom_dump_tasks或者sysctl -a | grep oom_dump_tasks
- 值为0:OOM发生时不会打印相关信息。
- 值为非0:以下三种情况会调用dump_tasks打印系统中所有task的内存状况。
- 由于OOM导致kernel panic。
- 没有找到需要结束的进程。
- 找到进程并将其结束的时候。
例如:将该参数设置成0,可用以下两种方式: