03-Docker資源控制

????cgroup是control group的簡(jiǎn)寫,是Linux內(nèi)核提供的一種限制所使用物理資源的機(jī)制,這些資源主要包括CPU、內(nèi)存、blkio。Docker就是采用cgroup來控制容器對(duì)操作系統(tǒng)資源的使用。

一、內(nèi)存限制

1.概述

Docker 提供的內(nèi)存限制功能有以下幾點(diǎn):

? a.容器能使用的內(nèi)存和交換分區(qū)大小。

? b.容器的核心內(nèi)存大小。

? c.容器虛擬內(nèi)存的交換行為。

? d.容器內(nèi)存的軟性限制。

? e.是否殺死占用過多內(nèi)存的容器。

? f.容器被殺死的優(yōu)先級(jí)

一般情況下,達(dá)到內(nèi)存限制的容器過段時(shí)間后就會(huì)被系統(tǒng)殺死。

2. 內(nèi)存限制相關(guān)的參數(shù)

????執(zhí)行docker run命令時(shí)能使用的和內(nèi)存限制相關(guān)的所有選項(xiàng)如下。

3. 用戶內(nèi)存限制

????用戶內(nèi)存限制就是對(duì)容器能使用的內(nèi)存和交換分區(qū)的大小作出限制。使用時(shí)要遵循兩條直觀的規(guī)則:-m,--memory選項(xiàng)的參數(shù)最小為 4 M。--memory-swap不是交換分區(qū),而是內(nèi)存加交換分區(qū)的總大小,所以--memory-swap必須比-m,--memory大。在這兩條規(guī)則下,一般有四種設(shè)置方式。

1)不設(shè)置

????如果不設(shè)置-m,--memory和--memory-swap,容器默認(rèn)可以用完宿主機(jī)的所有內(nèi)存和 swap 分區(qū)。不過注意,如果容器占用宿主機(jī)的所有內(nèi)存和 swap 分區(qū)超過一段時(shí)間后,會(huì)被宿主機(jī)系統(tǒng)殺死(如果沒有設(shè)置--oom-kill-disable=true的話)。

2)設(shè)置-m,--memory,不設(shè)置--memory-swap

????給-m或--memory設(shè)置一個(gè)不小于 4M 的值,假設(shè)為 a,不設(shè)置--memory-swap,或?qū)?-memory-swap設(shè)置為0。這種情況下,容器能使用的內(nèi)存大小為 a,能使用的交換分區(qū)大小也為 a。因?yàn)镈ocker 默認(rèn)容器交換分區(qū)的大小和內(nèi)存相同。

????如果在容器中運(yùn)行一個(gè)一直不停申請(qǐng)內(nèi)存的程序,你會(huì)觀察到該程序最終能占用的內(nèi)存大小為 2a。

????比如$ docker run -m 1G ubuntu:16.04,該容器能使用的內(nèi)存大小為 1G,能使用的 swap 分區(qū)大小也為 1G。容器內(nèi)的進(jìn)程能申請(qǐng)到的總內(nèi)存大小為 2G。

3)設(shè)置-m,--memory=a,--memory-swap=b,且b > a

????給-m設(shè)置一個(gè)參數(shù) a,給--memory-swap設(shè)置一個(gè)參數(shù) b。a 是容器能使用的內(nèi)存大小,b是容器能使用的內(nèi)存大小 + swap分區(qū)大小。所以b必須大于a。b -a 即為容器能使用的 swap 分區(qū)大小。

????比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,該容器能使用的內(nèi)存大小為 1G,能使用的 swap 分區(qū)大小為2G。容器內(nèi)的進(jìn)程能申請(qǐng)到的總內(nèi)存大小為 3G。

4)設(shè)置-m,--memory=a,--memory-swap=-1

????給-m參數(shù)設(shè)置一個(gè)正常值,而給--memory-swap設(shè)置成 -1。這種情況表示限制容器能使用的內(nèi)存大小為 a,而不限制容器能使用的 swap 分區(qū)大小。這時(shí)候,容器內(nèi)進(jìn)程能申請(qǐng)到的內(nèi)存大小為 a + 宿主機(jī)的 swap 大小。

4. Memoryreservation

????Memory reservation 是一種軟性限制,用于節(jié)制容器內(nèi)存使用。給--memory-reservation設(shè)置一個(gè)比-m小的值后,雖然容器最多可以使用-m使用的內(nèi)存大小,但在宿主機(jī)內(nèi)存資源緊張時(shí),在系統(tǒng)的下次內(nèi)存回收時(shí),系統(tǒng)會(huì)回收容器的部分內(nèi)存頁,強(qiáng)迫容器的內(nèi)存占用回到--memory-reservation設(shè)置的值大小。

????沒有設(shè)置時(shí)(默認(rèn)情況下)--memory-reservation的值和-m的限定的值相同。將它設(shè)置為0或者設(shè)置的比-m的參數(shù)大 等同于沒有設(shè)置。

????Memory reservation 是一種軟性機(jī)制,它不保證任何時(shí)刻容器使用的內(nèi)存不會(huì)超過--memory-reservation限定的值,它只是確保容器不會(huì)長(zhǎng)時(shí)間占用超過--memory-reservation限制的內(nèi)存大小。

????例如:$ docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash

????如果容器使用了大于200M但小于500M內(nèi)存時(shí),下次系統(tǒng)的內(nèi)存回收會(huì)嘗試將容器的內(nèi)存鎖緊到200M以下。

5. OOM killer

????默認(rèn)情況下,在出現(xiàn)out-of-memory(OOM)錯(cuò)誤時(shí),系統(tǒng)會(huì)殺死容器內(nèi)的進(jìn)程來獲取更多空閑內(nèi)存。這個(gè)殺死進(jìn)程來節(jié)省內(nèi)存的進(jìn)程,我們姑且叫它 OOM killer。我們可以通過設(shè)置--oom-kill-disable選項(xiàng)來禁止OOM killer 殺死容器內(nèi)進(jìn)程。但請(qǐng)確保只有在使用了-m/--memory選項(xiàng)時(shí)才使用--oom-kill-disable禁用OOM killer。如果沒有設(shè)置-m選項(xiàng),卻禁用了OOM-killer,可能會(huì)造成出現(xiàn)out-of-memory錯(cuò)誤時(shí),系統(tǒng)通過殺死宿主機(jī)進(jìn)程來獲取更多內(nèi)存。

????下面的例子限制了容器的內(nèi)存為 100M 并禁止了 OOM killer,是正確的使用方法:

????$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash

????而下面這個(gè)容器沒設(shè)置內(nèi)存限制,卻禁用了 OOM killer 是非常危險(xiǎn)的:

????$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash

????一般一個(gè)容器只有一個(gè)進(jìn)程,這個(gè)唯一進(jìn)程被殺死,容器也就被殺死了。我們可以通過--oom-score-adj選項(xiàng)來設(shè)置在系統(tǒng)內(nèi)存不夠時(shí),容器被殺死的優(yōu)先級(jí)。負(fù)值更不可能被殺死,而正值更有可能被殺死。

6. 核心內(nèi)存

????核心內(nèi)存和用戶內(nèi)存不同的地方在于核心內(nèi)存不能被交換出。不能交換出去的特性使得容器可以通過消耗太多內(nèi)存來堵塞一些系統(tǒng)服務(wù)。核心內(nèi)存包括:

????a.stack pages(棧頁面)

????b.slab pages

????c.socket memory pressure

? ? d.tcp memory pressure

????可以通過設(shè)置核心內(nèi)存限制來約束這些內(nèi)存。例如,每個(gè)進(jìn)程都要消耗一些棧頁面,通過限制核心內(nèi)存,可以在核心內(nèi)存使用過多時(shí)阻止新進(jìn)程被創(chuàng)建。

????核心內(nèi)存和用戶內(nèi)存并不是獨(dú)立的,必須在用戶內(nèi)存限制的上下文中限制核心內(nèi)存。

????假設(shè)用戶內(nèi)存的限制值為 U,核心內(nèi)存的限制值為 K。有三種可能地限制核心內(nèi)存的方式:

????U != 0,不限制核心內(nèi)存。這是默認(rèn)的標(biāo)準(zhǔn)設(shè)置方式

????K < U,核心內(nèi)存是用戶內(nèi)存的子集。這種設(shè)置在部署時(shí),每個(gè)cgroup 的內(nèi)存總量被過度使用。過度使用核心內(nèi)存限制是絕不推薦的,因?yàn)橄到y(tǒng)還是會(huì)用完不能回收的內(nèi)存。在這種情況下,你可以設(shè)置K,這樣 groups 的總數(shù)就不會(huì)超過總內(nèi)存了。然后,根據(jù)系統(tǒng)服務(wù)的質(zhì)量自由地設(shè)置 U。

????K > U,因?yàn)楹诵膬?nèi)存的變化也會(huì)導(dǎo)致用戶計(jì)數(shù)器的變化,容器核心內(nèi)存和用戶內(nèi)存都會(huì)觸發(fā)回收行為。這種配置可以讓管理員以一種統(tǒng)一的視圖看待內(nèi)存。對(duì)想跟蹤核心內(nèi)存使用情況的用戶也是有用的。

????例如:

????$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash

????容器中的進(jìn)程最多能使用500M內(nèi)存,在這500M中,最多只有50M核心內(nèi)存。

????$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash

????沒有設(shè)置用戶內(nèi)存限制,所以容器中的進(jìn)程可以使用盡可能多的內(nèi)存,但是最多能使用50M核心內(nèi)存。

7. Swappiness

????默認(rèn)情況下,容器的內(nèi)核可以交換出一定比例的匿名頁。--memory-swappiness就是用來設(shè)置這個(gè)比例的。--memory-swappiness可以設(shè)置為從0到100。0表示關(guān)閉匿名頁面交換。100表示所有的匿名頁都可以交換。默認(rèn)情況下,如果不使用--memory-swappiness,則該值從父進(jìn)程繼承而來。

????例如:

????$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash

????將--memory-swappiness設(shè)置為0可以保持容器的工作集,避免交換代理的性能損失。

二、CPU 限制

1. 概述

????Docker 的資源限制和隔離完全基于Linux cgroups。對(duì)CPU資源的限制方式也和cgroups相同。Docker提供的CPU資源限制選項(xiàng)可以在多核系統(tǒng)上限制容器能利用哪些vCPU。而對(duì)容器最多能使用的CPU時(shí)間有兩種限制方式:

????一是有多個(gè)CPU密集型的容器競(jìng)爭(zhēng)CPU時(shí),設(shè)置各個(gè)容器能使用的CPU時(shí)間相對(duì)比例。

????二是以絕對(duì)的方式設(shè)置容器在每個(gè)調(diào)度周期內(nèi)最多能使用的CPU時(shí)間。

2. CPU 限制相關(guān)參數(shù)

????docker run命令和 CPU 限制相關(guān)的所有選項(xiàng)如下:


????其中--cpuset-cpus用于設(shè)置容器可以使用的vCPU核。-c,--cpu-shares用于設(shè)置多個(gè)容器競(jìng)爭(zhēng)CPU時(shí),各個(gè)容器相對(duì)能分配到的CPU時(shí)間比例。--cpu-period和--cpu-quata用于設(shè)置容器能使用的CPU絕對(duì)時(shí)間。

3. 用戶CPU限制

????我們可以設(shè)置容器可以在哪些 CPU 核上運(yùn)行。

????例如:$ docker run -it --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash

????表示容器中的進(jìn)程可以在cpu1和cpu3上執(zhí)行。

????$ docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash

????表示容器中的進(jìn)程可以在 cpu 0、cpu 1 及 cpu 2 上執(zhí)行。

4. CPU 資源的相對(duì)限制

????默認(rèn)情況下,所有的容器得到同等比例的CPU周期。在有多個(gè)容器競(jìng)爭(zhēng)CPU時(shí)我們可以設(shè)置每個(gè)容器能使用的CPU時(shí)間比例。這個(gè)比例叫作共享權(quán)值,通過-c或--cpu-shares設(shè)置。Docker默認(rèn)每個(gè)容器的權(quán)值為1024。不設(shè)置或?qū)⑵湓O(shè)置為0,都將使用這個(gè)默認(rèn)值。系統(tǒng)會(huì)根據(jù)每個(gè)容器的共享權(quán)值和所有容器共享權(quán)值和比例來給容器分配CPU時(shí)間。

????假設(shè)有三個(gè)正在運(yùn)行的容器,這三個(gè)容器中的任務(wù)都是CPU密集型的。第一個(gè)容器的CPU共享權(quán)值是1024,其它兩個(gè)容器的CPU共享權(quán)值是512。第一個(gè)容器將得到50%的CPU時(shí)間,而其它兩個(gè)容器就只能各得到25%的CPU時(shí)間了。如果再添加第四個(gè)CPU共享值為1024 的容器,每個(gè)容器得到的CPU時(shí)間將重新計(jì)算。第一個(gè)容器的CPU時(shí)間變?yōu)?3%,其它容器分得的CPU時(shí)間分別為16.5%、16.5%、33%。

????必須注意的是,這個(gè)比例只有在CPU密集型的任務(wù)執(zhí)行時(shí)才有用。在四核的系統(tǒng)上,假設(shè)有四個(gè)單進(jìn)程的容器,它們都能各自使用一個(gè)核的 100% CPU 時(shí)間,不管它們的CPU共享權(quán)值是多少。

????在多核系統(tǒng)上,CPU時(shí)間權(quán)值是在所有CPU核上計(jì)算的。即使某個(gè)容器的CPU時(shí)間限制少于 100%,它也能使用各個(gè)CPU核的100%時(shí)間。

????例如,假設(shè)有一個(gè)不止三核的系統(tǒng)。用-c=512的選項(xiàng)啟動(dòng)容器{C0},并且該容器只有一個(gè)進(jìn)程,用-c=1024的啟動(dòng)選項(xiàng)為啟動(dòng)容器C2,并且該容器有兩個(gè)進(jìn)程。CPU 權(quán)值的分布可能是這樣的:

PID??? container???CPU CPU share

100??? {C0}????0?? 100% of CPU0

101??? {C1}????1?? 100% of CPU1

102??? {C1}????2?? 100% of CPU2

5. CPU 資源的絕對(duì)限制

????Linux通過CFS(Completely Fair Scheduler,完全公平調(diào)度器)來調(diào)度各個(gè)進(jìn)程對(duì)CPU的使用。CFS 默認(rèn)的調(diào)度周期是100ms。

????我們可以設(shè)置每個(gè)容器進(jìn)程的調(diào)度周期,以及在這個(gè)周期內(nèi)各個(gè)容器最多能使用多少CPU時(shí)間。使用--CPU-period即可設(shè)置調(diào)度周期,使用--CPU-quota即可設(shè)置在每個(gè)周期內(nèi)容器能使用的CPU時(shí)間。兩者一般配合使用。

????例如:

????$ docker run -it --CPU-period=50000 --CPU-quota=25000 ubuntu:16.04 /bin/bash

????將 CFS 調(diào)度的周期設(shè)為50000,將容器在每個(gè)周期內(nèi)的 CPU 配額設(shè)置為 25000,表示該容器每 50ms 可以得到 50% 的 CPU 運(yùn)行時(shí)間。

????$ docker run -it --CPU-period=10000 --CPU-quota=20000 ubuntu:16.04 /bin/bash

????將容器的 CPU 配額設(shè)置為 CFS 周期的兩倍,CPU 使用時(shí)間怎么會(huì)比周期大呢?其實(shí)很好解釋,給容器分配兩個(gè) vCPU 就可以了。該配置表示容器可以在每個(gè)周期內(nèi)使用兩個(gè) vCPU 的 100% 時(shí)間。

????CFS 周期的有效范圍是 1ms~1s,對(duì)應(yīng)的--CPU-period的數(shù)值范圍是 1000~1000000。而容器的 CPU 配額必須不小于 1ms,即--CPU-quota的值必須 >= 1000??梢钥闯鲞@兩個(gè)選項(xiàng)的單位都是 us。

三、磁盤IO限制

1. 概述

????相對(duì)于CPU和內(nèi)存的配額控制,docker對(duì)磁盤IO的控制相對(duì)不成熟,大多數(shù)都必須在有宿主機(jī)設(shè)備的情況下使用。

2. IO限制相關(guān)參數(shù)

????執(zhí)行docker run命令時(shí)能使用的和IO限制相關(guān)的所有選項(xiàng)如下。

3. 磁盤IO配額控制

1)blkio-weight

????要使–blkio-weight生效,需要保證IO的調(diào)度算法為CFQ。可以使用下面的方式查看:

????root@ubuntu:~# cat /sys/block/sda/queue/scheduler

????noop [deadline] cfq

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

????docker run -ti –rm –blkio-weight 100 ubuntu:stress

????docker run -ti –rm –blkio-weight 1000 ubuntu:stress

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

????time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct

2)device-write-bps

????使用下面的命令創(chuàng)建容器,并執(zhí)行命令驗(yàn)證寫速度的限制。

????docker run -tid –name disk1 –device-write-bps /dev/sda:1mb ubuntu:stress

????通過dd來驗(yàn)證寫速度,輸出如下圖示:

????可以看到容器的寫磁盤速度被成功地限制到了1MB/s。device-read-bps等其他磁盤IO限制參數(shù)可以使用類似的方式進(jìn)行驗(yàn)證。

4. 容器空間大小限制

????在docker使用devicemapper作為存儲(chǔ)驅(qū)動(dòng)時(shí),默認(rèn)每個(gè)容器和鏡像的最大大小為10G。如果需要調(diào)整,可以在daemon啟動(dòng)參數(shù)中,使用dm.basesize來指定,但需要注意的是,修改這個(gè)值,不僅僅需要重啟docker daemon服務(wù),還會(huì)導(dǎo)致宿主機(jī)上的所有本地鏡像和容器都被清理掉。

????使用aufs或者overlay等其他存儲(chǔ)驅(qū)動(dòng)時(shí),沒有這個(gè)限制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容