docker資源限制及應用

防僞碼:乘風破浪會有時,直掛雲帆濟滄海



一、本文將介紹 cgroup 如何做到內存,cpu io 速率的隔離

本文用腳本運行示例進程,來驗證 Cgroups 關於 cpu、內存、io 這三部分的隔離效果。

測試機器環境

                             wKioL1hqWHGCi5u_AABkNYcNp_4428.png-wh_50

執行 mount 命令查看 cgroup 的掛載點

wKiom1hqWILy7zM7AAKrYaLLQGM475.png-wh_50

從上圖可以看到 cgroup 掛載在/sys/fs/cgroup 目錄

groups 可以限制 blkiocpucpuacctcpusetdevicesfreezermemorynet_clsns 等系

統的資源,以下是主要子系統的說明:

blkio 這個子系統設置限制每個塊設備的輸入輸出控制。例如:磁盤,光盤以及 usb 等等。

cpu 這個子系統使用調度程序爲 cgroup 任務提供 cpu 的訪問。

cpuacct 產生 cgroup 任務的 cpu 資源報告。

cpuset 如果是多核心的 cpu,這個子系統會爲cgroup 任務分配單獨的 cpu 和內存。

devices 允許或拒絕 cgroup 任務對設備的訪問。

freezer 暫停和恢復 cgroup 任務。

memory 設置每個 cgroup 的內存限制以及產生內存資源報告。

net_cls 標記每個網絡包以供 cgroup 方便使用,它通過使用等級識別符(classid)標記網絡數

據包,從而允許 Linux 流量控制程序(TCTraffic Controller)識別從具體 cgroup 中生成

的數據包。

ns:命名空間子系統

cgroups 管理進程 cpu 資源

我們先看一個限制 cpu 資源的例子:

跑一個耗 cpu 的腳本

運行一個容器,在容器內創建腳本並運行腳本,腳本內容:

wKioL1hqWJbAjglNAACvR7rpV5Q486.png-wh_50

將容器切換到後臺運行

在宿主機上 top 可以看到這個腳本基本佔了 90%多的 cpu 資源

wKiom1hqWKvhf0iQAADi-jFV8JY108.png-wh_50

下面用 cgroups 控制這個進程的 cpu 資源

對於 centos7 來說,通過 systemd-cgls 來查看系統 cgroups tree

#systemd-cgls

wKioL1hqWLuA9wAaAADr7CgnbhM275.png-wh_50

注:4239 就是我們所運行的容器 pid

wKiom1hqWNCRdjKbAACKBWdR1pY497.png-wh_50

cpu.cfs_quota_us 設爲 50000,相對於 cpu.cfs_period_us 100000 50%

wKioL1hqWP-wZSgiAABib_xEn_M444.png-wh_50

進入容器,再次執行腳本,打開宿主機的另一個終端執行 top 命令

然後 top 的實時統計數據如下,cpu 佔用率將近 50%,看來 cgroups 關於 cpu 的控制起了效果

wKiom1hqWRCQcZ0sAADz-yc9oQU613.png-wh_50

CPU 資源控制

CPU 資源的控制也有兩種策略,一種是完全公平調度 CFSCompletelyFair Scheduler

策略,提供了限額和按比例分配兩種方式進行資源控制;另一種是實時調度(Real-Time

Scheduler)策略,針對實時進程按週期分配固定的運行時間。配置時間都以微秒(μs)爲

單位,文件名中用 us 表示。

CFS  調度策略下的配置

按權重比例設定 CPU 的分配

docker 提供了–cpu-shares 參數,在創建容器時指定容器所使用的 CPU 份額值。例如:

使用命令 docker run -tid cpu-shares 100 鏡像,創建容器,則最終生成的 cgroup cpu 份額

配置可以下面的文件中找到:

wKioL1hqWSKzR8JXAABOpuucqKI646.png-wh_50

wKiom1hqWTvg5-eWAAEQECVwoHU600.png-wh_50

cpu-shares 的值不能保證可以獲得 1 vcpu 或者多少 GHz CPU 資源,僅僅只是一個加權

值。

該加權值是一個整數(必須大於等於 2)表示相對權重,最後除以權重總和算出相對比例,

按比例分配 CPU 時間。

默認情況下,每個 docker 容器的 cpu 份額都是 1024。單獨一個容器的份額是沒有意義的,

只有在同時運行多個容器時,容器的 cpu 加權的效果才能體現出來。例如,兩個容器 AB

cpu 份額分別爲 1000 500,在 cpu 進行時間片分配的時候,容器 A 比容器 B 多一倍的

機會獲得 CPU 的時間片。如果容器 A 的進程一直是空閒的,那麼容器 B 是可以獲取比容器

A 更多的 CPU 時間片的。極端情況下,比如說主機上只運行了一個容器,即使它的 cpu 份額

只有 50,它也可以獨佔整個主機的 cpu 資源。

cgroups 只在容器分配的資源緊缺時,也就是說在需要對容器使用的資源進行限制時,纔會

生效。因此,無法單純根據某個容器的 cpu 份額來確定有多少 cpu 資源分配給它,資源分配

結果取決於同時運行的其他容器的 cpu 分配和容器中進程運行情況。

cpu-shares 演示案例:

先刪除 docker 主機上運行的容器

wKiom1hqWVqQJMZZAACGUc8twiY618.png-wh_50

Docker 通過--cpu-shares 指定 CPU 份額

運行一個容器指定 cpu 份額爲 1024

wKioL1hqWWmiRZGVAACbyrBaAwI847.png-wh_50

注:

--cpu-shares 指定 CPU 份額,默認就是 1024

--cpuset-cpus 可以綁定 CPU。例如,指定容器在--cpuset-cpus0,1 --cpuset-cpus 0-3

--cpu stress 命令的選項表示產生 n 個進程 每個進程都反覆不停的計算隨機數的平方根

stress 命令是 linux 下的一個壓力測試工具。

docker 宿主機上打開一個 terminal 執行 top

wKiom1hqWX3Q1b1yAAD1TrX2AW0052.png-wh_50

wKioL1hqWY2zCdkCAACcje1Ma4Q899.png-wh_50

查看 top 的現實結果

wKiom1hqWZ2Cs9JCAADzP6w1nT4416.png-wh_50

可以看到 container1 CPU 佔比爲 1024/(1024+512)=2/3container2 CPU 佔比爲

512/(1024+512)=1/3

container1 cpu.shares 改爲 512

#echo 512 >/sys/fs/cgroup/cpu/system.slice/docker-<容器的完整長ID>/cpu.shares

wKiom1hqWa-wxTMxAAEeiXAh9R4212.png-wh_50

可以看到兩個容器的 CPU 佔比趨於平均

設定 CPU 使用週期使用時間上限

cgroups 裏,可以用cpu.cfs_period_us cpu.cfs_quota_us 來限制該組中的所有進程在單

位時間裏可以使用的 cpu 時間。cpu.cfs_period_us 就是時間週期,默認爲 100000,即百毫

秒。cpu.cfs_quota_us 就是在這期間內可使用的 cpu 時間,默認 -1,即無限制。

cpu.cfs_period_us:設定時間週期(單位爲微秒(μs)),必須與 cfs_quota_us 配合使用。

cpu.cfs_quota_us :設定週期內最多可使用的時間(單位爲微秒(μs))。這裏的配置指 task

對單個 cpu 的使用上限。

舉個例子,如果容器進程需要每 1 秒使用單個 CPU 0.2 秒時間,可以將 cpu-period 設置爲

1000000(即 1 秒),cpu-quota 設置爲 2000000.2 秒)。

當然,在多核情況下,若 cfs_quota_us cfs_period_us 的兩倍,就表示在兩個核上

完全使用 CPU,例如如果允許容器進程需要完全佔用兩個 CPU,則可以將 cpu-period 設置爲

100000(即 0.1 秒),cpu-quota 設置爲 2000000.2 秒)。

使用示例:

使用命令 docker run 創建容器

wKiom1hqWceQvFEAAACgphZXiUU551.png-wh_50

在宿主機上執行 top

wKiom1hqWgrwcd85AAEC3W5xFvA934.png-wh_50

從上圖可以看到基本佔了 100% cpu 資源

則最終生成的 cgroup cpu 週期配置可以下面的目錄中找到:

/sys/fs/cgroup/cpu/system.slice/docker-<容器的完整長ID>/

wKioL1hqWh6As_h1AACDomHg8Bk034.png-wh_50

修改容器的 cpu.cfs_period_us cpu.cfs_quota_us

wKiom1hqWj-RAmSoAACBLhRaEds268.png-wh_50

執行 top 查看 cpu 資源

wKiom1hqWlGBWklmAAD0h8Ly7bk345.png-wh_50

從上圖可以看到基本佔了 50% cpu 資源

RT  調度策略下的配置 實時調度策略與公平調度策略中的按週期分配時間的方法類似,也是

在週期內分配一個固定的運行時間。

cpu.rt_period_us :設定週期時間。

cpu.rt_runtime_us:設定週期中的運行時間。

wKiom1hqWmPg_pJSAACDXgBgn1E462.png-wh_50

cpuset - CPU 綁定

對多核 CPU 的服務器,docker 還可以控制容器運行限定使用哪些 cpu 內核和內存節點,即

使用–cpuset-cpus 和–cpuset-mems 參數。對具有 NUMA 拓撲(具有多 CPU、多內存節點)的

服務器尤其有用,可以對需要高性能計算的容器進行性能最優的配置。如果服務器只有一個

內存節點,則–cpuset-mems 的配置基本上不會有明顯效果

注:

現在的機器上都是有多個 CPU 和多個內存塊的。以前我們都是將內存塊看成是一大塊內存,

所有 CPU 到這個共享內存的訪問消息是一樣的。但是隨着處理器的增加,共享內存可能會

導致內存訪問衝突越來越厲害,且如果內存訪問達到瓶頸的時候,性能就不能隨之增加。

NUMANon-Uniform MemoryAccess)就是這樣的環境下引入的一個模型。比如一臺機

器是有2個處理器,有4個內存塊。我們將1個處理器和兩個內存塊合起來,稱爲一個NUMAnode,這樣這個機器就會有兩個 NUMAnode。在物理分佈上,NUMA node 的處理器和內

存塊的物理距離更小,因此訪問也更快。比如這臺機器會分左右兩個處理器(cpu1, cpu2),

在每個處理器兩邊放兩個內存塊(memory1.1, memory1.2, memory2.1,memory2.2),這樣

NUMA node1 cpu1 訪問 memory1.1 memory1.2 就比訪問memory2.1 memory2.2

更快。所以使用 NUMA 的模式如果能儘量保證本 node 內的 CPU 只訪問本 node 內的內存

塊,那這樣的效率就是最高的。

使用示例:

wKioL1hqWo7RIXgQAAFg9eAH0A8109.png-wh_50

表示創建的容器只能用 012 這三個內核。最終生成的 cgroup cpu 內核配置如下:

wKioL1hqWp3hJDWTAACqX3bgI6U447.png-wh_50

cpuset.cpus:在這個文件中填寫 cgroup 可使用的 CPU 編號,如 0-2,16 代表 012 16

4 CPU

cpuset.mems:與 CPU 類似,表示 cgroup 可使用的 memorynode,格式同上

通過 docker exec <容器 ID> taskset -c -p 1(容器內部第一個進程編號一般爲 1),可以看到容器

中進程與 CPU 內核的綁定關係,可以認爲達到了綁定 CPU 內核的目的。

總結:

CPU 配額控制參數的混合使用

當上面這些參數中時,cpu-shares 控制只發生在容器競爭同一個內核的時間片時,如果通過

cpuset-cpus 指定容器 A 使用內核 0,容器 B 只是用內核 1,在主機上只有這兩個容器使用對

應內核的情況,它們各自佔用全部的內核資源,cpu-shares 沒有明顯效果。

cpu-periodcpu-quota 這兩個參數一般聯合使用,在單核情況或者通過 cpuset-cpus 強制容

器使用一個 cpu 內核的情況下,即使 cpu-quota 超過 cpu-period,也不會使容器使用更多的

CPU 資源。

cpuset-cpuscpuset-mems 只在多核、多內存節點上的服務器上有效,並且必須與實際的物

理配置匹配,否則也無法達到資源控制的目的。

在系統具有多個 CPU 內核的情況下,需要通過 cpuset-cpus 爲容器 CPU 內核才能比較方便地

進行測試。

內存配額控制

CPU 控制一樣,docker 也提供了若干參數來控制容器的內存使用配額,可以控制容器的

swap 大小、可用內存大小等各種內存方面的控制。主要有以下參數:

Docker 提供參數-m,--memory=""限制容器的內存使用量,如果不設置-m,則默認容器內存

是不設限的,容器可以使用主機上的所有空閒內存

內存配額控制使用示例

設置容器的內存上限,參考命令如下所示

#docker run -dit --memory 128m 鏡像

默認情況下,除了–memory 指定的內存大小以外,docker 還爲容器分配了同樣大小的 swap

分區,也就是說,上面的命令創建出的容器實際上最多可以使用 256MB 內存,而不是 128MB內存。如果需要自定義 swap 分區大小,則可以通過聯合使用–memoryswap 參數來實現控

制。

wKiom1hqWrGRHIM2AAD2aRx_TeU606.png-wh_50

可以發現,使用 256MB 進行壓力測試時,由於超過了內存上限(128MB 內存+128MB swap),

進程被 OOM(out of memory)殺死。

使用 250MB 進行壓力測試時,進程可以正常運行。

wKioL1hqWsCCiCUnAAE5GmM-naM499.png-wh_50

通過 docker stats 可以查看到容器的內存已經滿負載了。

#docker stats test2

wKiom1hqWtODkGaTAACA8LqagP0232.png-wh_50

對上面的命令創建的容器,可以查看到在 cgroups 的配置文件中,查看到容器的內存大小爲

128MB (128×1024×1024=134217728B), swap 256MB

(256×1024×1024=268435456B)

#cat/sys/fs/cgroup/memory/system.slice/docker-<容器的完整ID>/memory.limit_in_bytes

134217728

#cat /sys/fs/cgroup/memory/system.slice/docker-<

ID>/memory.memsw.limit_in_bytes

268435456

wKioL1hqWuvgFfH0AAFIKO4tZgA308.png-wh_50

磁盤 IO  配額控制

主要包括以下參數:

--device-read-bps:限制此設備上的讀速度(bytesper second),單位可以是 kbmb 或者 gb

--device-read-iops:通過每秒讀 IO 次數來限制指定設備的讀速度。

--device-write-bps :限制此設備上的寫速度(bytesper second),單位可以是 kbmb 或者 gb

--device-write-iops:通過每秒寫 IO 次數來限制指定設備的寫速度。

--blkio-weight:容器默認磁盤 IO 的加權值,有效值範圍爲10-1000

--blkio-weight-device 針對特定設備的 IO 加權控制。其格式爲DEVICE_NAME:WEIGHT

磁盤 IO 配額控制示例

blkio-weight

使用下面的命令創建兩個–blkio-weight 值不同的容器:

在容器中同時執行下面的 dd 命令,進行測試

wKiom1hqWv3SwKf1AAEoGpaCWJA592.png-wh_50

注:oflag=direct 規避掉文件系統的 cache,把寫請求直接封裝成 io 指令發到硬盤


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章