使用 docker 對(duì)容器資源進(jìn)行限制
Intro
在使用 docker 運(yùn)行容器時(shí),一臺(tái)主機(jī)上可能會(huì)運(yùn)行幾百個(gè)容器,這些容器雖然互相隔離,但是底層卻使用著相同的 CPU、內(nèi)存和磁盤(pán)資源。如果不對(duì)容器使用的資源進(jìn)行限制,那么容器之間會(huì)互相影響,小的來(lái)說(shuō)會(huì)導(dǎo)致容器資源使用不公平;大的來(lái)說(shuō),可能會(huì)導(dǎo)致主機(jī)和集群資源耗盡,服務(wù)完全不可用。
docker 作為容器的管理者,自然提供了控制容器資源的功能。正如使用內(nèi)核的 namespace 來(lái)做容器之間的隔離,docker 也是通過(guò)內(nèi)核的 cgroups 來(lái)做容器的資源限制。這篇文章就介紹如何使用 docker 來(lái)限制 CPU、內(nèi)存和 IO,以及對(duì)應(yīng)的 cgroups 文件。
NOTE:如果想要了解 cgroups 的更多信息,可以參考 kernel 文檔 或者其他資源。
我本地測(cè)試的 docker 版本是 17.03.0 社區(qū)版:
? stress docker version
Client:
Version: 17.03.0-ce
API version: 1.26
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Server:
Version: 17.03.0-ce
API version: 1.26 (minimum version 1.12)
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Experimental: false
使用的是 ubuntu 16.04 系統(tǒng),內(nèi)核版本是 4.10.0:
? ~ uname -a
Linux cizixs-ThinkPad-T450 4.10.0-28-generic #32~16.04.2-Ubuntu SMP Thu Jul 20 10:19:48 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
NOTE: 不同版本和系統(tǒng)的功能會(huì)有差異,具體的使用方法和功能解釋請(qǐng)以具體版本的 docker 官方文檔為準(zhǔn)。
我們使用 stress 容器來(lái)產(chǎn)生 CPU、內(nèi)存和 IO 的壓力,具體的使用請(qǐng)參考它的幫助文檔。
1. CPU 資源
主機(jī)上的進(jìn)程會(huì)通過(guò)時(shí)間分片機(jī)制使用 CPU,CPU 的量化單位是頻率,也就是每秒鐘能執(zhí)行的運(yùn)算次數(shù)。為容器限制 CPU 資源并不能改變 CPU 的運(yùn)行頻率,而是改變每個(gè)容器能使用的 CPU 時(shí)間片。理想狀態(tài)下,CPU 應(yīng)該一直處于運(yùn)算狀態(tài)(并且進(jìn)程需要的計(jì)算量不會(huì)超過(guò) CPU 的處理能力)。
docker 限制 CPU Share
docker 允許用戶為每個(gè)容器設(shè)置一個(gè)數(shù)字,代表容器的 CPU share,默認(rèn)情況下每個(gè)容器的 share 是 1024。要注意,這個(gè) share 是相對(duì)的,本身并不能代表任何確定的意義。當(dāng)主機(jī)上有多個(gè)容器運(yùn)行時(shí),每個(gè)容器占用的 CPU 時(shí)間比例為它的 share 在總額中的比例。舉個(gè)例子,如果主機(jī)上有兩個(gè)一直使用 CPU 的容器(為了簡(jiǎn)化理解,不考慮主機(jī)上其他進(jìn)程),其 CPU share 都是 1024,那么兩個(gè)容器 CPU 使用率都是 50%;如果把其中一個(gè)容器的 share 設(shè)置為 512,那么兩者 CPU 的使用率分別為 67% 和 33%;如果刪除 share 為 1024 的容器,剩下來(lái)容器的 CPU 使用率將會(huì)是 100%。
總結(jié)下來(lái),這種情況下,docker 會(huì)根據(jù)主機(jī)上運(yùn)行的容器和進(jìn)程動(dòng)態(tài)調(diào)整每個(gè)容器使用 CPU 的時(shí)間比例。這樣的好處是能保證 CPU 盡可能處于運(yùn)行狀態(tài),充分利用 CPU 資源,而且保證所有容器的相對(duì)公平;缺點(diǎn)是無(wú)法指定容器使用 CPU 的確定值。
docker 為容器設(shè)置 CPU share 的參數(shù)是 -c --cpu-shares,它的值是一個(gè)整數(shù)。
我的機(jī)器是 4 核 CPU,因此使用 stress 啟動(dòng) 4 個(gè)進(jìn)程來(lái)產(chǎn)生計(jì)算壓力:
? stress docker run --rm -it stress --cpu 4
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [7] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [8] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [9] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [10] forked
在另外一個(gè) terminal 使用 htop 查看資源的使用情況:

從上圖中可以看到,CPU 四個(gè)核資源都達(dá)到了 100%。四個(gè) stress 進(jìn)程 CPU 使用率沒(méi)有達(dá)到 100% 是因?yàn)橄到y(tǒng)中還有其他機(jī)器在運(yùn)行。
為了比較,我另外啟動(dòng)一個(gè) share 為 512 的容器:
? stress docker run --rm -it -c 512 stress --cpu 4
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [6] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked
因?yàn)槟J(rèn)情況下,容器的 CPU share 為 1024,所以這兩個(gè)容器的 CPU 使用率應(yīng)該大致為 2:1,下面是啟動(dòng)第二個(gè)容器之后的監(jiān)控截圖:

兩個(gè)容器分別啟動(dòng)了四個(gè) stress 進(jìn)程,第一個(gè)容器 stress 進(jìn)程 CPU 使用率都在 54% 左右,第二個(gè)容器 stress 進(jìn)程 CPU 使用率在 25% 左右,比例關(guān)系大致為 2:1,符合之前的預(yù)期。
限制容器能使用的 CPU 核數(shù)
上面講述的 -c --cpu-shares 參數(shù)只能限制容器使用 CPU 的比例,或者說(shuō)優(yōu)先級(jí),無(wú)法確定地限制容器使用 CPU 的具體核數(shù);從 1.13 版本之后,docker 提供了 --cpus 參數(shù)可以限定容器能使用的 CPU 核數(shù)。這個(gè)功能可以讓我們更精確地設(shè)置容器 CPU 使用量,是一種更容易理解也因此更常用的手段。
--cpus 后面跟著一個(gè)浮點(diǎn)數(shù),代表容器最多使用的核數(shù),可以精確到小數(shù)點(diǎn)二位,也就是說(shuō)容器最小可以使用 0.01 核 CPU。比如,我們可以限制容器只能使用 1.5 核數(shù) CPU:
? ~ docker run --rm -it --cpus 1.5 stress --cpu 3
stress: info: [1] dispatching hogs: 3 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked
在容器里啟動(dòng)三個(gè) stress 來(lái)跑 CPU 壓力,如果不加限制,這個(gè)容器會(huì)導(dǎo)致 CPU 的使用率為 300% 左右(也就是說(shuō)會(huì)占用三個(gè)核的計(jì)算能力)。實(shí)際的監(jiān)控如下圖:

可以看到,每個(gè) stress 進(jìn)程 CPU 使用率大約在 50%,總共的使用率為 150%,符合 1.5 核的設(shè)置。
如果設(shè)置的 --cpus 值大于主機(jī)的 CPU 核數(shù),docker 會(huì)直接報(bào)錯(cuò):
? ~ docker run --rm -it --cpus 8 stress --cpu 3
docker: Error response from daemon: Range of CPUs is from 0.01 to 4.00, as there are only 4 CPUs available.
See 'docker run --help'.
如果多個(gè)容器都設(shè)置了 --cpus ,并且它們之和超過(guò)主機(jī)的 CPU 核數(shù),并不會(huì)導(dǎo)致容器失敗或者退出,這些容器之間會(huì)競(jìng)爭(zhēng)使用 CPU,具體分配的 CPU 數(shù)量取決于主機(jī)運(yùn)行情況和容器的 CPU share 值。也就是說(shuō) --cpus 只能保證在 CPU 資源充足的情況下容器最多能使用的 CPU 數(shù),docker 并不能保證在任何情況下容器都能使用這么多的 CPU(因?yàn)檫@根本是不可能的)。
限制容器運(yùn)行在某些 CPU 核
現(xiàn)在的筆記本和服務(wù)器都會(huì)有多個(gè) CPU,docker 也允許調(diào)度的時(shí)候限定容器運(yùn)行在哪個(gè) CPU 上。比如,我的主機(jī)上有 4 個(gè)核,可以通過(guò) --cpuset 參數(shù)讓容器只運(yùn)行在前兩個(gè)核上:
? ~ docker run --rm -it --cpuset-cpus=0,1 stress --cpu 2
stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [7] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [8] forked
這樣,監(jiān)控中可以看到只有前面兩個(gè)核 CPU 達(dá)到了 100% 使用率。

--cpuset-cpus 參數(shù)可以和 -c --cpu-shares 一起使用,限制容器只能運(yùn)行在某些 CPU 核上,并且配置了使用率。
限制容器運(yùn)行在哪些核上并不是一個(gè)很好的做法,因?yàn)樗枰獙?shí)現(xiàn)知道主機(jī)上有多少 CPU 核,而且非常不靈活。除非有特別的需求,一般并不推薦在生產(chǎn)中這樣使用。
CPU 信息的 cgroup 文件
所有和容器 CPU share 有關(guān)的配置都在 /sys/fs/cgroup/cpu/docker/<docker_id>/ 目錄下面,其中 cpu.shares 保存了 CPU share 的值(其他文件的意義可以查看 cgroups 的官方文檔):
? ~ ls /sys/fs/cgroup/cpu/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/
cgroup.clone_children cpuacct.stat cpuacct.usage_all cpuacct.usage_percpu_sys cpuacct.usage_sys cpu.cfs_period_us cpu.shares notify_on_release
cgroup.procs cpuacct.usage cpuacct.usage_percpu cpuacct.usage_percpu_user cpuacct.usage_user cpu.cfs_quota_us cpu.stat tasks
? ~ cat /sys/fs/cgroup/cpu/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/cpu.shares
1024
和 cpuset(限制 CPU 核)有關(guān)的文件在 /sys/fs/cgroup/cpuset/docker/<docker_id> 目錄下,其中 cpuset.cpus 保存了當(dāng)前容器能使用的 CPU 核:
? ~ ls /sys/fs/cgroup/cpuset/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/
cgroup.clone_children cpuset.cpus cpuset.mem_exclusive cpuset.memory_pressure cpuset.mems notify_on_release
cgroup.procs cpuset.effective_cpus cpuset.mem_hardwall cpuset.memory_spread_page cpuset.sched_load_balance tasks
cpuset.cpu_exclusive cpuset.effective_mems cpuset.memory_migrate cpuset.memory_spread_slab cpuset.sched_relax_domain_level
? ~ cat /sys/fs/cgroup/cpuset/docker/d93c9a660f4a13789d995d56024f160e2267f2dc26ce676daa66ea6435473f6f/cpuset.cpus
0-1
--cpus 限制 CPU 核數(shù)并不像上面兩個(gè)參數(shù)一樣有對(duì)應(yīng)的文件對(duì)應(yīng),它是由 cpu.cfs_period_us和 cpu.cfs_quota_us 兩個(gè)文件控制的。如果容器的 --cpus 設(shè)置為 3,其對(duì)應(yīng)的這兩個(gè)文件值為:
? ~ cat /sys/fs/cgroup/cpu/docker/233a38cc641f2e4a1bec3434d88744517a2214aff9d8297e908fa13b9aa12e02/cpu.cfs_period_us
100000
? ~ cat /sys/fs/cgroup/cpu/docker/233a38cc641f2e4a1bec3434d88744517a2214aff9d8297e908fa13b9aa12e02/cpu.cfs_quota_us
300000
其實(shí)在 1.12 以及之前的版本,都是通過(guò) --cpu-period 和 --cpu-quota 這兩個(gè)參數(shù)控制容器能使用的 CPU 核數(shù)的。前者表示 CPU 的周期數(shù),默認(rèn)是 100000,單位是微秒,也就是 1s,一般不需要修改;后者表示容器的在上述 CPU 周期里能使用的 quota,真正能使用的 CPU 核數(shù)就是 cpu-quota / cpu-period,因此對(duì)于 3 核的容器,對(duì)應(yīng)的 cpu-quota 值為 300000。
2. 內(nèi)存資源
默認(rèn)情況下,docker 并沒(méi)有對(duì)容器內(nèi)存進(jìn)行限制,也就是說(shuō)容器可以使用主機(jī)提供的所有內(nèi)存。這當(dāng)然是非常危險(xiǎn)的事情,如果某個(gè)容器運(yùn)行了惡意的內(nèi)存消耗軟件,或者代碼有內(nèi)存泄露,很可能會(huì)導(dǎo)致主機(jī)內(nèi)存耗盡,因此導(dǎo)致服務(wù)不可用。對(duì)于這種情況,docker 會(huì)設(shè)置 docker daemon 的 OOM(out of memory) 值,使其在內(nèi)存不足的時(shí)候被殺死的優(yōu)先級(jí)降低。另外,就是你可以為每個(gè)容器設(shè)置內(nèi)存使用的上限,一旦超過(guò)這個(gè)上限,容器會(huì)被殺死,而不是耗盡主機(jī)的內(nèi)存。
限制內(nèi)存上限雖然能保護(hù)主機(jī),但是也可能會(huì)傷害到容器里的服務(wù)。如果為服務(wù)設(shè)置的內(nèi)存上限太小,會(huì)導(dǎo)致服務(wù)還在正常工作的時(shí)候就被 OOM 殺死;如果設(shè)置的過(guò)大,會(huì)因?yàn)檎{(diào)度器算法浪費(fèi)內(nèi)存。因此,合理的做法包括:
- 為應(yīng)用做內(nèi)存壓力測(cè)試,理解正常業(yè)務(wù)需求下使用的內(nèi)存情況,然后才能進(jìn)入生產(chǎn)環(huán)境使用
- 一定要限制容器的內(nèi)存使用上限
- 盡量保證主機(jī)的資源充足,一旦通過(guò)監(jiān)控發(fā)現(xiàn)資源不足,就進(jìn)行擴(kuò)容或者對(duì)容器進(jìn)行遷移
- 如果可以(內(nèi)存資源充足的情況),盡量不要使用 swap,swap 的使用會(huì)導(dǎo)致內(nèi)存計(jì)算復(fù)雜,對(duì)調(diào)度器非常不友好
docker 限制容器內(nèi)存使用量
在 docker 啟動(dòng)參數(shù)中,和內(nèi)存限制有關(guān)的包括(參數(shù)的值一般是內(nèi)存大小,也就是一個(gè)正數(shù),后面跟著內(nèi)存單位 b、k、m、g,分別對(duì)應(yīng) bytes、KB、MB、和 GB):
-
-m --memory:容器能使用的最大內(nèi)存大小,最小值為 4m -
--memory-swap:容器能夠使用的 swap 大小 -
--memory-swappiness:默認(rèn)情況下,主機(jī)可以把容器使用的匿名頁(yè)(anonymous page)swap 出來(lái),你可以設(shè)置一個(gè) 0-100 之間的值,代表允許 swap 出來(lái)的比例 -
--memory-reservation:設(shè)置一個(gè)內(nèi)存使用的 soft limit,如果 docker 發(fā)現(xiàn)主機(jī)內(nèi)存不足,會(huì)執(zhí)行 OOM 操作。這個(gè)值必須小于--memory設(shè)置的值 -
--kernel-memory:容器能夠使用的 kernel memory 大小,最小值為 4m。 -
--oom-kill-disable:是否運(yùn)行 OOM 的時(shí)候殺死容器。只有設(shè)置了-m,才可以把這個(gè)選項(xiàng)設(shè)置為 false,否則容器會(huì)耗盡主機(jī)內(nèi)存,而且導(dǎo)致主機(jī)應(yīng)用被殺死
關(guān)于 --memory-swap 的設(shè)置必須解釋一下,--memory-swap 必須在 --memory 也配置的情況下才能有用。
- 如果
--memory-swap的值大于--memory,那么容器能使用的總內(nèi)存(內(nèi)存 + swap)為--memory-swap的值,能使用的 swap 值為--memory-swap減去--memory的值 - 如果
--memory-swap為 0,或者和--memory的值相同,那么容器能使用兩倍于內(nèi)存的 swap 大小,如果--memory對(duì)應(yīng)的值是200M,那么容器可以使用400Mswap - 如果
--memory-swap的值為 -1,那么不限制 swap 的使用,也就是說(shuō)主機(jī)有多少 swap,容器都可以使用
如果限制容器的內(nèi)存使用為 64M,在申請(qǐng) 64M 資源的情況下,容器運(yùn)行正常(如果主機(jī)上內(nèi)存非常緊張,并不一定能保證這一點(diǎn)):
? docker run --rm -it -m 64m stress --vm 1 --vm-bytes 64M --vm-hang 0
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 67108864 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] sleeping forever with allocated memory
.....
而如果申請(qǐng) 100M 內(nèi)存,會(huì)發(fā)現(xiàn)容器里的進(jìn)程被 kill 掉了(worker 7 got signal 9,signal 9 就是 kill 信號(hào))
? docker run --rm -it -m 64m stress --vm 1 --vm-bytes 100M --vm-hang 0
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 104857600 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (415) <-- worker 7 got signal 9
stress: WARN: [1] (417) now reaping child worker processes
stress: FAIL: [1] (421) kill error: No such process
stress: FAIL: [1] (451) failed run completed in 0s
關(guān)于 swap 和 kernel memory 的限制就不在這里過(guò)多解釋了,感興趣的可以查看官方的文檔。
內(nèi)存信息的 cgroups 文件
對(duì)于 docker 來(lái)說(shuō),它的內(nèi)存限制也是存放在 cgroups 文件系統(tǒng)的。對(duì)于某個(gè)容器,你可以在 sys/fs/cgroup/memory/docker/<container_id> 目錄下看到容器內(nèi)存相關(guān)的文件:
? ls /sys/fs/cgroup/memory/docker/b067fa0c58dcdd4fa856177fac0112655b605fcc9a0fe07e36950f0086f62f46
cgroup.clone_children memory.kmem.failcnt memory.kmem.tcp.limit_in_bytes memory.max_usage_in_bytes memory.soft_limit_in_bytes notify_on_release
cgroup.event_control memory.kmem.limit_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.move_charge_at_immigrate memory.stat tasks
cgroup.procs memory.kmem.max_usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.numa_stat memory.swappiness
memory.failcnt memory.kmem.slabinfo memory.kmem.usage_in_bytes memory.oom_control memory.usage_in_bytes
memory.force_empty memory.kmem.tcp.failcnt memory.limit_in_bytes memory.pressure_level memory.use_hierarchy
而上面的內(nèi)存限制對(duì)應(yīng)的文件是 memory.limit_in_bytes:
? cat /sys/fs/cgroup/memory/docker/b067fa0c58dcdd4fa856177fac0112655b605fcc9a0fe07e36950f0086f62f46/memory.limit_in_bytes
67108864
3. IO 資源(磁盤(pán))
對(duì)于磁盤(pán)來(lái)說(shuō),考量的參數(shù)是容量和讀寫(xiě)速度,因此對(duì)容器的磁盤(pán)限制也應(yīng)該從這兩個(gè)維度出發(fā)。目前 docker 支持對(duì)磁盤(pán)的讀寫(xiě)速度進(jìn)行限制,但是并沒(méi)有方法能限制容器能使用的磁盤(pán)容量(一旦磁盤(pán) mount 到容器里,容器就能夠使用磁盤(pán)的所有容量)。
? ~ docker run -it --rm ubuntu:16.04 bash
root@5229f756523c:/# time $(dd if=/dev/zero of=/tmp/test.data bs=10M count=100 && sync)
100+0 records in
100+0 records out
1048576000 bytes (1.0 GB) copied, 3.82859 s, 274 MB/s
real 0m4.124s
user 0m0.000s
sys 0m1.812s
限制磁盤(pán)的權(quán)重
通過(guò) --blkio-weight 參數(shù)可以設(shè)置 block 的權(quán)重,這個(gè)權(quán)重和 --cpu-shares 類(lèi)似,它是一個(gè)相對(duì)值,取值范圍是 10-1000,當(dāng)多個(gè) block 去屑磁盤(pán)的時(shí)候,其讀寫(xiě)速度和權(quán)重成反比。
不過(guò)在我的環(huán)境中,--blkio-weight 參數(shù)雖然設(shè)置了對(duì)應(yīng)的 cgroups 值,但是并沒(méi)有作用,不同 weight 容器的讀寫(xiě)速度還是一樣的。github 上有一個(gè)對(duì)應(yīng)的 issue,但是沒(méi)有詳細(xì)的解答。
--blkio-weight-device 可以設(shè)置某個(gè)設(shè)備的權(quán)重值,測(cè)試下來(lái)雖然兩個(gè)容器同時(shí)讀的速度不同,但是并沒(méi)有按照對(duì)應(yīng)的比例來(lái)限制。
限制磁盤(pán)的讀寫(xiě)速率
除了權(quán)重之外,docker 還允許你直接限制磁盤(pán)的讀寫(xiě)速率,對(duì)應(yīng)的參數(shù)有:
-
--device-read-bps:磁盤(pán)每秒最多可以讀多少比特(bytes) -
--device-write-bps:磁盤(pán)每秒最多可以寫(xiě)多少比特(bytes)
上面兩個(gè)參數(shù)的值都是磁盤(pán)以及對(duì)應(yīng)的速率,格式為 <device-path>:<limit>[unit],device-path表示磁盤(pán)所在的位置,限制 limit 為正整數(shù),單位可以是 kb、mb 和 gb。
比如可以把設(shè)備的度速率限制在 1mb:
$ docker run -it --device /dev/sda:/dev/sda --device-read-bps /dev/sda:1mb ubuntu:16.04 bash
root@6c048edef769:/# cat /sys/fs/cgroup/blkio/blkio.throttle.read_bps_device
8:0 1048576
root@6c048edef769:/# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=5M count=10
10+0 records in
10+0 records out
52428800 bytes (52 MB) copied, 50.0154 s, 1.0 MB/s
從磁盤(pán)中讀取 50m 花費(fèi)了 50s 左右,說(shuō)明磁盤(pán)速率限制起了作用。
另外兩個(gè)參數(shù)可以限制磁盤(pán)讀寫(xiě)頻率(每秒能執(zhí)行多少次讀寫(xiě)操作):
-
--device-read-iops:磁盤(pán)每秒最多可以執(zhí)行多少 IO 讀操作 -
--device-write-iops:磁盤(pán)每秒最多可以執(zhí)行多少 IO 寫(xiě)操作
上面兩個(gè)參數(shù)的值都是磁盤(pán)以及對(duì)應(yīng)的 IO 上限,格式為 <device-path>:<limit>,limit 為正整數(shù),表示磁盤(pán) IO 上限數(shù)。
比如,我們可以讓磁盤(pán)每秒最多讀 100 次:
? ~ docker run -it --device /dev/sda:/dev/sda --device-read-iops /dev/sda:100 ubuntu:16.04 bash
root@2e3026e9ccd2:/# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=1k count=1000
1000+0 records in
1000+0 records out
1024000 bytes (1.0 MB) copied, 9.9159 s, 103 kB/s
從測(cè)試中可以看出,容器設(shè)置了讀操作的 iops 為 100,在容器內(nèi)部從 block 中讀取 1m 數(shù)據(jù)(每次 1k,一共要讀 1000 次),共計(jì)耗時(shí)約 10s,換算起來(lái)就是 100 iops/s,符合預(yù)期結(jié)果。
寫(xiě)操作 bps 和 iops 與讀類(lèi)似,這里就不再重復(fù)了,感興趣的可以自己實(shí)驗(yàn)。
磁盤(pán)信息的 cgroups 文件
容器中磁盤(pán)限制的 cgroups 文件位于 /sys/fs/cgroup/blkio/docker/<docker_id> 目錄:
? ~ ls /sys/fs/cgroup/blkio/docker/1402c1682cba743b4d80f638da3d4272b2ebdb6dc6c2111acfe9c7f7aeb72917/
blkio.io_merged blkio.io_serviced blkio.leaf_weight blkio.throttle.io_serviced blkio.time_recursive tasks
blkio.io_merged_recursive blkio.io_serviced_recursive blkio.leaf_weight_device blkio.throttle.read_bps_device blkio.weight
blkio.io_queued blkio.io_service_time blkio.reset_stats blkio.throttle.read_iops_device blkio.weight_device
blkio.io_queued_recursive blkio.io_service_time_recursive blkio.sectors blkio.throttle.write_bps_device cgroup.clone_children
blkio.io_service_bytes blkio.io_wait_time blkio.sectors_recursive blkio.throttle.write_iops_device cgroup.procs
blkio.io_service_bytes_recursive blkio.io_wait_time_recursive blkio.throttle.io_service_bytes blkio.time notify_on_release
其中 blkio.throttle.read_iops_device 對(duì)應(yīng)了設(shè)備的讀 IOPS,前面一列是設(shè)備的編號(hào),可以通過(guò) cat /proc/partitions 查看設(shè)備和分區(qū)的設(shè)備號(hào);后面是 IOPS 上限值:
? ~ cat /sys/fs/cgroup/blkio/docker/1402c1682cba743b4d80f638da3d4272b2ebdb6dc6c2111acfe9c7f7aeb72917/blkio.throttle.read_iops_device
8:0 100
blkio.throttle.read_bps_device 對(duì)應(yīng)了設(shè)備的讀速率,格式和 IOPS 類(lèi)似,只是第二列的值為 bps:
? ~ cat /sys/fs/cgroup/blkio/docker/9de94493f1ab4437d9c2c42fab818f12c7e82dddc576f356c555a2db7bc61e21/blkio.throttle.read_bps_device
8:0 1048576
總結(jié)
從上面的實(shí)驗(yàn)可以看出來(lái),CPU 和內(nèi)存的資源限制已經(jīng)是比較成熟和易用,能夠滿足大部分用戶的需求。磁盤(pán)限制也是不錯(cuò)的,雖然現(xiàn)在無(wú)法動(dòng)態(tài)地限制容量,但是限制磁盤(pán)讀寫(xiě)速度也能應(yīng)對(duì)很多場(chǎng)景。
至于網(wǎng)絡(luò),docker 現(xiàn)在并沒(méi)有給出網(wǎng)絡(luò)限制的方案,也不會(huì)在可見(jiàn)的未來(lái)做這件事情,因?yàn)槟壳熬W(wǎng)絡(luò)是通過(guò)插件來(lái)實(shí)現(xiàn)的,和容器本身的功能相對(duì)獨(dú)立,不是很容易實(shí)現(xiàn),擴(kuò)展性也很差。docker 社區(qū)已經(jīng)有很多呼聲,也有 issue 是關(guān)于網(wǎng)絡(luò)流量限制的: issue 26767、issue 37、issue 4763。
資源限制一方面可以讓我們?yōu)槿萜鳎☉?yīng)用)設(shè)置合理的 CPU、內(nèi)存等資源,方便管理;另外一方面也能有效地預(yù)防惡意的攻擊和異常,對(duì)容器來(lái)說(shuō)是非常重要的功能。如果你需要在生產(chǎn)環(huán)境使用容器,請(qǐng)務(wù)必要花時(shí)間去做這件事情。
參考資料
- docker docs: Limit a container’s resources
- Resource management in Docker
- Docker資源管理探秘:Docker背后的內(nèi)核Cgroups機(jī)制
Memo
本文轉(zhuǎn)載至 https://cizixs.com/2017/08/04/docker-resources-limit/