摘要
Docker通過Cgroup來控制容器使用的資源配額,包括CPU、內存、磁盤三大方面,基本覆蓋了常見的資源配額和使用量控制。
一、Cgroup簡介
Cgroup是Control Groups的縮寫,是Linux內核提供的一種可以限制、記錄、隔離進程組所使用的物理資源(如CPU、內存、磁盤IO等待)的機制,被LXC、docker等很多項目用於實現進程資源控制。Cgroup本身是提供將進程進行分組化管理的功能和接口的基礎結構,I/O或內存的分配控制等具體的資源管理是通過該功能來實現的。這些具體的資源管理功能稱為Cgroup子系統,有以下幾大子系統實現:
blkio:設置限制每個塊設備的輸入輸出控制。例如:磁盤,光盤以及usb等等
CPU:使用調度程序為cgroup任務提供CPU的訪問
cpuacct:產生cgroup任務的CPU資源報告
cpuset:如果是多核心的CPU,這個子系統會為cgroup任務分配單獨的CPU和內存
devices:允許或拒絕cgroup任務對設備的訪問
freezer:暫停和恢復cgroup任務
memory:設置每個cgroup的內存限制以及產生內存資源報告
net_cls:標記每個網絡包以供cgroup方便使用
ns:命名空間子系統
perf_event:增加了對每個group的監測跟蹤的能力,可以監測屬於某個特定的group的所有線程以及運行在特定CPU上的線程
下面開始使用stress壓力測試工具來測試CPU和內存使用狀況
二、安裝stress工具
1 創建stress目錄 2 [root@server1 ~]# mkdir /opt/stress 3 [root@server1 ~]# cd /opt/stress/
4
5 編寫Dockerfile文件 6 [root@server1 stress]# vim DockerfileFROM centos:7
7 MAINTAINER xu 8 RUN yum -y install wget
9 RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/rep
10 o/epel-7.repo 11 RUN yum -y install stress 12
13 構建鏡像 14 [root@server1 stress]# docker build -t centos:stress . 15
16 查看鏡像 17 [root@server1 stress]# docker images 18 REPOSITORY TAG IMAGE ID CREATED SIZE 19 centos stress 821b4e4b3972 5 seconds ago 417MB
三、CPU資源分配
默認情況下,每個Docker容器的CPU份額都是1024。單獨一個容器的份額是沒有意義的。只有在同時運行多個容器時,容器的CPU加權的效果才能體現出來。例如,兩個容器A、B的CPU份額分別為1000和500,在CPU進行時間片分配的時候,容器A比容器B多一倍的機會獲得CPU的時間片。但分配的結果取決於當時主機和其他容器的運行狀態,實際上也無法保證容器A一定能獲得CPU時間片。比如容器A的進程一直時空閑的,那么容器B是可以獲取比容器A更多的CPU時間片的,極端情況下,例如主機上只運行了一個容器,即使它的CPU份額只有50,它也可以獨占整個主機的CPU資源
Cgroup只在容器分配的資源緊缺時,即在需要對容器使用的資源進行限制時,才會生效。因此,無法單純根據某個容器的CPU份額來確定有多少CPU資源分配給它,資源分配結果取決於同時運行的其他容器的CPU分配和容器中進程運行情況。
可以通過cpu share可以設置容器使用CPU的優先級,比如,啟動了兩個容器及運行查看CPU使用百分比
3.1、運行兩個容器
1 [root@server1 stress]# docker run -dit --name cpu500 --cpu-shares 500 centos:stress stress -c 10
2 c0083744c59f24ca4ff008b2e5fa25dbd7e3363c7b81362d5f4f87c0ac486c7b 3 [root@server1 stress]# docker run -dit --name cpu1000 --cpu-shares 1000 centos:stress stress -c 10
4 b84cc76469bc7264b26a3c9db846b2e07918a25e425b1bf62bc9688e501dc82f
3.2、查看內存使用情況
1 進入cpu1000容器 2 [root@server1 stress]# docker exec -it b84cc76469bc /bin/bash 3
4 查看cpu使用情況 5 [root@b84cc76469bc /]# top 6
7 進入cpu500容器 8 [root@server1 stress]# docker exec -it c0083744c59f /bin/bash 9
10 查看cpu使用情況 11 [root@c0083744c59f /]# top
容器1000cpu使用情況如下
容器500cpu使用情況如下
對比后可以得出cpu1000的容器cpu使用率大概是另一個的兩倍
四、CPU周期限制
Docker提供了--cpu-period、--cpu-quota兩個參數控制容器可以分配到的CPU時鍾周期。
- --cpu-period是用來指定容器對CPU的使用要在多長時間內做一次重新分配。
- --cpu-quota是用來指定在這個周期內,最多可以有多少時間用來跑這個容器。
- 與--cpu-shares不同的是,這種配置是指定一個絕對值,容器對CPU資源的使用絕對不會超過配置的值
- cpu-period和cpu-quota的單位為微秒(μs)。cpu-period的最小值為1000微秒,最大值為1秒(10^6 μs),默認值為 0.1 秒(100000 μs)。
- cpu-quota的值默認為-1,表示不做控制。cpu-period和cpu-quota參數一般聯合使用
例如:容器進程需要每1秒使用單個CPU的0.2秒時間,可以將cpu-period設置為1000000(即1秒),cpu-quota 設置為 200000(0.2 秒)。當然,在多核情況下,如果允許容器進程完全占用兩個CPU,則可以將cpu-period設置為100000(即0.1秒),cpu-quota設置為200000(0.2秒)。
1 運行容器並設置周期 2 [root@server1 stress]# docker run -dit --cpu-period 100000 --cpu-quota 200000 centos:stress 3 9449396fc3b0b8a99b1fb8fb211998032bc91933df831b5c3341e435a64beda5 4 [root@server1 stress]# docker ps -a 5 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6 9449396fc3b0 centos:stress "/bin/bash" 5 seconds ago Up 4 seconds goofy_mclean 7
8 進入容器 9 [root@server1 stress]# docker exec -it 9449396fc3b0 /bin/bash 10
11 查看周期 12 [root@9449396fc3b0 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 13 100000
14 [root@9449396fc3b0 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 15 200000
五、CPU Core控制
對多核 CPU 的服務器,Docker 還可以控制容器運行使用哪些 CPU 內核,即使用--cpuset-cpus 參數。這對具有多 CPU 的服務器尤其有用,可以對需要高性能計算的容器進行性能最優的配置。
5.1、進行CPU Core控制配置
1 表示創建的容器只能用 0、1兩個內核 2 [root@server1 ~]# docker run -dit --name cpu1 --cpuset-cpus 0-1 centos:stress 3 3266be3c50ebfe8e4e34bc3c202e7ad419314aedccdea1b09c941baf029569d1 4
5 查看容器運行狀態 6 [root@server1 ~]# docker ps -a 7 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8 3266be3c50eb centos:stress "/bin/bash" 6 seconds ago Up 5 seconds cpu1 9
10 進入容器 11 [root@server1 ~]# docker exec -it 3266be3c50eb /bin/bash 12
13 生成 的 cgroup 的 CPU 內核配置如下 14 [root@3266be3c50eb /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus 15 0-1
5.2、CPU內核綁定
通過下面指令可以看到容器中進程與 CPU 內核的綁定關系,達到綁定 CPU 內核的目的
1 [root@server1 ~]# docker exec 3266be3c50eb taskset -c -p 1
2 pid 1's current affinity list: 0,1
5.3、壓力測試
六、CPU配額參數的混合使用
通過 cpuset-cpus 參數指定容器 A 使用 CPU 內核 1,容器 B 只是用 CPU 內核 3。在主機上只有這兩個容器使用對應 CPU 內核的情況,它們各自占用全部的內核資源,cpu-shares 沒有明顯效果。
cpuset-cpus、cpuset-mems 參數只在多核、多內存節點上的服務器上有效,並且必須與實際的物理配置匹配,否則也無法達到資源控制的目的。
在系統具有多個 CPU 內核的情況下,需要通過 cpuset-cpus 參數為設置容器 CPU 內核才能方便地進行測試
1 CPU配置 2 [root@server1 ~]# docker run -dit --name cpu3 --cpuset-cpus 1 --cpu-shares 500 centos:stress stress -c 1
3 275bc9803463abd9098c632d836f605d76bab74467cff2cd57f956762f6fed24 4 [root@server1 ~]# docker run -dit --name cpu4 --cpuset-cpus 3 --cpu-shares 1000 centos:stress stress -c 1
5 be61e954017eb398701c9c17440cdf3c72450e4ebb1b76fb2022afeadee277fd 6
7 查看容器運行情況 8 [root@server1 ~]# docker ps -a 9 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 10 be61e954017e centos:stress "stress -c 1" 48 seconds ago Up 48 seconds cpu4 11 275bc9803463 centos:stress "stress -c 1" About a minute ago Up About a minute cpu3 12
13 分別進入兩個容器 14 [root@server1 ~]# docker exec -it 275bc9803463 /bin/bash 15 [root@server1 ~]# docker exec -it be61e954017e /bin/bash 16
17 分別查看CPU使用情況 18 [root@be61e954017e /]# top #記住按1查看每個核心的占用
cpu3容器cpu使用情況
cpu4容器cpu使用情況
總結:上面的 centos:stress 鏡像安裝了 stress 工具,用來測試 CPU 和內存的負載。通過 在兩個容器上分別執行 stress -c 1 命令,將會給系統一個隨機負載,產生 1 個進程。這 個進程都反復不停的計算由 rand() 產生隨機數的平方根,直到資源耗盡。觀察到宿主機上的 CPU 使用率,第三個內核的使用率接近 100%, 並且一批進程的 CPU 使用率明顯存在 2:1 的使用比例的對比。
七、內存限額
與操作系統類似,容器可使用的內存包括兩部分:物理內存和Swap,Docker通過下面兩組參數來控制容器內存的使用量
- -m或--memory:設置內存的使用限額,例如100M、1024M。
- --memory-swap:設置內存+swap的使用限額
執行如下命令允許該容器最多使用200M的內存和300M的內存+swap
1 內存限額配置 2 [root@server1 ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M 3 stress: dbug: [1] using backoff sleep of 3000us 4 stress: dbug: [1] --> hogvm worker 1 [6] forked 5 stress: dbug: [6] allocating 293601280 bytes ... 6 stress: dbug: [6] touching bytes in strides of 4096 bytes ... 7 會一直運行 8
9 --vm 1:啟動 1 個內存工作線程。 10 --vm-bytes 280M:每個線程分配 280M 內存。 11 默認情況下,容器可以使用主機上的所有空閑內存。 12
13 如果讓工作線程分配的內存超過300M,分配的內存超過限額,stress線程報錯,容器退出 14 [root@server1 ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M 15 stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd 16 stress: dbug: [1] using backoff sleep of 3000us 17 stress: dbug: [1] --> hogvm worker 1 [6] forked 18 stress: dbug: [6] allocating 325058560 bytes ... 19 stress: dbug: [6] touching bytes in strides of 4096 bytes ... 20 stress: FAIL: [1] (416) <-- worker 6 got signal 9
21 stress: WARN: [1] (418) now reaping child worker processes 22 stress: FAIL: [1] (422) kill error: No such process #沒有進程退出 23 stress: FAIL: [1] (452) failed run completed in 1s
八、Block IO的限制
默認情況下,所有容器能平等地讀寫磁盤,可以通過設置--blkio-weight參數來改變容器block IO的優先級。--blkio-weight與--cpu-shares類似,設置的是相對權重值,默認為500。
在下面的例子中,容器A讀寫磁盤的帶寬是容器B的兩倍
1 Block IO 的限制配置 2 [root@server1 ~]# docker run -dit --name container_A --blkio-weight 400 centos:stress 3 6cfcf6a595b729cf77267ad939e70a1e30f973b3197d4aec2903ec3429a78b3b 4 [root@server1 ~]# docker run -dit --name container_B --blkio-weight 200 centos:stress 5 e680d986196c36c4910c7e91fe798c2389e0d298c0c4e5bc7b76d99e288fdf82 6
7 查看容器運行情況 8 [root@server1 ~]# docker ps -a 9 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 10 e680d986196c centos:stress "/bin/bash" 46 seconds ago Up 46 seconds container_B 11 6cfcf6a595b7 centos:stress "/bin/bash" About a minute ago Up About a minute container_A 12
13 進入容器分別查看 14 [root@server1 ~]# docker exec -it 6cfcf6a595b7 /bin/bash 15 [root@6cfcf6a595b7 /]# cat /sys/fs/cgroup/blkio/blkio.weight 16 400
17
18 [root@server1 ~]# docker exec -it e680d986196c /bin/bash 19 [root@e680d986196c /]# cat /sys/fs/cgroup/blkio/blkio.weight 20 200
九、bps和iops的限制
bps是byte per second,每秒讀寫的數據量
iops是io per second,每秒IO的次數
可通過以下參數控制容器的bps和iops:
- --device-read-bps,限制讀某個設備的bps
- --device-write-bps,限制寫某個設備的bps
- --device-read-iops,限制讀某個設備的iops
- --device-write-iops,限制寫某個設備的iops
下面的示例是限制容器寫/dev/sda的速率為5MB/s
1 bps 和 iops 的限制配置 2 [root@server1 ~]# docker run -dit --device-write-bps /dev/sda:5MB centos:stress 3 88fa4e4ca86fa3f17ed829d91a597d7b7f6645ca0c11d23af3441406203291f2 4
5 查看容器運行情況 6 [root@server1 ~]# docker ps -a 7 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8 88fa4e4ca86f centos:stress "/bin/bash" 5 seconds ago Up 4 seconds interesting_mclaren 9
10 進入容器 11 [root@server1 ~]# docker exec -it 88fa4e4ca86f /bin/bash 12
13 進行測試 14 [root@88fa4e4ca86f /]# dd if=/dev/zero of=text bs=1M count=1024 oflag=direct # 15 可以按ctrl+c中斷查看 16 ^C14+0 records in
17 14+0 records out 18 14680064 bytes (15 MB) copied, 2.80506 s, 5.2 MB/s #限速5MB/s
通過dd命令測試在容器中寫磁盤的速度。因為容器的文件系統是在host /dev/sda上的,在容器中寫文件相當於對host /dev/sda進行寫操作。另外,oflag=direct指定用direct IO方式寫文件,這樣 --device-write-bps才能生效
結果表明限速5MB/s左右。作為對比測試,如果不限速,結果如下:
1 運行另一個容器 2 [root@server1 ~]# docker run -it centos:stress 3
4 [root@ffd2062aa3a4 /]# dd if=/dev/zero of=text bs=1M count=1024 oflag=direct 5 1024+0 records in
6 1024+0 records out 7 1073741824 bytes (1.1 GB) copied, 0.465947 s, 2.3 GB/s #不限速,2.3GB/s