Docker Cgroup

271 阅读11分钟

cgroups简介

Docker 通过 Cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面, 基本覆盖了常见的资源配额和使用量控制。
Cgroup 是 ControlGroups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 CPU、内存、磁盘 IO 等等) 的机制,被 LXC、docker 等很多项目用于实现进程资源控制。Cgroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理是通过该功能来实现的。

cgroups有四大功能

  • 资源限制:可以对任务使用的资源总额进行限制
  • 优先级分配:通过分配的cpu时间片数量以及磁盘IO带宽大小,实际上相当于控制了任务运行优先级。
  • 资源统计:可以统计系统的资源使用量,如cpu时长,内存用量等
  • 任务控制: cgroup可以对任务 执行挂起、恢复等操作

cpu时间切片

时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费

在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行

cpu资源控制

1)设置cpu使用率上限 Linux通过CFS(Completely Fair Scheduler,完全公平调度器)来调度各个进程对CPU的使用。CFS默认的调度周期是100ms。 我们可以设置每个容器进程的调度周期,以及在这个周期内各个容器最多能使用多少 CPU 时间。

使用 --cpu-period 即可设置调度周期,使用 --cpu-quota 即可设置在每个周期内容器能使用的CPU时间。两者可以配合使用。 CFS 周期的有效范围是 1ms1s,对应的 --cpu-period 的数值范围是 10001000000。 而容器的 CPU 配额必须不小于 1ms,即 --cpu-quota 的值必须 >= 1000。

1)查看容器的默认CPU使用限制

#创建并启动容器
docker run -itd --name test5 centos:7 /bin/bash
#查看容器状态
docker ps -a
CONTAINER ID   IMAGE      COMMAND       CREATED      STATUS       PORTS     NAMES
3ed82355f811   centos:7   "/bin/bash"   5 days ago   Up 6 hours             test5

#切换到cgroup下针对容器的相关配置目录
cd /sys/fs/cgroup/cpu/docker/3ed82355f81151c4568aaa6e7bc60ba6984201c119125360924bf7dfd6eaa42b/
cat cpu.cfs_quota_us 
-1

cat cpu.cfs_period_us 
100000

#cpu.cfs_period_us:cpu分配的周期(微秒,所以文件名中用 us 表示),默认为100000。
#cpu.cfs_quota_us:表示该cgroups限制占用的时间(微秒),默认为-1,表示不限制。 如果设为50000,表示占用50000/100000=50%的CPU。

image.png

image.png

2)进行压力测试

docker exec -it test1 /bin/bash
vim /cpu.sh
#!/bin/bash
i=0
while true
do
let i++
done

chmod +x /cpu.sh
./cpu.sh

top
#可以看到这个脚本占了很多的cpu资源

image.png image.png image.png

3)设置50%的比例分配CPU使用时间上限

docker run -itd --name test6 --cpu-quota 50000 centos:7 /bin/bash
#可以重新创建一个容器并设置限额
或者
cd /sys/fs/cgroup/cpu/docker/3ed82355f81151c4568aaa6e7bc60ba6984201c119125360924bf7dfd6eaa42b/
echo 50000 > cpu.cfs_quota_us

#进入容器test2,写一个死循环脚本并运行
docker exec -it 3ed82355f811 /bin/bash
vi /cpu.sh
 #!/bin/bash
 i=0
 while true
 do
  let i++
 done
 chmod +x cpu.sh
./cpu.sh

top
#可以看到cpu占用率接近50%,cgroups对cpu的控制起了效果

image.png

4)对已存在的容器进行CPU限制

直接修改/sys/fs/cgroup/cpu/docker/容器id/cpu.cfs_quota_us文件即可

cd /sys/fs/cgroup/cpu/docker/
ls
5544961646e2c58c895c64893cb49035f29c2952593c97e25b7fdfb6ad4f6187
55e08e9f0a0947701148c7b054143d428fbe717456743b558d0295a5367f0f82
......
cd 5544961646e2c58c895c64893cb49035f29c2952593c97e25b7fdfb6ad4f6187/
[root@localhost 5544961646e2c58c895c64893cb49035f29c2952593c97e25b7fdfb6ad4f6187]#echo "40000" > cpu.cfs_quota_us
[root@localhost 5544961646e2c58c895c64893cb49035f29c2952593c97e25b7fdfb6ad4f6187]#cd ~
docker exec -it test1 bash
[root@5544961646e2 /]# ./cpu.sh
^C
[root@5544961646e2 /]# exit
exit

top   
PID USER     PR NI   VIRT   RES   SHR S %CPU %MEM     TIME+ COMMAND  
4635 root    20  0  11688  1096  916 R  39.9  0.1     1:27.62 cpu.sh

image.png 5)设置CPU资源占用比(设置多个容器时才有效)

docker rm $(docker ps -aq) -f
#删除所有容器
Docker 通过 --cpu-shares 指定 CPU 份额,默认值为1024,值为1024的倍数。
#创建两个容器为 c1 和 c2,若只有这两个容器,设置容器的权重,使得c1和c2的CPU资源占比为1/32/3。
docker run -itd --name c1 --cpu-shares 512 centos:7	
docker run -itd --name c2 --cpu-shares 1024 centos:7
docker ps -a

image.png 5.1)分别进入容器进行压力测试

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
net.ipv4.ip_forward = 1
#宿主机开启路由转发功能,使容器能够连通外网

docekr exec -it c1 bash
与
docker exec -it c2 bash
#分别进入容器,进行压力测试
yum install -y epel-release
yum install -y stress
stress -c 4
#产生四个进程,每个进程都反复不停的计算随机数的平方根

c1: image.png

image.png

image.png

image.png c2:

image.png

image.png

image.png

5.2)查看容器运行状态,观察CPU使用占比

 #再打开一个终端,查看容器运行状态(动态更新)
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
d0d385e3b519   c2        279.70%   204.6MiB / 3.686GiB   5.42%     35.5MB / 672kB   0B / 49.8MB   7
3ba090d35e34   c1        119.64%   204.5MiB / 3.686GiB   5.42%     35.5MB / 661kB   0B / 49.9MB   7

 
 #因为宿主机有4核,所以CPU总百分比是400%。
 #c1:c2 = 133.03%:266.52% ≈ 1:2

image.png

6)设置容器绑定指定CPU(梆核)

注意:CPU编号是从0开始的,编号1、3是代表第二个核和第四个核

#先分配虚拟机4个CPU核数
docker run -itd --name test7 --cpuset-cpus 1,3 centos:7 /bin/bash

#进入容器,进行压力测试
yum install -y epel-release
yum install stress -y
stress -c 4

#退出容器,执行 top 命令再按 1 查看CPU使用情况。

image.png

image.png

image.png

7)对内存使用的限制

-m (或--memory=)选项用于限制容器可以使用的最大内存

docker run -itd --name test8 -m 512m centos:7 /bin/bash

docker stats

//限制可用的 swap 大小, --memory-swap
强调一下,--memory-swap 是必须要与 --memory 一起使用的。

正常情况下,--memory-swap 的值包含容器可用内存和可用 swap。
所以 -m 300m --memory-swap=1g 的含义为:容器可以使用 300M 的物理内存,并且可以使用 700M(1G - 300)的 swap。

如果 --memory-swap 设置为 0 或者 不设置,则容器可以使用的 swap 大小为 -m 值的两倍。
如果 --memory-swap 的值和 -m 值相同,则容器不能使用 swap。
如果 --memory-swap 值为 -1,它表示容器程序使用的内存受限,而可以使用的 swap 空间使用不受限制(宿主机有多少 swap 容器就可以使用多少)。

image.png

8)限制容器可用的swap大小

 #限制可用的swap 大小,--memory-swap
 ●强调一下, --memory-swap是必须要与 --memory(或-m)一起使用的。
 ●正常情况下, --memory-swap 的值包含容器可用内存和可用swap 。
 ●所以 -m 300m --memory-swap=1g 的含义为:容器可以使用300M 的物理内存,并且可以使用700M (1G - 300M)的swap。
 ​
 设置为0或者不设置,则容器可以使用的 swap 大小为 -m 值的两倍。
 如果 --memory-swap 的值和 -m 值相同,则容器不能使用swap。
 如果 --memory-swap 值为 -1,它表示容器程序使用的内存受限,而可以使用的swap空间使用不受限制(宿主机有多少swap 容器就可以使用多少)。
 #--memory-swap 的值包含容器可用内存和可用swap,减去-m的值才是可用swap的值。
 #表示容器可以使用512M的物理内存,并且可以使用512M的swap。因为1g减去512m的物理内存,剩余值才是可用swap。
 docker run -itd --name yy01 -m 512m --memory-swap=1g centos:7 bash
 
 
 #--memoryswap值和 -m 的值相同,表示容器无法使用swap
 docker run -itd --name yy02 -m 512m --memory-swap=512m centos:7 bash
 
 
 # --memory-swap 的值设置为0或者不设置,则容器可以使用的 swap 大小为 -m 值的两倍。
 docker run -itd --name yy03 -m 512m centos:7 bash
 
 
 # --memory-swap 值为 -1,它表示容器程序使用的内存受限,但可以使用的swap空间使用不受限制(宿主机有多少swap 容器就可以使用多少)。
 docker run -itd --name yy04 -m 512m --memory-swap=-1 centos:7 bash

9)对磁盘IO的配置控制(blkio)的限制

--device-read-bps:限制某个设备上的读速度bps ( 数据量),单位可以是kb、mb (M)或者gb。

--device-write-bps : 限制某个设备上的写速度bps ( 数据量),单位可以是kb、mb (M)或者gb。

--device-read-iops :限制读某个设备的iops (次数)

--device-write-iops :限制写入某个设备的iops ( 次数)

 --device-read-bps:限制某个设备上的读速度bps ( 数据量),单位可以是kb、mb (M)或者gb。
 例: docker run -itd --name test9 --device-read-bps /dev/sda:1M centos:7 /bin/bash
 #表示该容器每秒只能读取1M的数据量
 
 --device-write-bps : 限制某个设备上的写速度bps ( 数据量),单位可以是kb、mb (M)或者gb。
 例: docker run -itd --name test10 --device-write-bps /dev/sda:1mb centos:7 /bin/bash
 #表示该容器每秒只能写入1M的数据量
 
 --device-read-iops :限制读某个设备的iops (次数)
 --device-write-iops :限制写入某个设备的iops ( 次数)

9.1创建容器,不限制写的速度

 #创建容器tt01,不限制写入速度
 docker run -it --name tt01 centos:7 /bin/bash
 
 #通过dd来验证写速度,拷贝50M的数据
 dd if=/dev/zero of=/opt/test.out bs=10M count=5 oflag=direct   #添加oflag参数以规避掉文件系统cache
 
 #创建容器tt01,不限制写入速度
docker run -it --name test01 centos:7 /bin/bash
 #通过dd来验证写速度,拷贝50M的数据到容器中
dd if=/dev/zero of=/opt/test.out bs=10M count=5 oflag=direct   #添加oflag参数以规避掉文件系统cache
 5+0 records in
 5+0 records out
 52428800 bytes (52 MB) copied, 0.0948474 s, 553 MB/s
 
 #没有限制写速度的情况下,写入很快,0.09秒的时间内已写入50M的数据,写入速度为553M/s。

image.png

 #创建容器,并限制写入速度为1MB/s,即每秒只能写入1MB的数据量。
docker run -it --name test02 --device-write-bps /dev/sda:1mb centos:7 bash
 #通过dd来验证写速度,拷贝50M的数据到容器中
dd if=/dev/zero of=/opt/test.out bs=10M count=5 oflag=direct  #添加oflag参数以规避掉文件系统cache
 5+0 records in
 5+0 records out
 52428800 bytes (52 MB) copied, 50.0048 s, 1.0 MB/s
 
 #写入50M的数据,需要50s左右,因为限制了容器的写速度是 1.0 MB/s。

image.png

清除docker占用的磁盘空间

docker system prune -a 可用于清理磁盘,删除关闭的容器、无用的数据卷和网络

docker system prune -a

image.png

image.png