Docker如何处理OOM杀手和内存限制?

我有一个docker容器,通过一个bash脚本启动一个简单的java(基于jgroups)的应用程序。 java进程被Xmx限制为128m,允许容器使用256m(交换被禁用)。 不幸的是,我不时遇到以下OOM消息:

Jul 07 02:43:54 ip-10-1-2-125 kernel: oom_kill_process: 16 callbacks suppressed Jul 07 02:43:54 ip-10-1-2-125 kernel: java invoked oom-killer: gfp_mask=0x2400040, order=0, oom_score_adj=0 Jul 07 02:43:54 ip-10-1-2-125 kernel: java cpuset=0ead341e639c2f2bd27a38666aa0834c969e8c7e6d2fb21516a2c698adce8d5f mems_allowed=0 Jul 07 02:43:54 ip-10-1-2-125 kernel: CPU: 0 PID: 26686 Comm: java Not tainted 4.4.0-28-generic #47-Ubuntu Jul 07 02:43:54 ip-10-1-2-125 kernel: Hardware name: Xen HVM domU, BIOS 4.2.amazon 05/12/2016 Jul 07 02:43:54 ip-10-1-2-125 kernel: 0000000000000286 000000006ffe9d71 ffff8800bb3c7c88 ffffffff813eb1a3 Jul 07 02:43:54 ip-10-1-2-125 kernel: ffff8800bb3c7d68 ffff880033aea940 ffff8800bb3c7cf8 ffffffff812094fe Jul 07 02:43:54 ip-10-1-2-125 kernel: 000000000000258c 000000000000000a ffffffff81e66760 0000000000000206 Jul 07 02:43:54 ip-10-1-2-125 kernel: Call Trace: Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff813eb1a3>] dump_stack+0x63/0x90 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff812094fe>] dump_header+0x5a/0x1c5 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff811913b2>] oom_kill_process+0x202/0x3c0 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff811fd304>] ? mem_cgroup_iter+0x204/0x390 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff811ff363>] mem_cgroup_out_of_memory+0x2b3/0x300 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff81200138>] mem_cgroup_oom_synchronize+0x338/0x350 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff811fb660>] ? kzalloc_node.constprop.48+0x20/0x20 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff81191a64>] pagefault_out_of_memory+0x44/0xc0 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff8106b2c2>] mm_fault_error+0x82/0x160 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff8106b778>] __do_page_fault+0x3d8/0x400 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff8106b7c2>] do_page_fault+0x22/0x30 Jul 07 02:43:54 ip-10-1-2-125 kernel: [<ffffffff81829838>] page_fault+0x28/0x30 Jul 07 02:43:54 ip-10-1-2-125 kernel: Task in /docker/0ead341e639c2f2bd27a38666aa0834c969e8c7e6d2fb21516a2c698adce8d5f killed as a result of limit of /docker/0ead341e639c2f2bd27a38666aa0834c96 Jul 07 02:43:54 ip-10-1-2-125 kernel: memory: usage 262144kB, limit 262144kB, failcnt 6868 Jul 07 02:43:54 ip-10-1-2-125 kernel: memory+swap: usage 0kB, limit 9007199254740988kB, failcnt 0 Jul 07 02:43:54 ip-10-1-2-125 kernel: kmem: usage 0kB, limit 9007199254740988kB, failcnt 0 Jul 07 02:43:54 ip-10-1-2-125 kernel: Memory cgroup stats for /docker/0ead341e639c2f2bd27a38666aa0834c969e8c7e6d2fb21516a2c698adce8d5f: cache:96KB rss:262048KB rss_huge:135168KB mapped_file:16 Jul 07 02:43:54 ip-10-1-2-125 kernel: [ pid ] uid tgid total_vm rss nr_ptes nr_pmds swapents oom_score_adj name Jul 07 02:43:54 ip-10-1-2-125 kernel: [26659] 0 26659 1127 20 7 3 0 0 sh Jul 07 02:43:54 ip-10-1-2-125 kernel: [26665] 0 26665 1127 20 7 3 0 0 run.sh Jul 07 02:43:54 ip-10-1-2-125 kernel: [26675] 0 26675 688639 64577 204 7 0 0 java Jul 07 02:43:54 ip-10-1-2-125 kernel: Memory cgroup out of memory: Kill process 26675 (java) score 988 or sacrifice child Jul 07 02:43:54 ip-10-1-2-125 kernel: Killed process 26675 (java) total-vm:2754556kB, anon-rss:258308kB, file-rss:0kB Jul 07 02:43:54 ip-10-1-2-125 docker[977]: Killed 

正如你所看到的, 我的应用程序的RSS只是大约64M 。 但由于某种原因,cgroup的RSS是256M(包括128M的巨大页面)。

这是一种操作系统caching? 如果是这样,为什么OOM不会在杀死用户的应用程序之前刷新它们?

并不是所有的Java内存都在堆上。 在Java 8之前,Permgen已经迁移到了Metaspace。 每个线程还有一个堆栈(可能是1Mb),以及JVM的代码。 看起来你的容器太小了。

有Permgen和堆栈大小的可调参数。 Metaspace将根据需要增长。 有一些演示程序可以将Metaspace扩展到巨大的尺寸。

在调整容器大小之前,请研究Java的内存模型。 如果分配的内存太小,JVM本身将会失败并出现内存不足的情况。 如果堆栈太小,线程将会失败。

哦! 看起来我忘了发布答案。

上面的问题是与我的java进程,这是不相关的docker。 我错误地认为OOM报告以千字节打印RSS。 这是错误的 – OOM报告打印的页面,通常是每个4K的数量

在我的情况下,pid 26675需要64577页的RSS等于(64577 * 4K)258'308千字节。 添加2个bash进程给了我们当前CGroup-262144kB的限制。

所以,进一步的分析必须在JVM领域:堆/ metaspace分析,本地内存跟踪,线程等…