Docker中的OOMkiller比您想象的要难

再一次问好。预期课程开始时,“ Java Developer”准备了另一本小材料的翻译。




最近,一个Plumbr APM用户遇到了一个奇怪的问题,即代码为137 docker -container的紧急停止。配置简单,有几个嵌套的容器和虚拟机,类似于嵌套的玩偶:

  • 在Ubuntu上拥有自己的Iron服务器;
  • 内部装有Ubuntu的许多Docker容器;
  • Docker容器中的Java虚拟机。

在调查问题期间,我们找到了有关此主题的Docker 文档很明显,原因是容器的手动停止,或者是内存不足以及随后的oomkiller(内存不足杀手)干预。

我们看一下syslog,发现oomkiller确实被称为

[138805.608851] java invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=0
[138805.608887] [<ffffffff8116d20e>] oom_kill_process+0x24e/0x3b0
[138805.608916] Task in /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71 killed as a result of limit of /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71
[138805.608902] [<ffffffff8116da84>] pagefault_out_of_memory+0x14/0x90
[138805.608918] memory: usage 3140120kB, limit 3145728kB, failcnt 616038
[138805.608940] memory+swap: usage 6291456kB, limit 6291456kB, failcnt 2837
[138805.609043] Memory cgroup out of memory: Kill process 20611 (java) score 1068 or sacrifice child

如您所见,java进程达到了3145728 KB的限制(大约3 GB),这导致容器停止运行。这很奇怪,因为docker本身的启动限制为4 GB(在文件中docker-compose)。

您可能知道JVM也对内存使用施加了限制。尽管docker本身设置为4 GB限制,但JVM是使用option启动的Xmx=3GB。这甚至可能更加令人困惑,但是请记住,JVM可以使用比-Xmx指示的更多的内存(请参阅有关分析JVM中的内存使用的文章)。

直到我们了解发生了什么。 Docker应该允许使用4 GB。那么,为什么OOMkiller可以在3 GB上工作呢?对信息的进一步搜索使我们得出这样一个事实,即操作系统中的内存存在另一个限制,该限制已部署在硬件上。

对cgroups(对照组)表示感谢。 cgroups是Linux内核引擎,用于限制,控制和说明进程组对资源的使用。与其他解决方案(team nice/etc/security/limits.conf)相比,cgroup提供了更大的灵活性,因为它们可以处理(不足)一组过程。

在我们的情况下,cgroups将内存使用量限制为3 GB(通过memory.limit_in_bytes)。我们有一些进步!

使用Plumbr对GC内存和事件进行的研究表明,JVM大部分时间使用大约700 MB。只有在停止之前,内存分配激增时,才发生异常。随后,他长时间停顿了GC。因此,似乎发生了以下情况:

  • 在JVM中运行的Java代码试图获取大量内存。
  • JVM验证了3 GB的Xmx限制仍然很远,要求为操作系统分配内存。
  • Docker还检查并确认也没有达到其4 GB的限制。
  • 操作系统内核检查cgroup的3 GB限制并杀死容器。
  • JVM在可以处理自己的容器之前停止容器OutOfMemoryError

了解这一点后,我们为docker配置了2.5 GB的所有限制,为java配置了1.5的所有限制。之后,JVM可以处理OutOfMemoryError并抛出OutOfMemoryError异常。这使Plumbr发挥了神奇的作用-获得具有相应堆栈转储的内存快照,并表明存在对数据库的一个查询,在某种情况下,该查询试图加载几乎整个数据库。

发现


即使在这样简单的情况下,也存在三个内存限制:

  • JVM通过参数 -Xmx
  • Docker通过文件中的选项 docker-compose
  • 操作系统通过参数 memory.limit_in_bytes cgroups

因此,遇到OOM杀手时,应注意所有涉及的内存限制。

Docker开发人员的另一个结论。在封闭容器的内存限制高于cgroups限制的情况下,允许此类“嵌套娃娃”运行似乎没有任何意义。在启动带有适当警告的容器时对此进行简单检查可以为用户节省数百小时的调试时间。

就这样。我们正在课程中等你

All Articles