默认状况下容器使用的资源是不受限制的。也就是可使用主机内核调度器所容许的最大资源。可是在容器的使用过程当中,常常须要对容器可使用的主机资源进行限制,本文介绍如何限制容器可使用的主机内存。node
限制容器不能过多的使用主机的内存是很是重要的。对于 linux 主机来讲,一旦内核检测到没有足够的内存能够分配,就会扔出 OOME(Out Of Memmory Exception),并开始杀死一些进程用于释放内存空间。糟糕的是任何进程均可能成为内核猎杀的对象,包括 docker daemon 和其它一些重要的程序。更危险的是若是某个支持系统运行的重要进程被干掉了,整个系统也就宕掉了!这里咱们考虑一个比较常见的场景,大量的容器把主机的内存消耗殆尽,OOME 被触发后系统内核当即开始杀进程释放内存。若是内核杀死的第一个进程就是 docker daemon 会怎么样?结果是全部的容器都不工做了,这是不能接受的!
针对这个问题,docker 尝试经过调整 docker daemon 的 OOM 优先级来进行缓解。内核在选择要杀死的进程时会对全部的进程打分,直接杀死得分最高的进程,接着是下一个。当 docker daemon 的 OOM 优先级被下降后(注意容器进程的 OOM 优先级并无被调整),docker daemon 进程的得分不只会低于容器进程的得分,还会低于其它一些进程的得分。这样 docker daemon 进程就安全多了。
咱们能够经过下面的脚本直观的看一下当前系统中全部进程的得分状况:mysql
#!/bin/bash for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do printf "%2d %5d %s\n" \ "$(cat $proc/oom_score)" \ "$(basename $proc)" \ "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)" done 2>/dev/null | sort -nr | head -n 40
此脚本输出得分最高的 40 个进程,并进行了排序:linux
第一列显示进程的得分,mysqld 排到的第一名。显示为 node server.js 的都是容器进程,排名广泛比较靠前。红框中的是 docker daemon 进程,很是的靠后,都排到了 sshd 的后面。sql
有了上面的机制后是否就能够高枕无忧了呢!不是的,docker 的官方文档中一直强调这只是一种缓解的方案,而且为咱们提供了一些下降风险的建议:docker
好了,啰嗦了这么多,其实就是说:经过限制容器使用的内存上限,能够下降主机内存耗尽时带来的各类风险。ubuntu
为了测试容器的内存使用状况,笔者在 ubuntu 的镜像中安装了压力测试工做 stress,并新建立了镜像 u-stress。本文演示用的全部容器都会经过 u-stress 镜像建立(本文运行容器的宿主机为 CentOS7)。下面是建立 u-stress 镜像的 Dockerfile:安全
FROM ubuntu:latest RUN apt-get update && \ apt-get install stress
建立镜像的命令为:bash
$ docker build -t u-stress:latest .
在进入繁琐的设置细节以前咱们先完成一个简单的用例:限制容器可使用的最大内存为 300M。
-m(--memory=) 选项能够完成这样的配置:app
$ docker run -it -m 300M --memory-swap -1 --name con1 u-stress /bin/bash
下面的 stress 命令会建立一个进程并经过 malloc 函数分配内存:ssh
# stress --vm 1 --vm-bytes 500M
经过 docker stats 命令查看实际状况:
上面的 docker run 命令中经过 -m 选项限制容器使用的内存上限为 300M。同时设置 memory-swap 值为 -1,它表示容器程序使用内存的受限,而可使用的 swap 空间使用不受限制(宿主机有多少 swap 容器就可使用多少)。
下面咱们经过 top 命令来查看 stress 进程内存的实际状况:
上面的截图中先经过 pgrep 命令查询 stress 命令相关的进程,进程号比较大的那个是用来消耗内存的进程,咱们就查看它的内存信息。VIRT 是进程虚拟内存的大小,因此它应该是 500M。RES 为实际分配的物理内存数量,咱们看到这个值就在 300M 上下浮动。看样子咱们已经成功的限制了容器可以使用的物理内存数量。
强调一下 --memory-swap 是必需要与 --memory 一块儿使用的。
正常状况下, --memory-swap 的值包含容器可用内存和可用 swap。因此 --memory="300m" --memory-swap="1g" 的含义为:
容器可使用 300M 的物理内存,而且可使用 700M(1G -300M) 的 swap。--memory-swap 竟然是容器可使用的物理内存和可使用的 swap 之和!
把 --memory-swap 设置为 0 和不设置是同样的,此时若是设置了 --memory,容器可使用的 swap 大小为 --memory 值的两倍。
若是 --memory-swap 的值和 --memory 相同,则容器不能使用 swap。下面的 demo 演示了在没有 swap 可用的状况下向系统申请大量内存的场景:
$ docker run -it --rm -m 300M --memory-swap=300M u-stress /bin/bash # stress --vm 1 --vm-bytes 500M
demo 中容器的物理内存被限制在 300M,可是进程却但愿申请到 500M 的物理内存。在没有 swap 可用的状况下,进程直接被 OOM kill 了。若是有足够的 swap,程序至少还能够正常的运行。
咱们能够经过 --oom-kill-disable 选项强行阻止 OOM kill 的发生,可是笔者认为 OOM kill 是一种健康的行为,为何要阻止它呢?
除了限制可用 swap 的大小,还能够设置容器使用 swap 的紧迫程度,这一点和主机的 swappiness 是同样的。容器默认会继承主机的 swappiness,若是要显式的为容器设置 swappiness 值,可使用 --memory-swappiness 选项。
经过限制容器可用的物理内存,能够避免容器内服务异常致使大量消耗主机内存的状况(此时让容器重启是较好的策略),所以能够下降主机内存被耗尽带来的风险。