Docker----Cgroup原理描述及應用範例操練

2020-09-27 08:00:56


前言

CGroup 是 Control Groups 的縮寫,是 Linux 核心提供的一種可以限制、記錄、隔離行程群組 (process
groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24
核心,CGroups 不是全新創造的,它將程序管理從 cpuset 中剝離出來,作者是 Google 的 Paul
Menage。CGroups 也是 LXC 為實現虛擬化所使用的資源管理手段。

一:CGroup描述

1.1:CGroup 支援的檔案種類

檔名R/W用途
Release_agentRW刪除分組時執行的命令,這個檔案只存在於根分組
Notify_on_releaseRW設定是否執行 release_agent。為 1 時執行
TasksRW屬於分組的執行緒 TID 列表
Cgroup.procsR屬於分組的程序 PID 列表。僅包括多執行緒程序的執行緒 leader 的 TID,這點與 tasks 不同
Cgroup.event_controlRW監視狀態變化和分組刪除事件的組態檔

12:CGroup 層級圖

mark

如圖所示的 CGroup 層級關係顯示,CPU 和 Memory 兩個子系統有自己獨立的層級系統,而又通過 Task Group 取得關聯關係。

1.3:CGroup 特點

在 cgroups 中,任務就是系統的一個程序。

控制族群(control group)。控制族群就是一組按照某種標準劃分的程序。Cgroups
中的資源控制都是以控制族群為單位實現。一個程序可以加入到某個控制族群,也從一個行程群組遷移到另一個控制族群。一個行程群組的程序可以使用
cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設定的限制。

層級(hierarchy)。控制族群可以組織成 hierarchical
的形式,既一顆控制族群樹。控制族群樹上的子節點控制族群是父節點控制族群的孩子,繼承父控制族群的特定的屬性。

子系統(subsytem)。一個子系統就是一個資源控制器,比如 cpu 子系統就是控制 cpu
時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以後,這個層級上的所有控制族群都受到這個子系統的控制。

1.4:子系統的介紹

  • 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上的執行緒。:

1.5:CGroup 典型應用架構圖

mark

二:使用stress工具壓測CPU和記憶體

  • 使用Dockerfile來建立一個基於Centos的stress工具映象
[root@localhost ~]# iptables -F
[root@localhost ~]# setenforce 0
[root@localhost ~]# mkdir /opt/stress

[root@localhost ~]# vim /opt/stress/Dockerfile

FROM centos:7
MAINTAINER shuai "shuai.@tom.com"
RUN yum install -y wget
RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum install -y stress
  • 新建映象
[root@localhost ~]# cd /opt/stress/

[root@localhost stress]# docker build -t centos:stress .
  • 檢視映象
[root@localhost stress]# docker images
[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              stress              0cc400af48ae        23 hours ago        426MB
centos              7                   7e6257c9f8d8        6 weeks ago         203MB
  • 使用如下命令建立容器

命令中的–cpu–shares引數值不能保證可以獲得個vcpu或者多少GHz的CPU資源,它僅是一個彈性的加權值

[root@localhost stress]# docker run -itd --cpu-shares 100 centos:stress

[root@localhost stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
84fff47241b6        centos:stress       "/bin/bash"         38 seconds ago      Up 37 seconds                           compassionate_brahmagupta

預設情況下,Docker容器的CPU份額都是1024.單獨一個容器的份額是沒有意義的,只有在同時執行多個容器時,容器的CPU加權才能能體現出來。

例如:兩個容器A、B的CPU權重份額分別是1000和500,在CPU進行時間分片分配的時候,容器A比B多一倍的機會獲得CPU的時間片.

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

比如,啟動了兩個容器及執行檢視 CPU 使用百分比

[root@localhost stress]# docker run -itd --name cpu512 --cpu-shares 512 centos:stress stress -c 10      '//容器產生的十個函數子程序'

#進入容器使用top檢視CPU使用情況
[root@localhost stress]# docker exec -it 23bbe8182b5c bash

如圖所示
mark

2.再開一個容器做比較

[root@localhost ~]# docker run -tid --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10

[root@localhost ~]# docker ps -a
[root@localhost ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
605fc2d70557        centos:stress       "stress -c 10"      3 seconds ago       Up 2 seconds                            cpu1024
23bbe8182b5c        centos:stress       "stress -c 10"      5 minutes ago       Up 5 minutes                            cpu512

#進入容器檢視top    1檢視核心的負載
[root@localhost ~]# docker exec -it 605fc2d70557 bash   #入容器使用top對比兩個容器的%CPU,比例是1:2
[root@605fc2d70557 /]# top   '//檢視程序'

如圖
mark

三: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 引數一般聯合使用。

例如:

容器程序需要每一秒使用單個 CPU 的0.2秒時間,可以將 cpu-period 設定為 1000000(即1秒),cpu-quota
設定為 200000(0.2秒),當然,在多核情況下,如果允許容器程序完全佔有兩個 CPU,則可以將 cpu-period 設定為
100000(即0.1秒),cpu-quota 設定為 200000(0.2秒)。

[root@localhost stress]# docker run -itd --name cpushuai --cpu-period 100000 --cpu-quota 200000 centos:stress


[root@localhost stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                            PORTS               NAMES
5faae11dd9d1        centos:stress       "/bin/bash"         5 seconds ago       Up 4 seconds                                          cpushuai
605fc2d70557        centos:stress       "stress -c 10"      51 minutes ago      Exited (137) About a minute ago                       cpu1024
23bbe8182b5c        centos:stress       "stress -c 10"      56 minutes ago      Exited (137) About a minute ago     cpu512

#進入容器
[root@localhost stress]# docker exec -it 5faae11dd9d1 bash

[root@5faae11dd9d1 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 
100000
[root@5faae11dd9d1 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us  
200000

#檢視CPU
[root@5faae11dd9d1 cpu]# top

在這裡插入圖片描述

四:CPU Core 控制:

對於多核 CPU 的伺服器,Docker 還可以控制容器執行使用哪些 CPU 核心,即使用 --cpuset-cpus 引數。這對具有多 CPU 的伺服器尤其有用,可以對需要高效能運算的容器進行效能最優設定。

#建立容器
[root@localhost stress]# docker run -itd --name cpu02 --cpuset-cpus=0-2 centos:stress
//執行該命令(需要宿主機為四核),表示建立的容器只能使用012 三個核心。 不會影響其他核心來搶佔資源

#檢視容器列表
[root@localhost stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
8b65ddbb9c85        centos:stress       "/bin/bash"         52 seconds ago      Up 51 seconds                                     cpu02

#進入容器
[root@localhost stress]# docker exec -it 8b65ddbb9c85 bash

#檢視核心
[root@8b65ddbb9c85 /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-2

如圖
mark

  • 通過下面指令可以看到容器中程序與CPU核心的繫結關係,達到繫結CPU核心的目的
#容器內部的第一個程序號pid為1被繫結到指定CPU執行
[root@localhost stress]# docker exec 8b65ddbb9c85 taskset -c -p 1
pid 1's current affinity list: 0-2

如圖
mark

  • 我們用stress進行壓測
[root@localhost stress]# docker exec -it 8b65ddbb9c85 bash
[root@8b65ddbb9c85 /]# stress -c 10
stress: info: [47] dispatching hogs: 10 cpu, 0 io, 0 vm, 0 hdd
  • 另外開一個終端檢視top
    mark

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

通過 cpuset-cpus 引數指定容器 A 使用 CPU 核心 0,容器B 只是用 CPU 核心1;在主機上只有這兩個容器使用對應
CPU 核心的情況,它們各自佔有全部的核心資源,cpu-shares 沒有明顯效果。cpuset-cpus、cpuset-mems
引數只在多核、多記憶體節點上的伺服器上有效,並且必須與實際的物理設定匹配,否則也無法達到資源控制的目的。在系統具有多個 CPU
核心的情況下,需要通過 cpuset-cpus 引數為設定容器 CPU 核心才能方便地進行測試。

#新建容器cpu3 只使用cpu 1 權重為512
[root@localhost stress]# docker run -itd --name cpu3 --cpuset-cpus 1 --cpu-shares 512 centos:stress stress -c 1
25cffab0395c791d7467ac20433bb6c75b6e56ca6bf1df4c3e943e4eb3d63ef5

#檢視容器列表
[root@localhost stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
25cffab0395c        centos:stress       "stress -c 1"       7 seconds ago       Up 6 seconds                                      cpu3

#進入容器
[root@localhost stress]# docker exec -it 25cffab0395c bash
[root@25cffab0395c /]# top        '//檢視程序 按1可以看到每個核心的佔用情況'

如圖
mark

我們在建立一個容器做比較

#這次的權重為1024
[root@localhost ~]# docker run -itd --name cpu4 --cpuset-cpus 3 --cpu-shares 1024 centos:stress stress -c 1

[root@localhost ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
2259964f13bb        centos:stress       "stress -c 1"       6 seconds ago       Up 5 seconds                                      cpu4
25cffab0395c        centos:stress       "stress -c 1"       9 minutes ago       Up 9 minutes     

#進入容器檢視程序
[root@localhost ~]# docker exec -it 2259964f13bb bash
[root@2259964f13bb /]# top

如圖
mark

六:記憶體限額:

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

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

-m 或 --memory:設定記憶體的使用限額,例如 100M、1024M; –memory-swap:設定記憶體 +swap 的使用限額。 例如:執行如下命令允許該容器最多使用 200M的記憶體,300M 的swap:

[root@localhost stress]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
Unable to find image 'progrium/stress:latest' locally

# --vm 1:啟動1個記憶體工作執行緒;
   --vm-bytes 280M :每個執行緒分配280M記憶體;

如果讓工作執行緒分配的記憶體超過 300M,分配的記憶體超過限額,stress執行緒報錯,容器退出:

[root@localhost stress]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M

如圖
mark

七:Block IO 的限制:

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

–blkio-weight 與 --cpu-shares 類似,設定的是相對權重值,預設為500 在下面的例子中,容器 A
讀寫磁碟的頻寬是容器 B 的兩倍:

[root@localhost stress]# docker run -it --name container_A --blkio-weight 600 centos:stress
[root@9cae2ee0165a /]#  cat /sys/fs/cgroup/blkio/blkio.weight
600

[root@localhost stress]# docker run -it --name container_B --blkio-weight 300 centos:stress
[root@6ed938c0086b /]# cat /sys/fs/cgroup/blkio/blkio.weight
300

八:bps 和 iops 的限制:

(1)bps :是 byte per second,每秒讀寫的資料量;

(2)iops :是 io per second,每秒 IO 的次數;

(3)可以通過以下的引數來控制 bps 和 iops:

–device-read-bps:限制讀某個裝置的 bps; device-write-bps:限制寫某個裝置的 bps;
device-read-iops:限制讀某個裝置的 iops; device-write-iops:限制寫某個裝置的 iops。 例如:

  • 限制容器 寫 /dev/sda 磁碟的速率為 5MB/s:
[root@localhost stress]# docker run -it --device-write-bps /dev/sda:5MB centos:stress 

#通過dd命令測試在容器中寫磁碟的速度。因為容器的檔案系統在host/dev/sda上的 在容器中寫檔案相當於對host/dev/sda進行寫操作。另外,oflag=direct=direct指定用direct IO方式寫檔案

[root@11993eca702e /]# dd if=/dev/zero of=test bs=1M count=1024 oflag=direct
                                     #ctrl+c中斷
                                   
167+0 records out
175112192 bytes (175 MB) copied, 33.4011 s, 5.2 MB/s

[root@11993eca702e /]# exit
exit
#結果表明限速為5M/s左右。

#為了對比,我們不限速測試,如下
[root@localhost stress]# docker run -it centos:stress
[root@7e98a3cbc23e /]# 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, 1.01742 s, 1.1 GB/s

如圖
mark
是不是每天更加努力一點呢?