docker技術(shù)剖析--docker資源限制及應(yīng)用總結(jié)

2018-06-08 18:11 更新

防偽碼:有花堪折直須折,莫待無花空折枝。

一、本文將介紹 cgroup 如何做到內(nèi)存,cpu 和 io 速率的隔離

本文用腳本運(yùn)行示例進(jìn)程,來驗(yàn)證 Cgroups 關(guān)于 cpu、內(nèi)存、io 這三部分的隔離效果。

測試機(jī)器環(huán)境(docker 1.12版本)

啟動 Cgroups

systemctl enable cgconfig.service

systemctl start cgconfig.service

執(zhí)行 mount 命令查看 cgroup 的掛載點(diǎn)

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

groups 可以限制 blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns 等系

統(tǒng)的資源,以下是主要子系統(tǒng)的說明:

blkio 這個(gè)子系統(tǒng)設(shè)置限制每個(gè)塊設(shè)備的輸入輸出控制。例如:磁盤,光盤以及 usb 等等。

cpu 這個(gè)子系統(tǒng)使用調(diào)度程序?yàn)?cgroup 任務(wù)提供 cpu 的訪問。

cpuacct 產(chǎn)生 cgroup 任務(wù)的 cpu 資源報(bào)告。

cpuset 如果是多核心的 cpu,這個(gè)子系統(tǒng)會為 cgroup 任務(wù)分配單獨(dú)的 cpu 和內(nèi)存。

devices 允許或拒絕 cgroup 任務(wù)對設(shè)備的訪問。

freezer 暫停和恢復(fù) cgroup 任務(wù)。

memory 設(shè)置每個(gè) cgroup 的內(nèi)存限制以及產(chǎn)生內(nèi)存資源報(bào)告。

net_cls 標(biāo)記每個(gè)網(wǎng)絡(luò)包以供 cgroup 方便使用,它通過使用等級識別符(classid)標(biāo)記網(wǎng)絡(luò)數(shù)

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

的數(shù)據(jù)包。

ns:命名空間子系統(tǒng)

cgroups 管理進(jìn)程 cpu 資源

我們先看一個(gè)限制 cpu 資源的例子:

跑一個(gè)耗 cpu 的腳本

運(yùn)行一個(gè)容器,在容器內(nèi)創(chuàng)建腳本并運(yùn)行腳本,腳本內(nèi)容:

將容器切換到后臺運(yùn)行

ctrl + p && ctrl+q

在宿主機(jī)上 top 可以看到這個(gè)腳本基本占了 90%多的 cpu 資源

下面用 cgroups 控制這個(gè)進(jìn)程的 cpu 資源

對于 centos7 來說,通過 systemd-cgls 來查看系統(tǒng) cgroups tree:

#systemd-cgls

注:5028 就是我們所運(yùn)行的容器 pid

將 cpu.cfs_quota_us 設(shè)為 50000,相對于 cpu.cfs_period_us 的 100000 是 50%

進(jìn)入容器,再次執(zhí)行腳本,打開宿主機(jī)的另一個(gè)終端執(zhí)行 top 命令

然后 top 的實(shí)時(shí)統(tǒng)計(jì)數(shù)據(jù)如下,cpu 占用率將近 50%,看來 cgroups 關(guān)于 cpu 的控制起了效果

CPU 資源控制

CPU 資源的控制也有兩種策略,一種是完全公平調(diào)度 (CFS:Completely Fair Scheduler)

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

Scheduler)策略,針對實(shí)時(shí)進(jìn)程按周期分配固定的運(yùn)行時(shí)間。配置時(shí)間都以微秒(μs)為

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

CFS  調(diào)度策略下的配置

按權(quán)重比例設(shè)定 CPU 的分配

docker 提供了–cpu-shares 參數(shù),在創(chuàng)建容器時(shí)指定容器所使用的 CPU 份額值。例如:

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


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

# cat /sys/fs/cgroup/cpu/docker-<容器的完整長 ID>/cpu.shares

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

值。

該加權(quán)值是一個(gè)整數(shù)(必須大于等于 2)表示相對權(quán)重,最后除以權(quán)重總和算出相對比例,

按比例分配 CPU 時(shí)間。

默認(rèn)情況下,每個(gè) docker 容器的 cpu 份額都是 1024。單獨(dú)一個(gè)容器的份額是沒有意義的,

只有在同時(shí)運(yùn)行多個(gè)容器時(shí),容器的 cpu 加權(quán)的效果才能體現(xiàn)出來。例如,兩個(gè)容器 A、B

的 cpu 份額分別為 1000 和 500,在 cpu 進(jìn)行時(shí)間片分配的時(shí)候,容器 A 比容器 B 多一倍的

機(jī)會獲得 CPU 的時(shí)間片。如果容器 A 的進(jìn)程一直是空閑的,那么容器 B 是可以獲取比容器

A 更多的 CPU 時(shí)間片的。極端情況下,比如說主機(jī)上只運(yùn)行了一個(gè)容器,即使它的 cpu 份額

只有 50,它也可以獨(dú)占整個(gè)主機(jī)的 cpu 資源。

cgroups 只在容器分配的資源緊缺時(shí),也就是說在需要對容器使用的資源進(jìn)行限制時(shí),才會

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

結(jié)果取決于同時(shí)運(yùn)行的其他容器的 cpu 分配和容器中進(jìn)程運(yùn)行情況。


cpu-shares 演示案例:

先刪除 docker 主機(jī)上運(yùn)行的容器

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

運(yùn)行一個(gè)容器指定 cpu 份額為 1024

注:

--cpu-shares 指定 CPU 份額,默認(rèn)就是 1024

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

--cpu 是 stress 命令的選項(xiàng)表示產(chǎn)生 n 個(gè)進(jìn)程 每個(gè)進(jìn)程都反復(fù)不停的計(jì)算隨機(jī)數(shù)的平方根

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

在 docker 宿主機(jī)上打開一個(gè) terminal 執(zhí)行 top

然后再啟動一個(gè)容器, --cpu-shares 為 512 。

查看 top 的顯示結(jié)果

可以看到 container1 的 CPU 占比為 1024/(1024+512)=2/3,container2 的 CPU 占比為

512/(1024+512)=1/3

將 container1 的 cpu.shares 改為 512,

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

可以看到兩個(gè)容器的 CPU 占比趨于平均

設(shè)定 CPU 使用周期使用時(shí)間上限

cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 來限制該組中的所有進(jìn)程在單

位時(shí)間里可以使用的 cpu 時(shí)間。cpu.cfs_period_us 就是時(shí)間周期,默認(rèn)為 100000,即百毫

秒。cpu.cfs_quota_us 就是在這期間內(nèi)可使用的 cpu 時(shí)間,默認(rèn) -1,即無限制。

cpu.cfs_period_us:設(shè)定時(shí)間周期(單位為微秒(μs)),必須與 cfs_quota_us 配合使用。

cpu.cfs_quota_us :設(shè)定周期內(nèi)最多可使用的時(shí)間(單位為微秒(μs))。這里的配置指 task

對單個(gè) cpu 的使用上限。

舉個(gè)例子,如果容器進(jìn)程需要每 1 秒使用單個(gè) CPU 的 0.2 秒時(shí)間,可以將 cpu-period 設(shè)置為

1000000(即 1 秒),cpu-quota 設(shè)置為 200000(0.2 秒)。

當(dāng)然,在多核情況下,若 cfs_quota_us 是 cfs_period_us 的兩倍,就表示在兩個(gè)核上

完全使用 CPU,例如如果允許容器進(jìn)程需要完全占用兩個(gè) CPU,則可以將 cpu-period 設(shè)置為

100000(即 0.1 秒),cpu-quota 設(shè)置為 200000(0.2 秒)。

使用示例:

使用命令 docker run 創(chuàng)建容器

在宿主機(jī)上執(zhí)行 top

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

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

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

修改容器的 cpu.cfs_period_us 和 cpu.cfs_quota_us 值

執(zhí)行 top 查看 cpu 資源

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


RT  調(diào)度策略下的配置 實(shí)時(shí)調(diào)度策略與公平調(diào)度策略中的按周期分配時(shí)間的方法類似,也是

在周期內(nèi)分配一個(gè)固定的運(yùn)行時(shí)間。

cpu.rt_period_us :設(shè)定周期時(shí)間。

cpu.rt_runtime_us:設(shè)定周期中的運(yùn)行時(shí)間。


cpuset - CPU 綁定

對多核 CPU 的服務(wù)器,docker 還可以控制容器運(yùn)行限定使用哪些 cpu 內(nèi)核和內(nèi)存節(jié)點(diǎn),即

使用–cpuset-cpus 和–cpuset-mems 參數(shù)。對具有 NUMA 拓?fù)洌ň哂卸?CPU、多內(nèi)存節(jié)點(diǎn))的

服務(wù)器尤其有用,可以對需要高性能計(jì)算的容器進(jìn)行性能最優(yōu)的配置。如果服務(wù)器只有一個(gè)

內(nèi)存節(jié)點(diǎn),則–cpuset-mems 的配置基本上不會有明顯效果

注:

現(xiàn)在的機(jī)器上都是有多個(gè) CPU 和多個(gè)內(nèi)存塊的。以前我們都是將內(nèi)存塊看成是一大塊內(nèi)存,

所有 CPU 到這個(gè)共享內(nèi)存的訪問消息是一樣的。但是隨著處理器的增加,共享內(nèi)存可能會

導(dǎo)致內(nèi)存訪問沖突越來越厲害,且如果內(nèi)存訪問達(dá)到瓶頸的時(shí)候,性能就不能隨之增加。

NUMA(Non-Uniform Memory Access)就是這樣的環(huán)境下引入的一個(gè)模型。比如一臺機(jī)

器是有2個(gè)處理器,有4個(gè)內(nèi)存塊。我們將1個(gè)處理器和兩個(gè)內(nèi)存塊合起來,稱為一個(gè)NUMA


node,這樣這個(gè)機(jī)器就會有兩個(gè) NUMA node。在物理分布上,NUMA node 的處理器和內(nèi)

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

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

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

更快。所以使用 NUMA 的模式如果能盡量保證本 node 內(nèi)的 CPU 只訪問本 node 內(nèi)的內(nèi)存

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

使用示例:

表示創(chuàng)建的容器只能用 0、1、2 這三個(gè)內(nèi)核。最終生成的 cgroup 的 cpu 內(nèi)核配置如下:

cpuset.cpus:在這個(gè)文件中填寫 cgroup 可使用的 CPU 編號,如 0-2,16 代表 0、1、2 和 16

這 4 個(gè) CPU。

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

通過 docker exec <容器 ID> taskset -c -p 1(容器內(nèi)部第一個(gè)進(jìn)程編號一般為 1),可以看到容器

中進(jìn)程與 CPU 內(nèi)核的綁定關(guān)系,可以認(rèn)為達(dá)到了綁定 CPU 內(nèi)核的目的。

總結(jié):

CPU 配額控制參數(shù)的混合使用

當(dāng)上面這些參數(shù)中時(shí),cpu-shares 控制只發(fā)生在容器競爭同一個(gè)內(nèi)核的時(shí)間片時(shí),如果通過

cpuset-cpus 指定容器 A 使用內(nèi)核 0,容器 B 只是用內(nèi)核 1,在主機(jī)上只有這兩個(gè)容器使用對

應(yīng)內(nèi)核的情況,它們各自占用全部的內(nèi)核資源,cpu-shares 沒有明顯效果。

cpu-period、cpu-quota 這兩個(gè)參數(shù)一般聯(lián)合使用,在單核情況或者通過 cpuset-cpus 強(qiáng)制容

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

CPU 資源。

cpuset-cpus、cpuset-mems 只在多核、多內(nèi)存節(jié)點(diǎn)上的服務(wù)器上有效,并且必須與實(shí)際的物

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

在系統(tǒng)具有多個(gè) CPU 內(nèi)核的情況下,需要通過 cpuset-cpus 為容器 CPU 內(nèi)核才能比較方便地進(jìn)行測試。


內(nèi)存配額控制

和 CPU 控制一樣,docker 也提供了若干參數(shù)來控制容器的內(nèi)存使用配額,可以控制容器的

swap 大小、可用內(nèi)存大小等各種內(nèi)存方面的控制。主要有以下參數(shù):

Docker 提供參數(shù)-m, --memory=""限制容器的內(nèi)存使用量,如果不設(shè)置-m,則默認(rèn)容器內(nèi)存

是不設(shè)限的,容器可以使用主機(jī)上的所有空閑內(nèi)存

內(nèi)存配額控制使用示例

設(shè)置容器的內(nèi)存上限,參考命令如下所示

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

默認(rèn)情況下,除了–memory 指定的內(nèi)存大小以外,docker 還為容器分配了同樣大小的 swap

分區(qū),也就是說,上面的命令創(chuàng)建出的容器實(shí)際上最多可以使用 256MB 內(nèi)存,而不是 128MB內(nèi)存。如果需要自定義 swap 分區(qū)大小,則可以通過聯(lián)合使用–memory–swap 參數(shù)來實(shí)現(xiàn)控制。

可以發(fā)現(xiàn),使用 256MB 進(jìn)行壓力測試時(shí),由于超過了內(nèi)存上限(128MB 內(nèi)存+128MB swap),進(jìn)程被 OOM(out of memory)殺死。

使用 250MB 進(jìn)行壓力測試時(shí),進(jìn)程可以正常運(yùn)行。

通過 docker stats 可以查看到容器的內(nèi)存已經(jīng)滿負(fù)載了。

#docker stats test2

對上面的命令創(chuàng)建的容器,可以查看到在 cgroups 的配置文件中,查看到容器的內(nèi)存大小為

128MB (128×1024×1024=134217728B), 內(nèi) 存 和 swap 加 起 來 大 小 為 256MB

(256×1024×1024=268435456B)。

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

134217728

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

268435456

盤 磁盤 IO  配額控制

主要包括以下參數(shù):

--device-read-bps:限制此設(shè)備上的讀速度(bytes per second),單位可以是 kb、mb 或者 gb。

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

--device-write-bps :限制此設(shè)備上的寫速度(bytes per second),單位可以是 kb、mb 或者 gb。

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

--blkio-weight:容器默認(rèn)磁盤 IO 的加權(quán)值,有效值范圍為 10-1000。

--blkio-weight-device: 針對特定設(shè)備的 IO 加權(quán)控制。其格式為 DEVICE_NAME:WEIGHT

磁盤 IO 配額控制示例

blkio-weight

使用下面的命令創(chuàng)建兩個(gè)–blkio-weight 值不同的容器:

在容器中同時(shí)執(zhí)行下面的 dd 命令,進(jìn)行測試

注:oflag=direct 規(guī)避掉文件系統(tǒng)的 cache,把寫請求直接封裝成 io 指令發(fā)到硬盤


二、學(xué)習(xí) Docker 也有一段時(shí)間了,了解了 Docker 的基本實(shí)現(xiàn)原理,也知道了 Docker 的使

用方法,這里對 Docker 的一些典型應(yīng)用場景做一個(gè)總結(jié)

1、配置簡化

這是 Docker 的主要使用場景。將應(yīng)用的所有配置工作寫入 Dockerfile 中,創(chuàng)建好鏡像,

以后就可以無限次使用這個(gè)鏡像進(jìn)行應(yīng)用部署了。這大大簡化了應(yīng)用的部署,不需要為每次

部署都進(jìn)行繁瑣的配置工作,實(shí)現(xiàn)了一次打包,多次部署。這大大加快了應(yīng)用的開發(fā)效率,

使得程序員可以快速搭建起開發(fā)測試環(huán)境,不用關(guān)注繁瑣的配置工作,而是將所有精力都盡

可能用到開發(fā)工作中去。

2、代碼流水線管理

代碼從開發(fā)環(huán)境到測試環(huán)境再到生產(chǎn)環(huán)境,需要經(jīng)過很多次中間環(huán)節(jié),Docker 給應(yīng)用

提供了一個(gè)從開發(fā)到上線均一致的環(huán)境,開發(fā)測試人員均只需關(guān)注應(yīng)用的代碼,使得代碼的

流水線變得非常簡單,這樣應(yīng)用才能持續(xù)集成和發(fā)布。

3、快速部署

在虛擬機(jī)之前,引入新的硬件資源需要消耗幾天的時(shí)間。Docker 的虛擬化技術(shù)將這個(gè)

時(shí)間降到了幾分鐘,Docker 只是創(chuàng)建一個(gè)容器進(jìn)程而無需啟動操作系統(tǒng),這個(gè)過程只需要

秒級的時(shí)間。

4、應(yīng)用隔離

資源隔離對于提供共享 hosting 服務(wù)的公司是個(gè)強(qiáng)需求。 如果使用 VM,雖然隔離性非

常徹底,但部署密度相對較低,會造成成本增加。

Docker 容器充分利用 linux 內(nèi)核的 namespace 提供資源隔離功能。結(jié)合 cgroups,可以方便

的設(shè)置每個(gè)容器的資源配額。既能滿足資源隔離的需求,又能方便的為不同級別的用戶設(shè)置

不同級別的配額限制。

5、服務(wù)器資源整合

正如通過 VM 來整合多個(gè)應(yīng)用,Docker 隔離應(yīng)用的能力使得 Docker 同樣可以整合服務(wù)

器資源。由于沒有額外的操作系統(tǒng)的內(nèi)存占用,以及能在多個(gè)實(shí)例之間共享沒有使用的內(nèi)存,

Docker 可以比 VM 提供更好的服務(wù)器整合解決方案。

通常數(shù)據(jù)中心的資源利用率只有 30%,通過使用 Docker 并進(jìn)行有效的資源分配可以提高資

源的利用率。

6、多版本混合部署

隨著產(chǎn)品的不斷更新?lián)Q代,一臺服務(wù)器上部署多個(gè)應(yīng)用或者同一個(gè)應(yīng)用的多個(gè)版本在企

業(yè)內(nèi)部非常常見。但一臺服務(wù)器上部署同一個(gè)軟件的多個(gè)版本,文件路徑、端口等資源往往

會發(fā)生沖突,造成多個(gè)版本無法共存的問題。

如果用 docker,這個(gè)問題將非常簡單。由于每個(gè)容器都有自己獨(dú)立的文件系統(tǒng),所以根本不

存在文件路徑?jīng)_突的問題; 對于端口沖突問題,只需要在啟動容器時(shí)指定不同的端口映射即

可解決問題。

7、版本升級回滾

一次升級,往往不僅僅是應(yīng)用軟件本身的升級,通過還會包含依賴項(xiàng)的升級。 但新舊

軟件的依賴項(xiàng)很可能是不同的,甚至是有沖突的,所以在傳統(tǒng)的環(huán)境下做回滾一般比較困難。

如果使用 docker,我們只需要每次應(yīng)用軟件升級時(shí)制作一個(gè)新的 docker 鏡像,升級時(shí)先停

掉舊的容器, 然后把新的容器啟動。 需要回滾時(shí),把新的容器停掉,舊的啟動即可完成回

滾,整個(gè)過程各在秒級完成,非常方便。

8、內(nèi)部開發(fā)環(huán)境

在容器技術(shù)出現(xiàn)之前,公司往往是通過為每個(gè)開發(fā)人員提供一臺或者多臺虛擬機(jī)來充當(dāng)

開發(fā)測試環(huán)境。開發(fā)測試環(huán)境一般負(fù)載較低,大量的系統(tǒng)資源都被浪費(fèi)在虛擬機(jī)本身的進(jìn)程

上了。

Docker容器沒有任何CPU和內(nèi)存上的額外開銷,很適合用來提供公司內(nèi)部的開發(fā)測試環(huán)境。

而且由于 Docker 鏡像可以很方便的在公司內(nèi)部共享,這對開發(fā)環(huán)境的規(guī)范性也有極大的幫

助。

9、PaaS

使用 Docker 搭建大規(guī)模集群,提供 PaaS。這一應(yīng)用是最有前景的一個(gè)了,目前已有很

多創(chuàng)業(yè)公司在使用 Docker 做 PaaS 了,例如云雀云平臺。用戶只需提交代碼,所有運(yùn)維工作

均由服務(wù)公司來做。而且對用戶來說,整個(gè)應(yīng)用部署上線是一鍵式的,非常方便。

10、云桌面

在每一個(gè)容器內(nèi)部運(yùn)行一個(gè)圖形化桌面,用戶通過 RDP 或者 VNC 協(xié)議連接到容器。該

方案所提供的虛擬桌面相比于傳統(tǒng)的基于硬件虛擬化的桌面方案更輕量級,運(yùn)行速率大大提

升。不過該方案仍處于實(shí)驗(yàn)階段,不知是否可行??梢詤⒖家幌?Docker-desktop 方案。


謝謝觀看,真心的希望能夠幫到您!

本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號