您好,登錄后才能下訂單哦!
默認情況下容器使用的資源是不受限制的。也就是可以使用主機內核調度器所允許的最大資源。但是在容器的使用過程中,經常需要對容器可以使用的主機資源進行限制,本文介紹如何限制容器可以使用的主機內存。
限制容器不能過多的使用主機的內存是非常重要的。對于 linux 主機來說,一旦內核檢測到沒有足夠的內存可以分配,就會扔出 OOME(Out Of Memmory Exception),并開始殺死一些進程用于釋放內存空間。
糟糕的是任何進程都可能成為內核獵殺的對象,包括 docker daemon 和其它一些重要的程序。更危險的是如果某個支持系統運行的重要進程被干掉了,整個系統也就宕掉了!
這里我們考慮一個比較常見的場景,大量的容器把主機的內存消耗殆盡,OOME 被觸發后系統內核立即開始殺進程釋放內存。如果內核殺死的第一個進程就是 docker daemon 會怎么樣?結果是所有的容器都不工作了,這是不能接受的!
針對這個問題,docker 嘗試通過調整 docker daemon 的 OOM 優先級來進行緩解。內核在選擇要殺死的進程時會對所有的進程打分,直接殺死得分最高的進程,接著是下一個。
當 docker daemon 的 OOM 優先級被降低后(注意容器進程的 OOM 優先級并沒有被調整),docker daemon 進程的得分不僅會低于容器進程的得分,還會低于其它一些進程的得分。這樣 docker daemon 進程就安全多了。
我們可以通過下面的腳本直觀的看一下當前系統中所有進程的得分情況:
#!/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 個進程,并進行了排序:
第一列顯示進程的得分,mysqld 排到的第一名。顯示為 node server.js 的都是容器進程,排名普遍比較靠前。紅框中的是 docker daemon 進程,非常的靠后,都排到了 sshd 的后面。
有了上面的機制后是否就可以高枕無憂了呢!不是的,docker 的官方文檔中一直強調這只是一種緩解的方案,并且為我們提供了一些降低風險的建議:
通過測試掌握應用對內存的需求
保證運行容器的主機有充足的內存
限制容器可以使用的內存
好了,啰嗦了這么多,其實就是說:通過限制容器使用的內存上限,可以降低主機內存耗盡時帶來的各種風險。
為了測試容器的內存使用情況,筆者在 ubuntu 的鏡像中安裝了壓力測試工作 stress,并新創建了鏡像 u-stress。本文演示用的所有容器都會通過 u-stress 鏡像創建(本文運行容器的宿主機為 CentOS7)。
下面是創建 u-stress 鏡像的 Dockerfile:
FROM ubuntu:latest
RUN apt-get update && \
apt-get install stress
創建鏡像的命令為:
$ docker build -t u-stress:latest .
-m, --memory=""Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.
--memory=""Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.
--memory-swappiness=""Tune a container’s memory swappiness behavior. Accepts an integer between 0 and 100.(調整容器的內存swappiness行為。接受0到100之間的整數)
--memory-reservation=""Memory soft limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g.
--oom-kill-disable=falseWhether to disable OOM Killer for the container or not.
--kernel-memory=""Kernel memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.kernel memory 沒有特殊需求,則無需額外設置
-m(--memory=) 選項可以完成這樣的配置:
$ docker run -it -m 300M --memory-swap -1 --name con1 u-stress /bin/bash
下面的 stress 命令會創建一個進程并通過 malloc 函數分配內存:
# stress --vm 1 --vm-bytes 500M
通過 docker stats 命令查看實際情況:
上面的 docker run 命令中通過 -m 選項限制容器使用的內存上限為 300M。同時設置 memory-swap 值為 -1,它表示容器程序使用內存的受限,而可以使用的 swap 空間使用不受限制(宿主機有多少 swap 容器就可以使用多少)。如果 --memory-swap 設置小于 --memory則設置不生效,使用默認設置)。
下面我們通過 top 命令來查看 stress 進程內存的實際情況:
上面的截圖中先通過 pgrep 命令查詢 stress 命令相關的進程,進程號比較大的那個是用來消耗內存的進程,我們就查看它的內存信息。VIRT 是進程虛擬內存的大小,所以它應該是 500M。RES 為實際分配的物理內存數量,我們看到這個值就在 300M 上下浮動。看樣子我們已經成功的限制了容器能夠使用的物理內存數量。
也可以通過如下命令獲取 stress 進程的 swap 占用:
for file in /proc/*/status ; do awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 2 -n -r | grep stress
強調一下 --memory-swap 是必須要與 --memory 一起使用的。但是凡事沒有絕對,如果你非要不添加--memory-swap 選項呢?
如:docker run -it --rm -m 100M ubuntu-stress:latest /bin/bash
按照官方文檔的理解,如果指定 -m 內存限制時不添加 --memory-swap 選項或 --memory-swap 設置為 0 ,則表示容器中程序可以使用 100M 內存和 100M swap 內存。默認情況下,--memory-swap 會被設置成 memory 的 2倍。
We set memory limit(300M) only, this means the processes in the container can use 300M memory and 300M swap memory, by default, the total virtual memory size --memory-swapwill be set as double of memory, in this case, memory + swap would be 2*300M, so processes can use 300M swap memory as well.
如果按照以上方式運行容器提示如下信息:
WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.
可參考 Adjust memory and swap accounting 獲取解決方案:
To enable memory and swap on system using GNU GRUB (GNU GRand Unified Bootloader), do the following:
(1)Log into Ubuntu as a user with sudo privileges.
(2)Edit the /etc/default/grub file.
(3)Set the GRUB_CMDLINE_LINUX value as follows: GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
(4)Save and close the file.
(5)Update GRUB. $ sudo update-grub
(6)Reboot your system.
正常情況下, --memory-swap 的值包含容器可用內存和可用 swap。所以 --memory="300m" --memory-swap="1g" 的含義為:
容器可以使用 300M 的物理內存,并且可以使用 700M(1G -300M) 的 swap。--memory-swap 居然是容器可以使用的物理內存和可以使用的 swap 之和!
如果 --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 是一種健康的行為,為什么要阻止它呢?
swappiness 可以認為是宿主/proc/sys/vm/swappiness
設定:
Swappiness is a Linux kernel parameter that controls the relative weight given to swapping out runtime memory, as opposed to dropping pages from the system page cache. Swappiness can be set to values between 0 and 100 inclusive. A low value causes the kernel to avoid swapping, a higher value causes the kernel to try to use swap space.Swappiness
--memory-swappiness=0 表示禁用容器 swap 功能(這點不同于宿主機,宿主機 swappiness 設置為 0 也不保證 swap 不會被使用):
docker run -it --rm -m 100M --memory-swappiness=0 ubuntu-stress:latest /bin/bash
? ~ docker run -it --rm -m 100M --memory-swappiness=0 ubuntu-stress:latest /bin/bash
root@e3fd6cc73849:/# stress --vm 1 --vm-bytes 100M # 沒有任何商量的余地,到達 100M 直接被 kill
stress: info: [18] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: FAIL: [18] (416) <-- worker 19 got signal 9
stress: WARN: [18] (418) now reaping child worker processes
stress: FAIL: [18] (452) failed run completed in 0s
root@e3fd6cc73849:/#
--memory-reservation ...
選項可以理解為內存的軟限制。如果不設置 -m 選項,那么容器使用內存可以理解為是不受限的。按照官方的說法,memory reservation 設置可以確保容器不會長時間占用大量內存。
? ~ docker run -it --rm -m 100M --memory-swappiness=0 --oom-kill-disable ubuntu-stress:latest /bin/bash
root@f54f93440a04:/# stress --vm 1 --vm-bytes 200M # 正常情況不添加 --oom-kill-disable 則會直接 OOM kill,加上之后則達到限制內存之后也不會被 kill
stress: info: [17] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
但是如果是以下的這種沒有對容器作任何資源限制的情況,添加 --oom-kill-disable 選項就比較 危險 了:
$ docker run -it --oom-kill-disable ubuntu:14.04 /bin/bash
因為此時容器內存沒有限制,并且不會被 oom kill,此時系統則會 kill 系統進程用于釋放內存。
Kernel memory is fundamentally different than user memory as kernel memory can’t be swapped out. The inability to swap makes it possible for the container to block system services by consuming too much kernel memory. Kernel memory includes:
stack pages
slab pages
sockets memory pressure
這里直接引用 Docker 官方介紹,如果無特殊需求,kernel-memory 一般無需設置,這里不作過多說明。
Docker內存資源限制
進程不見了,Linux 的OOM Killer
十問 Linux 虛擬內存管理
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。