啃Docker之Docker資源控制(Cgroup資源設定方法)

2020-09-28 10:00:58

前言

預設情況下容器是沒有資源限制的,因為它本身就是一個程序,當一個容器佔用太多資源的話,會對其他容器產生影響,所以 ,合理應該分配容器資源是作為管理員必須要關注的問題。

一:Cgroup資源分配

1.1:為什麼容器要做資源分配?

  • 虛擬機器器在建立的時候就已經做了資源分配,(虛擬CPU,虛擬記憶體,虛擬磁碟等)

  • 容器是共用核心資源的,所以需要用Cgroup資源分配,參照因素是往年的監控系統中服務的吞吐量,和資源資料,資源耗用情況來分配

1.2:Cgroup概述

  • Docker通過Cgroup來控制容器使用的資源配額,包括CPU,記憶體,磁碟三大方面,基本覆蓋了常見的資源配額和使用量的控制

  • Cgroup是Control Groups的縮寫,是Linux核心提供的一種限制,記錄,隔離行程群組所使用的物理資源(如CPU 記憶體 磁碟IO 等等)的機制,被LXC docker等很多專案用於實現程序資源控制。Cgroup本身是提供將程序進行分組化管理的功能和介面的基礎結構,I/O或記憶體的分配控制等,這些具體的資源管理功能稱為Cgroup子系統,有以下幾個大子系統實現:

    • Blkio:設定限制每個儲存塊裝置的管理(硬碟等)的輸入輸出控制,例如:磁碟 光碟以及usb等等
    • CPU:使用排程程式為cgroup任務提供CPU的存取
    • Cpuacct:產生cgroup任務提供CPU的存取
    • Cpuacct:產生cgroup任務的CPU資源報告
    • Cpuset:如果是多核心的CPU,這個子系統會為cgroup任務分配單獨的CPU和記憶體
    • Devices:允許或拒絕cgroup任務對裝置的存取
    • Freezer:暫停和恢復cgroup任務
    • Memory:設定每個cgroup的記憶體限制以及產生記憶體資源報告
    • Net_cls:標記每個網路包以供cgroup方便使用
    • Ns:名稱空間子系統
    • Perf_event:增加了對每個group的監測跟蹤的能力,可以監測屬於某個特定的group的所有執行緒以及執行在特定CPU上的執行緒

1.2:使用stress壓力測試工具來測試CPU和記憶體使用狀況

1.2.1:stress工具介紹

stress 是Unix類系統下的工作量和壓力測試工具。它將對使用者指定的CPU數量的I/O,記憶體和硬碟的負載並報告它檢測到任何錯誤。它用於自動壓力測試和偵錯系統元件失敗的唯一或更經常負荷時。它可以執行在x86,ppc64的,和PPC 32 GNU / Linux的,Tru64的,SPARC Solaris的,和其他平臺

1.2.2:操作步驟

  • 使用Dockerfile來建立一個基於centos的stress工具映象
[root@localhost ~]# setenforce 0
[root@localhost ~]# iptables -F
[root@localhost ~]# systemctl start docker  '開啟docker功能'
[root@localhost opt]# mkdir /opt/stress  '在/opt目錄下建立一個stress目錄'
[root@localhost stress]# cd /opt/stress/
[root@localhost stress]# vim Dockerfile   '建立stress的映象'
FROM centos:7   '基於Centos:7的映象'
MAINTAINER This is cai  '描述資訊'
RUN yum -y install wget  '安裝wget下載工具'
RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo  '去阿里雲官網設定yum源'
RUN yum -y install stress  ##安裝stress
[root@localhost stress]# docker build -t centos:stress .   '建立映象'
  • 建立容器,–cpu-shares引數值不能保證可以獲得1個vcpu或者多少GHz的CPU資源,它僅是一個彈性的加權值
[root@localhost stress]# docker run -itd --cpu-shares 100 centos:stress
  • 說明:預設情況下,每個Docker容器的CPU份額都是1024,單獨一個容器的份額是沒有意義的。只有在同時執行多個容器時,容器的CPU加權的效果才能體現出來。

  • 例如:如果兩個容器A B的CPU分別為1000和500,在CPU進行時間片分配的時候,容器A比容器B多一倍機會獲得CPU的時間片

  • 但分配的結果取決於當時主機和其他容器的執行狀態,實際上也無法保證容器A一定能獲得CPU時間片。比如容器A的程序一直是空閒的,那麼容器B是可以獲取比容器A更多的CPU時間篇的。極端情況下,例如主機上只執行了一個容器,即使它的CPU份額只有50,它也可以獨佔整個主機的CPU資源

  • Cgroups只在容器分配資源緊缺時,即在需要對容器使用的資源進行限制時,才會生效。因此無法單純根據某個容器的CPU份額來確定有多少CPU資源分配,結果取決於同時執行的其他容器的CPU分配和容器中程序執行情況

  • 可以通過cpu share 可以設定容器使用CPU的優先順序,比如啟動了兩個容器及執行檢視CPU使用百分比

  • 執行容器 容器名字為cpu512 ;–cpu-shares 512權重是512;映象為centos:stress; stress -c 10:分配10個子程序

[root@localhost stress]# docker run -itd --name cpu512 --cpu-shares 512 centos:stress stress -c 10 #容器產生10個子函數程序
[root@localhost stress]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2f1330d9cf4b        centos:stress       "stress -c 10"      22 sec
[root@localhost stress]# docker exec -it 2f1330d9cf4b bash  進入容器使使用者top檢視CPU使用情況
'CPU的佔用率大概在40%左右,跑了10個程序'

在這裡插入圖片描述

  • 這時再開啟一個終端,將權重設定為1024,檢視區別

[

root@localhost ~]# docker run -itd --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10
2ea71fafe6df1295294ccfa895d03973eedb49865a34ac9a0b6592ec021d37bf
[root@localhost ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2ea71fafe6df        centos:stress       "stress -c 10"      8 seconds ago       Up 7 seconds                            cpu1024
2f1330d9cf4b        centos:stress       "stress -c 10"      4 minutes ago       Up 4 minutes                            cpu512
[root@localhost ~]# docker exec -it 2ea71fafe6df bash
[root@2ea71fafe6df /]# top   
'按1檢視幾個CPU的情況'
'發現cpu1024容器的CPU佔用率大概在25%左右'

在這裡插入圖片描述

  • 這時再回到cpu512容器這邊,發現cpu的佔用率為1024的一半 1:2的比例,說明權重設定成功

在這裡插入圖片描述

二:CPU的週期限制

  • Docker提供了 --cpu-period --cpu-quota兩個引數控制容器可以分配到CPU時鐘週期

    • –cpu-period:是用來指定容器對CPU的使用要在多長時間內做一次重新分配
    • –cpu-quota:是用來指定在這個週期內,最多可以有多少時間用來跑這個容器,與–cpu-shares不同的是這種設定是指定一個絕對值,容器對CPU資源的使用絕對不會超過設定的值
  • cpu-period和cpu-quota的單位是微秒。cpu-period的最小值為1000微妙,最大值為1秒,預設值為0.1秒

  • 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秒),也就是說,0.2秒代表要使用2個核心數資源,當把一個0.1秒的核心數用完,還有0.1秒的就需要找另一個核心數去完成

2.1:操作步驟

  • 執行容器,設定cpu為0.1秒重新整理時間,佔用總資源配額的是0.2秒
[root@localhost stress]# docker run -itd  --cpu-period  100000   --cpu-quota 200000 centos:stress
  • 進入容器檢視重新整理週期和建立的配額值
[root@localhost stress]# docker run -tid  --cpu-period  100000   --cpu-quota 200000 centos:stress
8ef8b10290ff87e6f455ab6faed35d6667e8f087ca41f3b019f12cbe95427133
[root@localhost stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                       PORTS               NAMES
8ef8b10290ff        centos:stress       "/bin/bash"         About a minute ago   Up About a minute   
[root@localhost stress]# docker exec -it 8ef8b10290ff bash
[root@8ef8b10290ff /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us
100000				'重新整理週期為0.1秒'
[root@8ef8b10290ff /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
200000				'可以使用兩個CPU資源'

三:CPU Core控制

  • 對多核CPU的伺服器。Docker還可以控制容器執行使用哪些CPU核心,即使用–cpuset-cpus引數

  • 這對具有多CPU的伺服器尤其有用,可以對需要提供效能計算的容器進行效能最優的設定

3.1:實操步驟

  • 執行容器;容器名為cpu1 用到的核心數是0和1
[root@localhost stress]# docker run -itd --name cpu01 --cpuset-cpus 0-1 centos:stress
c07d9999818631cae0d2ee608e9d849316c87842b631ba04f57aae5d3026ef65
[root@localhost stress]# docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
c07d99998186        centos:stress       "/bin/bash"         9 seconds ago       Up 8 seconds                            cpu01
[root@localhost stress]# docker exec -it c07d99998186 bash
[root@c07d99998186 /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-1
  • 通過下面的指令可以檢視容器中程序與CPU核心的繫結關係,達到繫結CPU核心的目的
[root@localhost stress]# docker exec c07d99998186 taskset -c -p 1
pid 1's current affinity list: 0,1		''PID程序繫結到0和1上面'
  • 這時進入容器,讓他跑10個執行緒,檢視cpu0和1是否滿載
[root@localhost stress]# docker exec -it c07d99998186 bash
[root@c07d99998186 /]# stress -c 10				'分配10個子程序'
'發現0和1的核心數滿載,證明是繫結在0和1的核心數上'
[root@c07d99998186 /]# top

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-pxITvBPO-1601040748327)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20200925211551153.png)]

四:CPU配額控制引數的混合使用

  • 通過cpuset-cpus引數指定容器A使用CPU核心0,容器B只是用CPU核心1

  • 在主機上只有這兩個容器使用對應CPU核心的情況,他們各自佔用全部的核心資源,cpu-shares沒有明顯效果。

  • Cpuset-cpus cpuset-mems引數只在多核,多記憶體節點上的伺服器有效,並且必須與實際的物理設定匹配,否則也無法達到資源控制的目的

  • 在系統具有多個CPU核心的情況下,需要通過cpuset-cpus引數為設定容器CPU核心才能方便進行測試

4.1:實操步驟

  • 執行容器,容器名為cpu3;指定執行的核心數是1核心數,指定1個子程序
[root@localhost stress]# docker run -itd --name cpu3 --cpuset-cpus 1 centos:stress stress -c 1
32768c29c1fefff5c41556f6cbbfcc857eaadc177ed2654bec36048371db3b17
[root@localhost stress]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
32768c29c1fe        centos:stress       "stress -c 1"       6 seconds ago       Up 5 seconds                            cpu3
[root@localhost stress]# docker exec -it 32768c29c1fe bash
[root@32768c29c1fe /]# top

在這裡插入圖片描述

五:記憶體限額

  • 與作業系統類似,容器可使用的記憶體包括兩個部分:實體記憶體和Swap

  • Docker通過下面兩組引數來控制容器記憶體的使用量

    • -m或–memory:設定實體記憶體的使用限額,例如100M;1024M
    • –memory-swap:設定 記憶體+swap的使用限額

5.1:實操步驟

  • 執行如下命令允許該容器最多使用200M記憶體和300M的swap

  • –vm1:啟動1個記憶體工作執行緒

  • –vm-bytes 280M:每個執行緒分配280M記憶體(實體記憶體只有200M,所以會去借80M的虛擬記憶體,但是總記憶體數不能超過300M)

  • 映象名為:progrium/stress

[root@localhost ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
  • 如果讓工作執行緒分配的記憶體超過300M,分配的記憶體超過限額,stress執行緒報錯,容器會退出
[root@localhost ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M  '設定的記憶體超過限額300M'
  • 總結:實體記憶體資源小於等於(不能超過)記憶體+swap的使用限額

六:Block IO的限制

  • 預設情況下,所有容器能平等地讀寫磁碟,可以通過設定–blkio-weight引數來改變容器Block IO的優先順序

  • –blkio-weight與–cpu-shares類似,設定的是相對權重值,預設為500

6.1:實操步驟

  • 設定容器A的磁碟讀寫權重為600
[root@localhost ~]# docker run -itd --name conA --blkio-weight 600 centos:stress
'進入容器檢視設定情況'
[root@localhost stress]# docker exec -it 7b9d53a062fb bash
[root@7b9d53a062fb /]# cat /sys/fs/cgroup/blkio/blkio.weight
600				'設定的值為600'
  • 這時在執行一個設定權重值為500,檢視情況
[root@localhost stress]# docker run -itd --name conB --blkio-weight 500 centos:stress
fba42cee52fd4892b731bfefb6aa2d894e18747e72fcd79b50a0ba03c8893832
[root@localhost stress]# docker ps -a | grep conB
fba42cee52fd        centos:stress       "/bin/bash"              12 seconds ago       Up 11 seconds 
[root@localhost stress]# docker exec -it fba42cee52fd bash
[root@fba42cee52fd /]# cat /sys/fs/cgroup/blkio/blkio.weight
500

七: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

7.1:實操步驟

  • 將容器的sda系統磁碟的讀寫速率設定為5MB/s
[root@localhost ~]# docker run -it --device-write-bps /dev/sda:5MB centos:stress
  • 進入容器寫入1GB的檔案,檢視讀寫的速率
[root@455af49f49b7 /]# dd if=/dev/zero of=test bs=1M count=1024 oflag=direct

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-sRaWhrky-1601040748329)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20200925212926581.png)]

  • 這時退出容器,重新建立容器不限速,檢視讀寫速率
[root@455af49f49b7 /]# exit  '退出容器'
exit
[root@localhost ~]# docker run -it centos:stress   '不限速,執行容器'
[root@9e6875fda3e8 /]# dd if=/dev/zero of=test bs=1M count=1024 oflag=direct
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.980528 s, 1.1 GB/s  ##發現不限速的讀寫速率是1.1GB/s