限制Docker容器的内存无效。

46

我正在Ubuntu 13.04 (Raring Ringtail)系统上运行最新版本的Docker:

root@docker:~# docker version
Client version: 0.6.6
Go version (client): go1.2rc3
Git commit (client): 6d42040
Server version: 0.6.6
Git commit (server): 6d42040
Go version (server): go1.2rc3
Last stable version: 0.6.6

但是当我启动容器时,

root@docker:~# docker run -m=1524288 -i  -t ubuntu /bin/bash
root@7b09f638871a:/# free -m
             total       used       free     shared    buffers     cached
Mem:          1992        608       1383          0         30        341
-/+ buffers/cache:        237       1755
Swap:         2047          0       2047

我没有看到任何限制,而且我的内核启用了cgroups内存限制:

kernel /boot/vmlinuz-3.8.0-33-generic ro console=tty0 root=/dev/xvda1 cgroup_enable=memory swapaccount=1

我在这里错过了什么显而易见的东西吗?

对此进行跟进,我发现在虚拟化服务器上运行容器化应用程序与在裸机上有一些有趣的区别。例如,在运行 Java 服务的容器中的虚拟化 Ubuntu 服务器上,OOM 将会杀死 Java。然而,在金属上,Java 将遵守通过 Docker 设置的内存限制。[我还不了解两者之间的实现细节,因此无法得出结论,只是想分享一下] - nerdwaller
6个回答

52

free命令无法显示此信息,因为这是通过cgroups进行强制控制的。相反,在容器外的主机上,您可以使用/sysfs和cgroup内存来检查:

vagrant@precise64:~$ docker run -m=524288 -d  -t busybox sleep 3600
f03a017b174f
vagrant@precise64:~$ cat /sys/fs/cgroup/memory/lxc/f03a017b174ff1022e0f46bc1b307658c2d96ffef1dd97e7c1929a4ca61ab80f//memory.limit_in_bytes
524288

如果想看到内存不足的情况,可以运行一些比你分配的内存更多的东西 - 例如:

docker run -m=524288 -d -p 8000:8000 -t ubuntu:12.10  /usr/bin/python3 -m http.server
8480df1d2d5d
vagrant@precise64:~$ docker ps | grep 0f742445f839
vagrant@precise64:~$ docker ps -a | grep 0f742445f839
0f742445f839        ubuntu:12.10        /usr/bin/python3 -m    16 seconds ago       Exit 137                                blue_pig

dmesg 中,您应该看到容器和进程已被终止:
[  583.447974] Pid: 1954, comm: python3 Tainted: GF          O 3.8.0-33-generic #48~precise1-Ubuntu
[  583.447980] Call Trace:
[  583.447998]  [<ffffffff816df13a>] dump_header+0x83/0xbb
[  583.448108]  [<ffffffff816df1c7>] oom_kill_process.part.6+0x55/0x2cf
[  583.448124]  [<ffffffff81067265>] ? has_ns_capability_noaudit+0x15/0x20
[  583.448137]  [<ffffffff81191cc1>] ? mem_cgroup_iter+0x1b1/0x200
[  583.448150]  [<ffffffff8113893d>] oom_kill_process+0x4d/0x50
[  583.448171]  [<ffffffff816e1cf5>] mem_cgroup_out_of_memory+0x1f6/0x241
[  583.448187]  [<ffffffff816e1e7f>] mem_cgroup_handle_oom+0x13f/0x24a
[  583.448200]  [<ffffffff8119000d>] ? mem_cgroup_margin+0xad/0xb0
[  583.448212]  [<ffffffff811949d0>] ? mem_cgroup_charge_common+0xa0/0xa0
[  583.448224]  [<ffffffff81193ff3>] mem_cgroup_do_charge+0x143/0x170
[  583.448236]  [<ffffffff81194125>] __mem_cgroup_try_charge+0x105/0x350
[  583.448249]  [<ffffffff81194987>] mem_cgroup_charge_common+0x57/0xa0
[  583.448261]  [<ffffffff8119517a>] mem_cgroup_newpage_charge+0x2a/0x30
[  583.448275]  [<ffffffff8115b4d3>] do_anonymous_page.isra.35+0xa3/0x2f0
[  583.448288]  [<ffffffff8115f759>] handle_pte_fault+0x209/0x230
[  583.448301]  [<ffffffff81160bb0>] handle_mm_fault+0x2a0/0x3e0
[  583.448320]  [<ffffffff816f844f>] __do_page_fault+0x1af/0x560
[  583.448341]  [<ffffffffa02b0a80>] ? vfsub_read_u+0x30/0x40 [aufs]
[  583.448358]  [<ffffffffa02ba3a7>] ? aufs_read+0x107/0x140 [aufs]
[  583.448371]  [<ffffffff8119bb50>] ? vfs_read+0xb0/0x180
[  583.448384]  [<ffffffff816f880e>] do_page_fault+0xe/0x10
[  583.448396]  [<ffffffff816f4bd8>] page_fault+0x28/0x30
[  583.448405] Task in /lxc/0f742445f8397ee7928c56bcd5c05ac29dcc6747c6d1c3bdda80d8e688fae949 killed as a result of limit of /lxc/0f742445f8397ee7928c56bcd5c05ac29dcc6747c6d1c3bdda80d8e688fae949
[  583.448412] memory: usage 416kB, limit 512kB, failcnt 342

谢谢,现在我明白了;所以最好的方法是检查cgroup内存以查看当前使用情况。 - Disco
您可以在此处阅读有关cgroup内存指标的更多信息:http://blog.docker.io/2013/10/gathering-lxc-docker-containers-metrics/特别是memory.stat伪文件。 - user146416
非常感谢。在Ubuntu上配置此项的详细信息请参见https://github.com/dotcloud/docker/issues/4250 - user246672
如果上述方法对您不起作用,那可能是因为您的Docker使用了“本地”libcontainer驱动程序(这是现在的默认设置),而不是lxc。在这种情况下,容器内存统计和限制位于/sys/fs/cgroup/memory/docker/<CONT_ID>/memory.stat/sys/fs/cgroup/memory/docker/<CONT_ID>/memory.limit_in_bytes - skozin
3
docker stats 还提供了交互式界面,显示容器资源限制和当前使用情况。 - Max Desiatov
dmesg 是什么,我如何使用它来查看容器进程被杀死的情况? - gsalgadotoledo

24

我正在链接到这篇好文章,讨论容器内存使用的重点。以下是摘要,稍作修改以适用于Docker而不是通用的LXC:

启动一个具有记忆限制的容器:

$ sudo docker run -m 512M -it ubuntu /bin/bash
root# apt-get update && apt-get install -y build-essential

在容器内创建一个名为foo.c的文件,其内容如下:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    int i;
    for (i=0; i<65536; i++) {
        char *q = malloc(65536);
        printf ("Malloced: %ld \n", (long)65536*i);
    }
    sleep(9999999);
}

编译文件:

gcc -o foo foo.c

打开一个新的终端来监视容器的内存使用情况:

cd /sys/fs/cgroup/memory/lxc/{{containerID}}
while true; do echo -n "Mem Usage (mb): " && expr `cat memory.usage_in_bytes` / 1024 / 1024; echo -n "Mem+swap Usage (mb): " && expr `cat memory.limit_in_bytes` / 1024 / 1024; sleep 1; done

启动容器中的内存消耗

./foo

现在看着你的容器达到最大限度。注意:当内存不足时,malloc函数开始失败,但容器本身保持不变。通常情况下,容器内部的软件由于malloc函数的失败而崩溃,但具有弹性的软件将继续运行。
最后注意:Docker的-m标志不会单独计算交换空间和RAM。如果你使用-m 512M,那么其中一部分512将是交换空间,而不是RAM。如果你只想使用RAM,你需要直接使用LXC选项(这意味着你需要使用LXC执行驱动程序而不是libcontainer来运行Docker)。
# Same as docker -m 512m
sudo docker run --lxc-conf="lxc.cgroup.memory.limit_in_bytes=512M" -it ubuntu /bin/bash

# Set total to equal maximum RAM (for example, don't use swap)
sudo docker run --lxc-conf="lxc.cgroup.memory.max_usage_in_bytes=512M" --lxc-conf="lxc.cgroup.memory.limit_in_bytes=512M" -it ubuntu /bin/bash

在使用交换空间与不使用时,存在明显的差异。使用交换空间,上面的 foo 程序很快达到约450 MB,然后慢慢消耗剩余部分,而仅使用 RAM 时,我的内存使用立即跳至511 MB。使用交换空间时,容器的内存占用标记为约60 MB,这基本上是将交换空间计算为“使用量”。没有交换空间时,当我进入容器时,我的内存使用量少于10 MB。


我试图让collectd和docker一起工作。然而,Collectd发布的是整个系统(主机)的RAM使用情况,而不是docker受限内存的使用情况。http://stackoverflow.com/q/37881322/1925997 我想知道这个选项是否有帮助,但是当我用--lxc-conf="lxc.cgroup.memory.limit_in_bytes=512M"运行docker时,我遇到了“提供但未定义的标志:--lxc-conf”错误。有什么解决方法吗? - Kishore Bandi
很棒的回答!顺便提一下,我认为你在第一个代码行的例子中漏掉了 run 命令:sudo docker *run* -m 512M -it ubuntu /bin/bash - gsalgadotoledo
感谢 @gsalgadotoedo 修复。 - Hamy

3
运行命令:docker stats,以查看您在容器上指定的内存限制。

1
如果您正在使用较新版本的Docker,则应查找该信息的位置为/sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytes
docker run --memory="198m" redis
docker ps --no-trunc` # to get the container long_id
313105b341eed869bcc355c4b3903b2ede2606a8f1b7154e64f913113db8b44a

cat /sys/fs/cgroup/memory/docker/313105b341eed869bcc355c4b3903b2ede2606a8f1b7154e64f913113db8b44a/memory.limit_in_bytes
207618048 # in bytes

0

Debian GNU/Linux 10(buster)

Docker 4.1.1

$ docker run -m 4g -it b5e1eb14396b /bin/bash 


$ cat /sys/fs/cgroup/memory/memory.limit_in_bytes
4294967296
#4.294967296 Gigabytes

但是当我安装软件包时,仍然会出现内存不足的情况,这些命令显示内存很小(默认?)。交换似乎也无法解决问题。

#inside container
$ free
              total        used        free      shared  buff/cache   available
Mem:        2033396      203060      784600       87472     1045736     1560928

# outside container
$ docker stats
CONTAINER ID   NAME             CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O     PIDS
18bd88308490   gallant_easley   0.00%     1.395MiB / 1.939GiB   0.07%     1.62kB / 384B   1.95MB / 0B   1

啊,我忘记了Docker UI中那些[用户友好的]资源限制。如果您尝试超过它们,它应该会警告您。

enter image description here


0

-m开关确实有效(设置硬内存限制),并接受人类可读的k|m|g内存单位。

您可以使用docker inspect来验证它对"Memory"键产生了期望的效果:

$ docker run --rm -d --name ubuntu -m 8g ubuntu:focal && docker inspect ubuntu | grep Memory
            "Memory": 8589934592,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": -1,
            "MemorySwappiness": null,

$ docker run --rm -d --name ubuntu -m 16g ubuntu:focal && docker inspect ubuntu | grep Memory
            "Memory": 17179869184,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": -1,
            "MemorySwappiness": null,

您还可以设置突发限制,即内存请求/预留/保证最小值(这不会保护主机免于崩溃,但会保护容器化应用程序在达到物理极限之前耗尽内存):

$ docker run --rm -d --name ubuntu --memory-reservation 16g ubuntu:focal && docker inspect ubuntu | grep Memory
            "Memory": 0,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 17179869184,
            "MemorySwap": 0,
            "MemorySwappiness": null,

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接