OOM行为
关于OOM行为的思考 以及Kswapd的动作和行为。
http://evertrain.blogspot.com/2018/04/oom.html
更详细的打分算法见源码 https://github.com/torvalds/linux/blob/master/mm/oom_kill.c
发生之后
OOM killer会将kill的信息记录到系统日志/var/log/messages,检索相关信息就能匹配到是否触发。
grep 'Out of memory' /var/log/messages
也可以通过dmesg
dmesg -Tx | egrep -i 'killed process'
查看分数最高的进程
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
printf "%2d %5d %s\n" \
"$(cat $proc/oom_score)" \
"$(basename $proc)" \
"$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10
保护措施
设置OverCommit
只有在OverCommit的时候才会触发OOM, 默认是许可一定程度的OverCommit的。
https://docs.kernel.org/vm/overcommit-accounting.html
vm.overcommit_memory 作用是控制OverCommit是否被许可。
0
Heuristic overcommit handling. Obvious overcommits of address space are refused. Used for a typical system. It ensures a seriously wild allocation fails while allowing overcommit to reduce swap usage. root is allowed to allocate slightly more memory in this mode. This is the default.
1
Always overcommit. Appropriate for some scientific applications. Classic example is code using sparse arrays and just relying on the virtual memory consisting almost entirely of zero pages.
2
Don’t overcommit. The total address space commit for the system is not permitted to exceed swap + a configurable amount (default is 50%) of physical RAM. Depending on the amount you use, in most situations this means a process will not be killed while accessing pages but will receive errors on memory allocation as appropriate.
Useful for applications that want to guarantee their memory allocations will be available in the future without having to initialize every page.
[root@ip-172-31-9-192 log]# cat /proc/meminfo | grep Comm
CommitLimit: 4794236 kB
Committed_AS: 2344744 kB
---
CommitLimit: 可提交内存的上限, 超过这个上限系统认为目前内存已经是OverCommit。
Committed_AS: 已经提交内存的上限,当前所有进程已经提交的内存使用,这个不是已经分配出去的, 是进程申请的。
设置Killer的行为
root@ip-172-31-11-235:/home/ec2-user|⇒ cat /proc/sys/vm/oom_kill_allocating_task
# 值为0:会 kill 掉得分最高的进程
# 值为非0:会kill 掉当前申请内存而触发OOM的进程
设置进程
- 对于需要保护的进程可以使用OOM_ADJ=-17 将这个进程从OOM Killer的列表中移除(已经在内核的2.6之后废弃,处于兼容性保留了这个文件接口
- 调整OOM_SCORE_ADJ, 范围是 -1000 < oom_score_adj < 1000
直接调整到-1000,会出现在计算分数列表的最后
echo -1000 > /proc/31595/oom_score_adj
手动触发一次OOM规则, Kill符合要求的进程
echo f > /proc/sysrq-trigger
调整服务的OOM Score
对于服务本身的保护方式, 可以采用使用Systemd Unit file里面进行 OOMADJSCORE=*** 的方式来指定,例如保护MySQL的进程不会在OOM Killer的列表中。
cat /usr/lib/systemd/system/mariadb.service
[Service]
Type=simple
User=mysql
Group=mysql
ExecStartPre=/usr/libexec/mariadb-prepare-db-dir %n
ExecStart=/usr/bin/mysqld_safe --basedir=/usr
ExecStartPost=/usr/libexec/mariadb-wait-ready $MAINPID
# Setting Here. and setting in the /proc/$PID/oom_score_adj.
OOMScoreAdjust=-1000
sudo systemctl daemon-reload && sudo systemctl restart mariadb
避免OOM的方式
- 关闭OverCommit: 关闭OverCommit会导致进程如果无法拿到内存就fast fail , 不会出现OOMKiller干掉无辜进程的情况。
- 开启OverCommit, 开启一定程度的Swap: 开启Swap会导致内存在接近99%的时候,会出现系统响应变慢的问题,但是会由于申请的内存没有超过TotalMEM + TotalSWAP,因此不会触发OOM ,但是会导致明显的系统响应问题。 如果超过了 TotalMEM + TotalSWAP 会立刻触发一次OOMkiller结束进程。
- 开启OverCommit, 调整进程的优先级: 对于特定的进程进行保护。OOM会按照设置的积分计算需要Kill的进程。
- 开启OverCommit, 调整OOMkiller的行为方式: 计算积分后Kill 或者 直接Kill当前新申请内存的进程。这个方式感觉和默认的关闭OverCommit的行为类型,都是拒绝新的进程以保证旧的进程可以存活。
更可靠的方式
Faceboook的oomd
https://github.com/facebookincubator/oomd
oomd使用的是 PSI接口来评估内存的压力,可以通过自定义规则的方式来对来进行内存压力的分析,而不是简单的内存用量。
Fedora的Early OOM
https://github.com/rfjakob/earlyoom
EarlyOOM的作用是提前OOM,这样可以保障用户空间的图形桌面不会到交换空间去, 主要解决的问题是内存压力过大的交换动作会将桌面环境换出导致响应变慢。
输出结果记录
Cgroup - Pod
首先是哪个程序超过了cgroup的limit, 触发了cgroup 的oom, 这里是 xray 这个进程自己。
kern :warn : [Thu Aug 17 17:29:27 2023] xray invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=-997
这个部分是触发kprint的信息, 解释如下:
在哪个CPU(1)上, 运行的进程PID(1586622), commandline的名称(xray), 哪个版本的内核, 是否被标记为 taint.kern :warn : [Thu Aug 17 17:29:27 2023] CPU: 1 PID: 1586622 Comm: xray Kdump: loaded Not tainted 5.14.0-333.el9.x86_64 #1
硬件信息, BIOS信息
kern :warn : [Thu Aug 17 17:29:27 2023] Hardware name: Red Hat KVM, BIOS 1.15.0-2.module_el8.6.0+2880+7d9e3703 04/01/2014
打印发生oom时刻的堆栈
kern :warn : [Thu Aug 17 17:29:27 2023] Call Trace: kern :warn : [Thu Aug 17 17:29:27 2023] <TASK> kern :warn : [Thu Aug 17 17:29:27 2023] dump_stack_lvl+0x34/0x48 kern :warn : [Thu Aug 17 17:29:27 2023] dump_header+0x4a/0x201 kern :warn : [Thu Aug 17 17:29:27 2023] oom_kill_process.cold+0xb/0x10 kern :warn : [Thu Aug 17 17:29:27 2023] out_of_memory+0xed/0x2e0 kern :warn : [Thu Aug 17 17:29:27 2023] mem_cgroup_out_of_memory+0x13a/0x150 kern :warn : [Thu Aug 17 17:29:27 2023] try_charge_memcg+0x79d/0x860 kern :warn : [Thu Aug 17 17:29:27 2023] ? __mem_cgroup_charge+0x55/0x80 kern :warn : [Thu Aug 17 17:29:27 2023] charge_memcg+0x7a/0xf0 kern :warn : [Thu Aug 17 17:29:27 2023] __mem_cgroup_charge+0x29/0x80 kern :warn : [Thu Aug 17 17:29:27 2023] __filemap_add_folio+0x224/0x590 kern :warn : [Thu Aug 17 17:29:27 2023] ? scan_shadow_nodes+0x30/0x30 kern :warn : [Thu Aug 17 17:29:27 2023] filemap_add_folio+0x37/0xa0 kern :warn : [Thu Aug 17 17:29:27 2023] __filemap_get_folio+0x1fd/0x330 kern :warn : [Thu Aug 17 17:29:27 2023] filemap_fault+0x40b/0x740 kern :warn : [Thu Aug 17 17:29:27 2023] ? next_uptodate_page+0x160/0x1f0 kern :warn : [Thu Aug 17 17:29:27 2023] ? _raw_spin_unlock+0xa/0x30 kern :warn : [Thu Aug 17 17:29:27 2023] ? filemap_map_pages+0x298/0x540 kern :warn : [Thu Aug 17 17:29:27 2023] __do_fault+0x36/0x140 kern :warn : [Thu Aug 17 17:29:27 2023] do_read_fault+0xf0/0x160 kern :warn : [Thu Aug 17 17:29:27 2023] do_fault+0xa9/0x390 kern :warn : [Thu Aug 17 17:29:27 2023] __handle_mm_fault+0x585/0x650 kern :warn : [Thu Aug 17 17:29:27 2023] handle_mm_fault+0xc5/0x2a0 kern :warn : [Thu Aug 17 17:29:27 2023] do_user_addr_fault+0x1b4/0x6a0 kern :warn : [Thu Aug 17 17:29:27 2023] ? syscall_exit_to_user_mode+0x12/0x30 kern :warn : [Thu Aug 17 17:29:27 2023] exc_page_fault+0x62/0x150 kern :warn : [Thu Aug 17 17:29:27 2023] asm_exc_page_fault+0x22/0x30
寄存器的地址和相关信息。
kern :warn : [Thu Aug 17 17:29:27 2023] RIP: 0033:0x46ea1d kern :warn : [Thu Aug 17 17:29:27 2023] Code: Unable to access opcode bytes at RIP 0x46e9f3. kern :warn : [Thu Aug 17 17:29:27 2023] RSP: 002b:000000c000073f10 EFLAGS: 00010202 kern :warn : [Thu Aug 17 17:29:27 2023] RAX: 0000000000000000 RBX: 0000000000002710 RCX: 000000000046ea1d kern :warn : [Thu Aug 17 17:29:27 2023] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 000000c000073f10 kern :warn : [Thu Aug 17 17:29:27 2023] RBP: 000000c000073f20 R08: 000000000001af6c R09: 0000000000000072 kern :warn : [Thu Aug 17 17:29:27 2023] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000011 kern :warn : [Thu Aug 17 17:29:27 2023] R13: 000000c000aa6c00 R14: 000000c0000064e0 R15: 0000000000001000
oom时刻的内存信息 和 /sys/fs/cgroup/memory.stat, 当前发生oom下面的 Cgroup 的信息
kern :warn : [Thu Aug 17 17:29:27 2023] </TASK> kern :info : [Thu Aug 17 17:29:27 2023] memory: usage 208204kB, limit 204800kB, failcnt 7470 kern :info : [Thu Aug 17 17:29:27 2023] swap: usage 0kB, limit 0kB, failcnt 0 kern :info : [Thu Aug 17 17:29:27 2023] Memory cgroup stats for /kubepods.slice/kubepods-pod089f25ee_a41a_414c_a028_71a38dfb30d3.slice/cri-containerd-c3f1de0d0969e5d3c6758d2e59be98d4dc8ba0367af77f52b09d15225139c74f.scope: kern :info : [Thu Aug 17 17:29:27 2023] anon 28139520 file 8192 kernel 1343488 kernel_stack 212992 pagetables 167936 sec_pagetables 0 percpu 0 sock 183623680 vmalloc 0 shmem 0 zswap 0 zswapped 0 file_mapped 0 file_dirty 0 file_writeback 0 swapcached 0 anon_thp 8388608 file_thp 0 shmem_thp 0 inactive_anon 28135424 active_anon 4096 inactive_file 4096 active_file 0 unevictable 0 slab_reclaimable 367104 slab_unreclaimable 562944 slab 930048 workingset_refault_anon 0 workingset_refault_file 5784 workingset_activate_anon 0 workingset_activate_file 2856 workingset_restore_anon 0 workingset_restore_file 2331 workingset_nodereclaim 0 pgscan 8263 pgsteal 5880 pgscan_kswapd 1945 pgscan_direct 6318 pgsteal_kswapd 1496 pgsteal_direct 4384 pgfault 23739 pgmajfault 416 pgrefill 4326 pgactivate 379 pgdeactivate 3235 pglazyfree 0 pglazyfreed 0 zswpin 0 zswpout 0 thp_fault_alloc 14 thp_collapse_alloc 4
任务信息, 被kill的进程的信息, 内存的使用量以及积分模式。
kern :info : [Thu Aug 17 17:29:27 2023] Tasks state (memory values in pages): kern :info : [Thu Aug 17 17:29:27 2023] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name kern :info : [Thu Aug 17 17:29:27 2023] [1586608] 0 1586608 184282 6593 180224 0 -997 xray kern :info : [Thu Aug 17 17:29:27 2023] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=cri-containerd-c3f1de0d0969e5d3c6758d2e59be98d4dc8ba0367af77f52b09d15225139c74f.scope,mems_allowed=0,oom_memcg=/kubepods.slice/kubepods-pod089f25ee_a41a_414c_a028_71a38dfb30d3.slice/cri-containerd-c3f1de0d0969e5d3c6758d2e59be98d4dc8ba0367af77f52b09d15225139c74f.scope,task_memcg=/kubepods.slice/kubepods-pod089f25ee_a41a_414c_a028_71a38dfb30d3.slice/cri-containerd-c3f1de0d0969e5d3c6758d2e59be98d4dc8ba0367af77f52b09d15225139c74f.scope,task=xray,pid=1586608,uid=0 kern :err : [Thu Aug 17 17:29:27 2023] Memory cgroup out of memory: Killed process 1586608 (xray) total-vm:737128kB, anon-rss:26372kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:176kB oom_score_adj:-997