docker容器的參數如何指定配額
1. 內存
現在讓我看下內存限制。
第一件事需要注意的是,默認一個容器可以使用主機上的所有內存。
如果你想為容器中的所有進程限制內存,使用docker run
命令的 -m
開關即可。你可以使用bytes值定義它的值或是添加后綴(k
,m
或g
)。
1.1 示例:管理一個容器的內存分配
你可以像這樣使用-m
開關:
$ docker run -it --rm -m 128m fedora bash
為了顯示限制的實際情況,我將使用我的stress
鏡像。考慮一下的運行:
$ docker run -it --rm -m 128m stress --vm 1 --vm-bytes 128M --vm-hang 0 stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress
工具將創建一個進程,並嘗試分配128MB內存給它。它工作的很好,但是如果我們使用的比實際分配給容器的更多的內存會發生什么?
$ docker run -it --rm -m 128m stress --vm 1 --vm-bytes 200M --vm-hang 0 stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
它照樣正常工作,是不是很奇怪?是的,我同意。
memory.memsw.limit_in_bytes
值是被設置成我們指定的內存參數的兩倍,當我們啟動一個容器的時候。memory.memsw.limit_in_bytes
參數表達了什么?它是memory和swap的總和。這意味着Docker將分配給容器-m
內存值以及-m
swap值。
當前的Docker接口不允許我們指定(或者是完全禁用它)多少的swap應該被使用,所以我們現在需要一起使用它。
有了以上信息,我們可以再次運行我們的示例。這次我們嘗試分配超過我們分配的兩倍內存。它將使用所有的內存和所有的 swap,然后玩完了。
$ docker run -it --rm -m 128m stress --vm 1 --vm-bytes 260M --vm-hang 0 stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd stress: FAIL: [1] (415) <-- worker 6 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 5s
如果你嘗試再次分配比如 250MB(--vm-bytes 250M),它將會很好的工作。
警告:如果你不通過-m
開關限制內存,swap也被不會被限制。(這在技術上是不正確的; 這有限度, 但是它設置的值在我們當前運行的系統是不可達的。 例如在我的筆記本上 16GB 的內存值是 18446744073709551615,這是 ~18.5 exabytes…)
不限制內存將導致一個容器可以很容易使得整個系統不穩定的問題。因此請記住要一直使用-m
參數。( 或者是使用MemoryLimit
屬性。)
你可以在/sys/fs/cgroup/memory/system.slice/docker-$FULL_CONTAINER_ID.scope/
下面發現關於內存的所有信息,例如:
$ ls /sys/fs/cgroup/memory/system.slice/docker-48db72d492307799d8b3e37a48627af464d19895601f18a82702116b097e8396.scope/ cgroup.clone_children memory.memsw.failcnt cgroup.event_control memory.memsw.limit_in_bytes cgroup.procs memory.memsw.max_usage_in_bytes memory.failcnt memory.memsw.usage_in_bytes memory.force_empty memory.move_charge_at_immigrate memory.kmem.failcnt memory.numa_stat memory.kmem.limit_in_bytes memory.oom_control memory.kmem.max_usage_in_bytes memory.pressure_level memory.kmem.slabinfo memory.soft_limit_in_bytes memory.kmem.tcp.failcnt memory.stat memory.kmem.tcp.limit_in_bytes memory.swappiness memory.kmem.tcp.max_usage_in_bytes memory.usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.use_hierarchy memory.kmem.usage_in_bytes notify_on_release memory.limit_in_bytes tasks memory.max_usage_in_bytes
2、CPU
2.1、Docker能夠指定(通過運行命令的-c
開關)給一個容器的可用的CPU分配值。這是一個相對權重,與實際的處理速度無關。事實上,沒有辦法說一個容器只可以獲得1Ghz CPU。請記住。
每個新的容器默認的將有1024
CPU配額,當我們單獨講它的時候,這個值並不意味着什么。但是如果我們啟動兩個容器並且兩個都將使用 100%CPU,CPU時間將在這兩個容器之間平均分割,因為它們兩個都有同樣的CPU配額(為了簡單起見,假設沒有任何其他進程在運行)。
如果我們設置容器的CPU配額是512
,相對於另外一個容器,它將使用一半的CPU時間。但這不意味着它僅僅能使用一半的CPU時間。如果另外一個容器(1024配額的)是空閑的 - 我們的容器將被允許使用100%CPU。這是需要注意的另外一件事。
限制僅僅當它們應該被執行的時候才會強制執行。CGroups不限制進程預先使用(比如,不允許它們更快地運行即使它們有空余資源)。相反的,它提供了它盡可能提供的以及它僅僅在必需的時候限制(比如,當太多的進程同時大量地使用CPU)。
當然,這很難說清楚(我想說的是這不可能說清楚的)多少資源應該被分配給你的進程。這實際取決於其他進程的行為以及多少配額被分配給它們。
示例:管理一個容器的 CPU 分配
正如我在前面提到的,你可以使用-c
開關來分配給運行在容器中的所有進程的配額值。
因為在我的機器上我有4核,我將使用4壓測:
$ docker run -it --rm stress --cpu 4 stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
如果你想以相同的方式啟動兩個容器,兩個都將使用 50% 左右的 CPU。但是當我們修改其中一個容器的 shares 時,將發生什么?
$ docker run -it --rm -c 512 stress --cpu 4 stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
正如你所看到的,CPU在兩個容器之間是以這樣的方式分割了,第一個容器使用了60%的CPU,另外一個使用了30%左右。這似乎是預期的結果。
注意:丟失的約10%CPU被GNOME、Chrome和我的音樂播放器使用了。
2.2、除了限制 CPU配額,我們可以做更多的事情:我們可以把容器的進程固定到特定的處理器(core)。為了做到這個,我們使用docker run
命令的- -cpuset
開關。
為了允許僅在第一個核上執行:
docker run -it --rm --cpuset=0 stress --cpu 1
為了允許僅在前兩個核上執行:
docker run -it --rm --cpuset=0,1 stress --cpu 2
你當然可以混合使用選項--cpuset
與-c
。
注意:Share enforcement僅僅發生在當進程運行在相同的核上的時候。這意味着如果你把一個容器固定在第一個核,而把另外一個容器固定在另外一個核,兩個都將使用各自核的 100%,即使它們有不同的CPU配額設置(再次聲明,我假設僅僅有兩個容器運行在主機上)。
2.3 變更一個正在運行的容器的配額
有可能改變一個正在運行的容器的配額(當然或是任何其他進程)。你可以直接與cgroups文件系統交互,但是因為我們有systemds,我們可以通過它來為我們管理。
為了這個目的,我們將使用systemctl
命令和set-property
參數。使用docker run
命令新的容器將有一個systemd scope,自動分配到其內的所有進程都將被執行。為了改變容器中所有進程的CPU配額,我們僅僅需要在scope內改變它,像這樣:
$ sudo systemctl set-property docker-4be96b853089bc6044b29cb873cac460b429cfcbdd0e877c0868eb2a901dbf80.scope CPUShares=512
注意:添加--runtime
暫時地改變設置。否則,當主機重啟后,這個設置會被記住。
把默認值從1024
變更到512
。你可以看到下面的結果。這一變化發生在記錄中。請注意CPU使用率。在systemd-cgtop中100%意味着滿額使用了一核,並且這是正確的,因為我綁定了兩個容器在相同的核上。
注意:為了顯示所有的屬性,你可以使用systemctl show docker-4be96b853089bc6044b29cb873cac460b429cfcbdd0e877c0868eb2a901dbf80.scope命令。想要列出所有可用的屬性,請查看man systemd.resource-control。
你可以在/sys/fs/cgroup/cpu/system.slice/
下發現指定容器的關於CPU的所有信息。
需要記住的一些事項:
- CPU配額僅僅是一個數字 - 與CPU速度無關
- 新容器默認有
1024
配額 - 在一台空閑主機上,低配額的容器仍可以使用100%的CPU
- 如果你想,你可以把容器固定到一個指定核
3、 限制磁盤空間
正如我前面提到的,這是艱難的話題,默認你每個容器有10GB的空間,有時候它太大了,有時候不能滿足我們所有的數據放在這里。不幸的是,為此我們什么都不能做。
我們能做的唯一的事情就是改變新容器的默認值,如果你認為一些其他的值(比如 5GB)更適合你的情況,你可以通過指定Docker daemon的 --storage-opt
來實現,像這樣:
docker -d --storage-opt dm.basesize=5G
4、限制寫速率
讓我測試沒有執行限制的速率:
$ docker run -it --rm --name block-device-test fedora bash bash-4.2# time $(dd if=/dev/zero of=testfile0 bs=1000 count=100000 && sync) 100000+0 records in 100000+0 records out 100000000 bytes (100 MB) copied, 0.202718 s, 493 MB/s real 0m3.838s user 0m0.018s sys 0m0.213s
花費了 3.8秒來寫入100MB數據,大概是26MB/s。讓我們嘗試限制一點磁盤的速率。
為了能調整容器可用的帶寬,我們需要明確的知道容器掛載的文件系統在哪里。當你在容器里面執行mount
命令的時候,你可以發現它,發現設備掛載在root文件系統:
$ mount /dev/mapper/docker-253:0-3408580-d2115072c442b0453b3df3b16e8366ac9fd3defd4cecd182317a6f195dab3b88 on / type ext4 (rw,relatime,context="system_u:object_r:svirt_sandbox_file_t:s0:c447,c990",discard,stripe=16,data=ordered) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) tmpfs on /dev type tmpfs (rw,nosuid,context="system_u:object_r:svirt_sandbox_file_t:s0:c447,c990",mode=755) [SNIP]
在我們的示例中是:/dev/mapper/docker-253:0-3408580-d2115072c442b0453b3df3b16e8366ac9fd3defd4cecd182317a6f195dab3b88
你也可以使用nsenter
得到這個值,像這樣:
$ sudo /usr/bin/nsenter --target $(docker inspect -f '{{ .State.Pid }}' $CONTAINER_ID) --mount --uts --ipc --net --pid mount | head -1 | awk '{ print $1 }' /dev/mapper/docker-253:0-3408580-d2115072c442b0453b3df3b16e8366ac9fd3defd4cecd182317a6f195dab3b88
現在我們可以改變BlockIOWriteBandwidth
屬性的值,像這樣:
$ sudo systemctl set-property --runtime docker-d2115072c442b0453b3df3b16e8366ac9fd3defd4cecd182317a6f195dab3b88.scope "BlockIOWriteBandwidth=/dev/mapper/docker-253:0-3408580-d2115072c442b0453b3df3b16e8366ac9fd3defd4cecd182317a6f195dab3b88 10M"
這應該把磁盤的速率限制在10MB/s,讓我們再次運行dd
:
bash-4.2# time $(dd if=/dev/zero of=testfile0 bs=1000 count=100000 && sync) 100000+0 records in 100000+0 records out 100000000 bytes (100 MB) copied, 0.229776 s, 435 MB/s real 0m10.428s user 0m0.012s sys 0m0.276s
可以看到,它花費了10s來把100MB數據寫入磁盤,因此這速率是 10MB/s。