溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Docker內(nèi)存使用

發(fā)布時間:2020-09-23 09:04:30 來源:網(wǎng)絡 閱讀:1327 作者:品鑒初心 欄目:云計算

前言

默認情況下容器使用的資源是不受限制的。也就是可以使用主機內(nèi)核調度器所允許的最大資源。但是在容器的使用過程中,經(jīng)常需要對容器可以使用的主機資源進行限制,本文介紹如何限制容器可以使用的主機內(nèi)存。

為什么要限制容器對內(nèi)存的使用

限制容器不能過多的使用主機的內(nèi)存是非常重要的。對于 linux 主機來說,一旦內(nèi)核檢測到?jīng)]有足夠的內(nèi)存可以分配,就會扔出 OOME(Out Of Memmory Exception),并開始殺死一些進程用于釋放內(nèi)存空間。

糟糕的是任何進程都可能成為內(nèi)核獵殺的對象,包括 docker daemon 和其它一些重要的程序。更危險的是如果某個支持系統(tǒng)運行的重要進程被干掉了,整個系統(tǒng)也就宕掉了!

這里我們考慮一個比較常見的場景,大量的容器把主機的內(nèi)存消耗殆盡,OOME 被觸發(fā)后系統(tǒng)內(nèi)核立即開始殺進程釋放內(nèi)存。如果內(nèi)核殺死的第一個進程就是 docker daemon 會怎么樣?結果是所有的容器都不工作了,這是不能接受的!

針對這個問題,docker 嘗試通過調整 docker daemon 的 OOM 優(yōu)先級來進行緩解。內(nèi)核在選擇要殺死的進程時會對所有的進程打分,直接殺死得分最高的進程,接著是下一個。

當 docker daemon 的 OOM 優(yōu)先級被降低后(注意容器進程的 OOM 優(yōu)先級并沒有被調整),docker daemon 進程的得分不僅會低于容器進程的得分,還會低于其它一些進程的得分。這樣 docker daemon 進程就安全多了。

我們可以通過下面的腳本直觀的看一下當前系統(tǒng)中所有進程的得分情況:

#!/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 個進程,并進行了排序:

Docker內(nèi)存使用

第一列顯示進程的得分,mysqld 排到的第一名。顯示為 node server.js 的都是容器進程,排名普遍比較靠前。紅框中的是 docker daemon 進程,非常的靠后,都排到了 sshd 的后面。

有了上面的機制后是否就可以高枕無憂了呢!不是的,docker 的官方文檔中一直強調這只是一種緩解的方案,并且為我們提供了一些降低風險的建議:

  • 通過測試掌握應用對內(nèi)存的需求

  • 保證運行容器的主機有充足的內(nèi)存

  • 限制容器可以使用的內(nèi)存

  • 為主機配置 swap

好了,啰嗦了這么多,其實就是說:通過限制容器使用的內(nèi)存上限,可以降低主機內(nèi)存耗盡時帶來的各種風險。

壓力測試工具 stress

為了測試容器的內(nèi)存使用情況,筆者在 ubuntu 的鏡像中安裝了壓力測試工作 stress,并新創(chuàng)建了鏡像 u-stress。本文演示用的所有容器都會通過 u-stress 鏡像創(chuàng)建(本文運行容器的宿主機為 CentOS7)。

下面是創(chuàng)建 u-stress 鏡像的 Dockerfile:

FROM ubuntu:latest

RUN apt-get update && \
        apt-get install stress

創(chuàng)建鏡像的命令為:

$ docker build -t u-stress:latest .

目前 Docker 支持內(nèi)存資源限制選項

  • -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.(調整容器的內(nèi)存swappiness行為。接受0到100之間的整數(shù))

  • --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 沒有特殊需求,則無需額外設置

限制內(nèi)存使用上限

-m ... --memory-swap ...

-m(--memory=) 選項可以完成這樣的配置:

$ docker run -it -m 300M --memory-swap -1 --name con1 u-stress /bin/bash

下面的 stress 命令會創(chuàng)建一個進程并通過 malloc 函數(shù)分配內(nèi)存:

# stress --vm 1 --vm-bytes 500M

通過 docker stats 命令查看實際情況:

Docker內(nèi)存使用

上面的 docker run 命令中通過 -m 選項限制容器使用的內(nèi)存上限為 300M。同時設置 memory-swap 值為 -1,它表示容器程序使用內(nèi)存的受限,而可以使用的 swap 空間使用不受限制(宿主機有多少 swap 容器就可以使用多少)。如果 --memory-swap 設置小于 --memory則設置不生效,使用默認設置)。

下面我們通過 top 命令來查看 stress 進程內(nèi)存的實際情況:

Docker內(nèi)存使用

上面的截圖中先通過 pgrep 命令查詢 stress 命令相關的進程,進程號比較大的那個是用來消耗內(nèi)存的進程,我們就查看它的內(nèi)存信息。VIRT 是進程虛擬內(nèi)存的大小,所以它應該是 500M。RES 為實際分配的物理內(nèi)存數(shù)量,我們看到這個值就在 300M 上下浮動。看樣子我們已經(jīng)成功的限制了容器能夠使用的物理內(nèi)存數(shù)量。

也可以通過如下命令獲取 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

限制可用的 swap 大小

強調一下 --memory-swap 是必須要與 --memory 一起使用的。但是凡事沒有絕對,如果你非要不添加--memory-swap 選項呢?

如:docker run -it --rm -m 100M ubuntu-stress:latest /bin/bash

按照官方文檔的理解,如果指定 -m 內(nèi)存限制時不添加 --memory-swap 選項或 --memory-swap 設置為 0 ,則表示容器中程序可以使用 100M 內(nèi)存和 100M swap 內(nèi)存。默認情況下,--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 的值包含容器可用內(nèi)存和可用 swap。所以 --memory="300m" --memory-swap="1g" 的含義為:

容器可以使用 300M 的物理內(nèi)存,并且可以使用 700M(1G -300M) 的 swap。--memory-swap 居然是容器可以使用的物理內(nèi)存和可以使用的 swap 之和!

如果 --memory-swap 的值和 --memory 相同,則容器不能使用 swap。

下面的 demo 演示了在沒有 swap 可用的情況下向系統(tǒng)申請大量內(nèi)存的場景:

$ docker run -it --rm -m 300M --memory-swap=300M u-stress /bin/bash
# stress --vm 1 --vm-bytes 500M

Docker內(nèi)存使用

demo 中容器的物理內(nèi)存被限制在 300M,但是進程卻希望申請到 500M 的物理內(nèi)存。在沒有 swap 可用的情況下,進程直接被 OOM kill 了。如果有足夠的 swap,程序至少還可以正常的運行。

我們可以通過 --oom-kill-disable 選項強行阻止 OOM kill 的發(fā)生,但是筆者認為 OOM kill 是一種健康的行為,為什么要阻止它呢?

-m ... --memory-swappiness ...

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 ...

--memory-reservation ...選項可以理解為內(nèi)存的軟限制。如果不設置 -m 選項,那么容器使用內(nèi)存可以理解為是不受限的。按照官方的說法,memory reservation 設置可以確保容器不會長時間占用大量內(nèi)存。

--oom-kill-disable

?  ~ 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,加上之后則達到限制內(nèi)存之后也不會被 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

因為此時容器內(nèi)存沒有限制,并且不會被 oom kill,此時系統(tǒng)則會 kill 系統(tǒng)進程用于釋放內(nèi)存。

--kernel-memory

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

  • tcp memory pressure

這里直接引用 Docker 官方介紹,如果無特殊需求,kernel-memory 一般無需設置,這里不作過多說明。

參考文檔

  • Docker內(nèi)存資源限制

  • 進程不見了,Linux 的OOM Killer

  • 十問 Linux 虛擬內(nèi)存管理

  • Docker: 限制容器可用的內(nèi)存
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI