默認狀況下容器使用的資源是不受限制的。也就是可使用主機內核調度器所容許的最大資源。可是在容器的使用過程當中,常常須要對容器可使用的主機資源進行限制,本文介紹如何限制容器可使用的主機內存。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 選項。
經過限制容器可用的物理內存,能夠避免容器內服務異常致使大量消耗主機內存的狀況(此時讓容器重啓是較好的策略),所以能夠下降主機內存被耗盡帶來的風險。