一,主题引入
项目组最近在生产环境部署的一个3节点Nacos集群出现单节点宕机的现象,现象看起来就像是好好的进程突然"人间蒸发”,突然得没有留下任何“遗言”,查看日志没有任何错误信息。通过监控看到进程占用的资源并不多,进程cpu和内存等核心指标都在合理使用范围内,不过节点所在虚拟机内存较高,最开始考虑是java进程OOM了,查看日志没有发现任何OutOfMemory堆栈,检查jvm启动参数也已经配置HeapDumpOnOutOfMemoryError,在指定路径下并没有看到Dump文件,这样可以完全排除JVM OutOfMemory的可能。第二个考虑是运维人员或者其他进程主动杀掉进程,搜索了history,并将系统上现存的进程和定时任务进行摸排,后来也排除了这种可能性。最后,考虑是系统内核杀掉,”雁过留痕”,系统的行为必然在某处会留下痕迹,通过查询系统日志,果然找到了真凶。
系统杀掉了进程号位2406的进程,在系统日志中打印了该进程被杀死的原因以及当时所占用的内存资源信息,还有一个相对陌生的oom_score_adj值,暂时先不管它,后文将重点展开描述这个变量。上述日志即为linux系统OOM机制实际运行一次所体现的结果。
二,了解OOM机制
2.1,问题产生背景
服务器上的进程正在消耗大量内存,并且系统需要更多内存用于自己的进程并分配给其他进程。当一个进程启动时,它向内核请求一块内存。这个初始请求通常是一个很大的请求,进程不会立即或实际上永远不会使用全部。内核意识到进程请求冗余内存的这种趋势,因此会过度分配系统内存。这意味着当系统具有例如 8GB 的 RAM 时,内核可能会分配 8.5GB 给进程。这通过确保分配给进程的内存得到积极使用来最大化系统内存的使用。通常,这种情况不会造成问题。然而,如果有足够多的进程开始使用它们请求的所有内存块,那么将没有足够的物理内存来支持它们。这意味着正在运行的进程需要比实际可用内存更多的内存。这种情况很严重,必须立即解决。
2.2,解决问题的“人”
“OOM Killer”或“Out of Memory Killer”是 Linux 内核在系统内存严重不足时使用的进程,内核调用 OOM Killer 来检查所有正在运行的进程,并杀死其中一个或多个进程,以释放系统内存并保持系统运行。
2.3,解决问题的过程
2.3.1 进程选择
每当发生内存不足故障时,内核都会调用 out_of_memory() 函数。其中使用了 select_bad_process() 函数,该函数从 badness() 函数中获取分数值来衡量进程“坏”的程度。最“坏”的进程就是即将被牺牲的进程。
badness() 函数遵循一些规则来选择进程:
- 内核需要为自己获取最少的内存
- 尝试回收大量内存
- 不会杀死使用较少量内存的进程
- 尝试杀死最少数量的进程
- 一些细致的算法可以提高用户想要杀死的进程的牺牲优先级
另外,OOM机制为每个进程设置“oom_score”,然后将该值乘以内存使用量,具有较大结果值的进程将很有可能被 OOM Killer终止
进程关联的oom_score的值保存在/proc/{pid}/oom_score文件中,如上图
此外,还有一个内核参数oom_score_adj,该参数默认为0,取值范围是-1000~1000,可以使得新的oom_score等于旧的oom_score+oom_score_adj,起到调节oom_score的作用。如果我们真的希望进程不被OOM-Killer杀死,我们可以为其添加一个大的负值,以减少进程被杀死的机会
默认的oom_score_adj如上图
oom_score_adj调整以及调整后的oom_score和oom_score_adj如上图所示
2.3.2 杀死进程
选择一个或多个进程后,OOM-Killer 将调用 oom_kill_task() 函数。该函数负责向进程发送终止/终止信号。在内存不足的情况下 oom_kill() 调用此函数,它可以向进程发送SIGKILL信号。生成内核日志消息。
因为为了能在业务日志也体现OOM kill现象,可以设置系统信号量回调函数进行捕捉打印。
三,关于OOM机制的思考
3.1 OOM可怕吗?
OOM虽然有个杀手进程,但是并不可怕,反而是系统的救星,它会杀死“最罪魁祸首”进程并使系统免于崩溃。Linux 提供了启用和禁用 OOM-Killer 的方法,但不建议禁用 OOM-killer。具体的禁用、启用方法可以另行参考其他文字,这里不再赘述。
3.2 如何利用或者配合OOM机制呢?
我们最终追求目标的是组件或者业务本身的可用性,而不是狭义的死磕“进程不要被OOM杀死”。
OOM Killer在选择牺牲进程时,一般都会选择使用大量内存并且具有大量的子进程的进程,Apache、MySQL、Nginx或邮件服务器等应用程序都是不错的选择。虽然 Web Server 或 DB Server 非常重要,但是当内核调用 OOM Killer 时,情况就很危急了。如果没有通过终止进程来释放内存,服务器将很快崩溃,可用性也将不可保证。
利用OOM机制的特点,我们可以降低所关注进程被杀掉的概率以防止一些极端情况的出现,对于单节点服务的应用性更强一些。高可用集群,容器化,多副本才能从根本上保障业务可用性。