Cgroup实现docker的资源控制

cgroup 相关概念解释

Cgroups提供了以下功能:

限制进程组可以使用的资源(Resource limiting ):比如memory子系统可以为进程组设定一个memory使用上限,进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)
进程组的优先级控制(Prioritization ):比如可以使用cpu子系统为某个进程组分配cpu share
 记录进程组使用的资源量(Accounting ):比如使用cpuacct子系统记录某个进程组使用的cpu时间
进程组隔离(Isolation):比如使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间
进程组控制(Control):比如使用freezer子系统可以将进程组挂起和恢复

CGroup的子系统

blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
cpu -- 这个子系统使用调度程序提供对 CPU  cgroup 任务访问。
cpuacct -- 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
cpuset -- 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
devices -- 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
freezer -- 这个子系统挂起或者恢复 cgroup 中的任务。
memory -- 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。
net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
ns -- 名称空间子系统

CGroup的术语

task:任务就是系统的一个进程
control group:控制族群就是按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制;
hierarchy:控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性;
subsystem:一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

相互关系

每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的 cgroup 都是此 cgroup 的后代)的初始成员;
一个子系统最多只能附加到一个层级;
一个层级可以附加多个子系统;
一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级;
系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。

如图所示,CPU 和 Memory 两个子系统有自己独立的层级系统,而又通过 Task Group 取得关联关系
在这里插入图片描述

top 按1出现4个核心数
在这里插入图片描述

[root@server1 ~]# mkdir /opt/stress
[root@server1 ~]# cd /opt/
[root@server1 opt]# ls
containerd  rh  stress
[root@server1 opt]# cd stress
[root@server1 stress]# vim Dockerfile
FROM centos:7
MAINTAINER chen "chen@kgc.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@server1 stress]# docker build -t centos:stress .

[root@server1 stress]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              stress              e3a7d1432e7b        28 seconds ago      417MB
centos              7                   8652b9f0cb4c        2 weeks ago         204MB

资源分配是在启动容器的时候进行
使用如下命令创建容器,命令中的--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资源。
cpu的资源是以时间按时间为计量单位的,CPU频率越高,运数的数据越快,同时被执行的任务就多。散热量就大。

Cgroups 只在容器分配的资源紧缺时,即在需要对容器使用的资源进行限制时,才会生效。因此,无法单纯根据某个容器的CPU份额来确定有多少CPU资源分配给它,
资源分配 结果取决于同时运行的其他容器的CPU分配和容器中进程运行情况。
可以通过 cpu share 可以设置容器使用 CPU 的优先级,比如启动了两个容器及运行查看 CPU 使用百分比。

[root@server1 stress]# docker run -itd --name cpu1 --cpu-shares 512 centos:stress stress -c 10
346a57f04d41145c385df26f0477d1ac67ffb738615de52642904e75e1c462e5
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
346a57f04d41        centos:stress       "stress -c 10"      16 seconds ago      Up 15 seconds                           cpu1
[root@server1 stress]# docker exec -it 346a57f04d41 /bin/bash
[root@346a57f04d41 /]# top

在这里插入图片描述
此时看不出什么效果,因为只有一个容器在运行,一个容器给予分配配额是无意义的,这样,我们再开启一个容器进行测试

[root@server1 ~]# docker run -itd --name cpu2 --cpu-shares 1024 centos:stress stress -c 10
[root@server1 ~]# docker ps -a
[root@server1 ~]# docker exec -it 231ea61837b6 /bin/bash
[root@231ea61837b6 /]# top

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
231ea61837b6        centos:stress       "stress -c 10"      2 minutes ago       Up 2 minutes                            cpu2
346a57f04d41        centos:stress       "stress -c 10"      8 minutes ago       Up 8 minutes   

在这里插入图片描述
在这里插入图片描述
使用top查看对比两个容器的%cpu,比例是1:2

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 设置为 400000(0.4 秒)。

[root@server1 stress]# docker run -itd --cpu-period 100000 --cpu-quota 400000 centos:stress 
54c7042da6bb1967306bc0d3303ffae605f14f385b9b170114654022dcb10e2a
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
54c7042da6bb        centos:stress       "/bin/bash"         11 seconds ago      Up 11 seconds                           practical_vaughan
[root@server1 stress]# docker exec -it 54c7042da6bb /bin/bash
[root@54c7042da6bb /]# cat /sys/fs/cgroup/cpu 
cpu/         cpu,cpuacct/ cpuacct/     cpuset/      
[root@54c7042da6bb /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 
100000
[root@54c7042da6bb /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 
400000

CPU Core (容器资源分配)控制对多核 CPU 的服务器,Docker 还可以控制容器运行使用哪些 CPU 内核,即使用–cpuset-cpus 参数。
这对具有多 CPU 的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置。

[root@server1 stress]# docker run -itd --name cpu03 --cpuset-cpus 0-1 centos:stress 
2e155af78ab6f120a6f5eeb04caffa736afd61b894608d1f3c1863452d233cd7
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2e155af78ab6        centos:stress       "/bin/bash"         4 seconds ago       Up 4 seconds                            cpu03
[root@server1 stress]# docker exec -it 2e155af78ab6 /bin/bash
top 1//发现四个核心数没发生任何变化

在这里插入图片描述

[root@2e155af78ab6 /]# stress -c 10  //做压测
stress: info: [27] dispatching hogs: 10 cpu, 0 io, 0 vm, 0 hdd
附加:ctrl+c结束测试
[root@server1 stress]# docker run -itd --name cpu04 --cpuset-cpus 2-3 centos:stress 
61277e77e6d52336784f929d598948f2576f355df5f1ab5352e05f41fb4fcc02
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
61277e77e6d5        centos:stress       "/bin/bash"         9 seconds ago       Up 9 seconds                            cpu04
2e155af78ab6        centos:stress       "/bin/bash"         6 minutes ago       Up 6 minutes                            cpu03
[root@server1 stress]# docker exec -it 61277e77e6d5 /bin/bash
[root@61277e77e6d5 /]# stress -c 10
stress: info: [27] dispatching hogs: 10 cpu, 0 io, 0 vm, 0 hdd

在这里插入图片描述
发现只有0,1核心数发生变化

[root@server1 stress]# docker run -itd --name cpu04 --cpuset-cpus 2-3 centos:stress 
61277e77e6d52336784f929d598948f2576f355df5f1ab5352e05f41fb4fcc02
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
61277e77e6d5        centos:stress       "/bin/bash"         9 seconds ago       Up 9 seconds                            cpu04
2e155af78ab6        centos:stress       "/bin/bash"         6 minutes ago       Up 6 minutes                            cpu03
[root@server1 stress]# docker exec -it 61277e77e6d5 /bin/bash
[root@61277e77e6d5 /]# stress -c 10
stress: info: [27] dispatching hogs: 10 cpu, 0 io, 0 vm, 0 hdd

在这里插入图片描述
发现只有2,3核心数发生变化
[root@61277e77e6d5 /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus
2-3

通过下面指令可以看到容器中进程与 CPU 内核的绑定关系,达到绑定 CPU 内核的目的。
[root@server1 stress]# docker exec 61277e77e6d5 taskset -c -p 1
  //容器内部第一个进程号pid为1被绑定到指定CPU上运行
pid 1's current affinity list: 2,3
[root@server1 stress]# docker exec 2e155af78ab6  taskset -c -p 1 //查询使用哪个核心
pid 1's current affinity list: 0,1 

CPU 配额控制参数的混合使用

通过 cpuset-cpus 参数指定容器 A 使用 CPU 内核 0,容器 B 只是用 CPU 内核 1。
在主机上只有这两个容器使用对应 CPU 内核的情况,它们各自占用全部的内核资源,cpu-shares 没有明显效果。
cpuset-cpus、cpuset-mems 参数只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
在系统具有多个 CPU 内核的情况下,需要通过 cpuset-cpus 参数为设置容器 CPU 内核才能方便地进行测试。

[root@server1 stress]# docker run -tid --name cpu3 --cpuset-cpus 1 --cpu-shares 512 centos:stress stress -c 1
d7f14bbbde81697c837e108fac8832735f0012794332a9605aae0f24a22b5ad0
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
d7f14bbbde81        centos:stress       "stress -c 1"       20 seconds ago      Up 19 seconds                           cpu3
[root@server1 stress]# docker exec -it d7f14bbbde81 /bin/bash

在这里插入图片描述
关闭容器

[root@server1 stress]# docker run -tid --name cpu4 --cpuset-cpus 3 --cpu-shares 1024 centos:stress stress -c 1
34c37f94323dbf4d5cde6c01047ebc25358a8455317fbea9f3d33ee54f06c695
[root@server1 stress]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
34c37f94323d        centos:stress       "stress -c 1"       15 seconds ago      Up 14 seconds                           cpu4
d7f14bbbde81        centos:stress       "stress -c 1"       2 minutes ago       Up 2 minutes                            cpu3
[root@server1 stress]# docker exec -it 34c37f94323d /bin/bash

在这里插入图片描述
总结:上面的 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。

[root@localhost stress]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 250M
--vm 1:启动 1 个内存工作线程。 
--vm-bytes 250M:每个线程分配 250M 内存。 

在这里插入图片描述

stress: dbug: [6] touching bytes in strides of 4096 bytes ...   ctrl+c 结束任务
^Cstress: FAIL: [1] (416) <-- worker 6 got signal 2
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 86s
[root@server1 stress]# docker ps -a  //此时容器的状态为自动退出
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                       PORTS               NAMES
4dc8305626cb        progrium/stress     "/usr/bin/stress --v…"   2 minutes ago       Exited (1) 44 seconds ago                        busy_franklin

默认情况下,容器可以使用主机上的所有空闲内存。
与 CPU 的 cgroups 配置类似, Docker 会自动为容器在目录 /sys/fs/cgroup/memory/docker/<容器的完整长 ID>
中创建相应 cgroup 配置文件

如果让工作线程分配的内存超过 300,分配的内存超过限额,stress 线程报错,容器 退出。

[root@server1 stress]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 400M
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 [6] forked
stress: dbug: [6] allocating 419430400 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 6 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 0s

Block IO 的限制
默认情况下,所有容器能平等地读写磁盘,可以通过设置–blkio-weight(权重) 参数来改变 容器 block IO 的优先级。
–blkio-weight 与 --cpu-shares 类似,设置的是相对权重值,默认为 500。
在下面 的例子中,容器 A 读写磁盘的带宽是容器 B 的两倍。

[root@server1 stress]#  docker run -it --name container_B --blkio-weight 250 centos:stress
[root@5ae358d427ed /]# cat /sys/fs/cgroup/blkio/blkio.weight
250

[root@server1 stress]#  docker run -it --name container_S --blkio-weight 321 centos:stress
[root@42b42ebfb393 /]# cat /sys/fs/cgroup/blkio/blkio.weight
321

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 的速率为 25 MB/s。

[root@server1 stress]#  docker run -it --device-write-bps /dev/sda:25MB centos:stress
[root@859811cc5303 /]# dd if=/dev/zero of=test bs=1M count=1024 oflag=direct   //

标记出读写的速度,可以按ctrl+c中断查看
此时没有显示数据,但ctrl+c可以终止查看
附加:dd if=/dev/null linux的黑洞,放不下,放进去就再也拿不出来了
zero:取之不完

[root@859811cc5303 /]# dd if=/dev/zero of=test bs=1M count=1024 oflag=direct
^C126+0 records in
126+0 records out
132120576 bytes (132 MB) copied, 5.0212 s, 26.3 MB/

过 dd 命令测试在容器中写磁盘的速度。因为容器的文件系统是在 host /dev/sda 上 的,
在容器中写文件相当于对 host /dev/sda 进行写操作。另外,oflag=direct 指定用 direct IO 方式写文件,
这样 --device-write-bps 才能生效。

结果表明限速 25MB/s 左右。作为对比测试,如果不限速,结果如下。

[root@server1 stress]#  docker run -it  centos:stress
[root@f4f7819485f2 /]# 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.815358 s, 1.3 GB/s
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值