【问题背景】
一台虚拟机crash,从物理机的日志看,触发了oom,于是把虚拟机给kill了,但是事后通过监控查看物理机上的内存空间是比较充足的,尚有40GB的可用内容。
那么问题来了,为什么明明内存足够,缺无法分配?
【问题服务器所在架构】
1台48C256GB的Intel x86服务器(开了超线程,实际为2个物理CPU,24core),在上面创建了两个虚拟机,每个虚拟机为提高性能,采用了vCPU绑核的方式进行了绑定,每个物理CPU(24C中配置23C)对应一个虚拟机。虚拟化采用了qemu-kvm的技术。故每台虚拟机的配置为23C120GB。
【报错日志分析】
oom日志分析
第一段:qemu-kvm需要申请内存,但是发现内存不够,于是触发了oom,触发本次oom的进程PID为2905469.
相应的调用函数为从下往上:
page_fault -> do_page_fault ->...->oom_kill_process->...->dump_stack
汇总当前节点的内存使用情况,Node 1上空余内存为40MB。
开始对当前系统的进程进行排序,最终选择score分数为782(最高)的进程pid 2905469作为第一个释放的pid进行killed。
也就是自己杀死了自己。GG
【寻找蛛丝马迹】
翻了一下numa架构,正常情况下,numa架构下,应该有Node0和Node1两个内存节点,每个内存节点128GB。
正常情况下,即便是进程绑定了CPU,也是代表说CPU优先使用对应NODE中的内存空间,一旦该NODE的内存空间不足了,就会使用另外一个NODE的内存。
然而在本次的oom过程中,只有收集了NODE 1的free内存。并没有手机NODE 0的free内存。这一点也就是事后为什么在监控上还能看到物理机内存剩余空间比较大的原因。这部分free的内存实际来源于NODE 0。
从oom的日志来看,应该是这台虚拟机的配置存在一些特殊情况导致了内存只在NODE 1中进行分配。
进一步分析虚拟机的xml文件,看到有如下配置:
<numatune>
<memory mode="strict" nodeset="1"/>
</numatune>
【根因分析】
numatune是libvirt的一个参数,可以用在numa架构的host上,以控制虚拟机的内存访问策略。
numatune的mode选项有:
-
strict:默认的策略,如果值指定的node上无法分配内存,则虚拟机
-
interleave:通过轮询方式,在指定的多个node上分配内存
-
preferred:优先在指定node上分配内存,如果内存不足,允许在其他node上分配内存
由此,可以看到当前服务器上的xml配置为nodeset=1且mode为strict,因此虚拟机只能使用NODE1上面的内存,也就是128GB,一旦128GB用完了,那么这个虚拟机将无足够的内存使用。
虽然两个虚拟机分别绑定了两个不同的内存NODE,但是除了虚拟机之外,操作系统上还有其他的openstack进程和系统进程也会使用内存,而且分布可能不均衡,因此在长期的运行下,随着内存实际使用率越来越高,最终触发了OOM。
【衍生阅读】