现在越来越多应用云原生化跑在k8s上面,k8s为应用提供了自动限制、自动重启、服务发现等各种能力。这些能力让开发减少了对运维相关属性的关注,但也让一些开发把一些错误当成了特性来使用,比如针对一些无状态的服务,利用 OOM 和自动重启来恢复。这看起来大多数时候似乎没有问题,借助自动恢复,OOM的应用会被重新来起来工作。但这种坏习惯会让系统在某些时候变得更不稳定,比如 OOM Killer 导致的死锁问题。
最初的现象:节点进入假死状态,登录节点上后ps
等系统命令无法使用,节点监控看CPU、内存、负载情况:
可以看到节点的CPU,内存没什么变化,但负载和IO都变大了的,可以推断大概率是内核和 IO 引起的问题。查看system journal log 看到当时容器发生了一次OOM记录:
查看 dockerd、khugepaged、jdb2 进程处于D状态(TASK_UNINTERRUPTIBLE,不可中断的睡眠状态)。说明都在等待 IO,持续这么长时间看状况应该是死锁了,结合容器 OOM 和ext4/jbd2 死锁找到一条相关的 bug 记录:https://bugs.centos.org/view.php?id=17982
最后检验是和这个 bug 完全吻合的。 简单说下原理:
这个问题其实只在 CentOS 7 的内核版本中出现,算 Centos 的一个内核bug,大家可能觉得系统人员去维护解决bug就行了,但是其实 OOM 引起的系统故障的bug 在 linux 各版本上都十分的多,比如下面这个案例提到的/proc/sys/kernel/printk
和OOM Killer 引起的一个死锁问题
虽然容器的自动重启和恢复可以帮助我们很大程度的解决异常,cgroups帮我们做了命名空间隔离,我们可以看到 OOM Killer 作为内核的一个功能,如果把他本身作为一种特性去对待是十分危险的。当容器进程遇到 OOM 内存泄漏的时候,我们应该去尝试解决他,而不是无视他。