目录
1、CPU使用率
2、CPU共享比例
3、CPU周期限制
4、CPU核心限制
5、CPU 配额控制参数的混合案例
6、内存限制
7、Block IO 的限制
8、限制bps 和iops
docker资源限制
Docker容器技术底层是通过Cgroup(Control Group 控制组)实现容器对物理资源使用的限制,限制的资源包括CPU、内存、磁盘三个方面。基本覆盖了常见的资源配额和使用量控制。
Cgroup 是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源的机制,被LXC及Docker等很多项目用于实现进程的资源控制。
Cgroup 是提供将进程进行分组化管理的功能和接口的基础结构,Docker中I/O 或内存的分配控制等具体的资源管理功能都是通过Cgroup功能来实现的。这些具体的资源管理功能称为Cgroup子系统,以下是对几大子系统的介绍。
- blkio:限制块设备的输入输出控制。如:磁盘、光盘、USB等。
- cpu:限制CPU资源的访问
- cpuacct:产生Cgroup 任务的CPU资源报告。
- cpuset:限制分配单独的cpu 和内存资源。
- devices:允许或拒绝对设备的访问。
- freezer:暂停和恢复Cgroup 任务。
- memory:设置每个Cgroup 的内存限制以及产生内存资源报告。
- net_cls:用于标记每个网络包。
- ns:命名空间子系统。
- perf_event:增加了对每group 的监测跟踪的能力,可以监测属于某个特定的group 的所有线程以及运行在特定CPU 上的线程。
使用下面的Dockerfile 来创建一个基于CentOS的stress工具镜像。
[root@localhost ~]# cat centos-7-x86_64.tar.gz | docker import - centos:7
[root@localhost ~]# mkdir stress
[root@localhost ~]# cd stress
[root@localhost stress]# vim Dockerfile
FROM centos:7
MAINTAINER crushlinux "crushlinux@163.com"
RUN yum -y install wget
RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install stress
[root@localhost ~]# cd stress/
[root@localhost stress]# docker build -t centos:stress .
1、CPU使用率
在CentOS7中可以通过修改对应的Cgroup配置文件cpu.cfs_quota_us的值来实现,直接执行echo命令将设定值导入到文件中就会立即生效。
例如将容器face0aee201c的CPU使用设置为20000,设置CPU的使用率限定为20%。
[root@localhost ~]# docker run -itd centos:stress /bin/bash
7faed0c97b48c0ae236473431589b5c55aa94d3a93b5cdc7f6887f62e3ca9d00
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7faed0c97b48 centos "/bin/bash" 30 seconds ago Up 29 seconds cranky_mclaren
[root@localhost ~]# echo "20000" > /sys/fs/cgroup/cpu,cpuacct/docker/7faed0c97b48c0ae236473431589b5c55aa94d3a93b5cdc7f6887f62e3ca9d00/cpu.cfs_quota_us
2、CPU共享比例
当多个容器任务运行时,很难计算CPU的使用率,为了使容器合理使用CPU资源,可以通过--cpu-shares选项设置容器按比例共享CPU资源,这种方式还可以实现CPU使用率的动态调整。
命令中的--cpu-shares 选项值不能保证可以获得1 个vcpu 或者多少GHz 的CPU 资源,仅仅只是一个弹性的加权值。
例如,运行3个新建容器A、B、C。占用CPU资源的比例为1:1:2:4
docker run --name A -itd --cpu-shares 1024 centos:stress /bin/bash
docker run --name B -itd --cpu-shares 1024 centos:stress /bin/bash
docker run --name C -itd --cpu-shares 2048 centos:stress /bin/bash
docker run --name D -itd --cpu-shares 4096 centos:stress /bin/bash
如果又一个容器D需要更多CPU资源,则可以将其--cpu-shares的值设置为4096,那么ABCD的CPU资源比例变为1:1:2:4。
默认情况下,每个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 shares可以设置容器使用CPU的优先级,比如启动了两个容器及运行查看CPU使用百分比。
[root@localhost ~]# docker run -tid --name cpu512 --cpu-shares 512 centos:stress stress -c 10
[root@localhost ~]# docker run -tid --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f68b6a6c4e3c centos:stress "stress -c 10" 13 seconds ago Up 12 seconds cpu1024
487ec0954a8b centos:stress "stress -c 10" 23 seconds ago Up 22 seconds cpu512
[root@localhost ~]# docker exec -it f68b6a6c4e3c /bin/bash
[root@localhost ~]# docker exec -it 487ec0954a8b /bin/bash
开启了10 个stress 进程,目的是充分让系统资源变得紧张。只有这样竞争资源,设定的资源比例才可以显现出来。如果只运行一个进行,会自动分配到空闲的CPU,这样比例就无法看出来。由于案例的环境不一样,可能导致上面两张图中占用CPU 百分比会不同,但是从cpu share 来看两个容器总比例一定会是1:2。
3、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 秒)。
[root@localhost ~]# docker run -it --cpu-period 100000 --cpu-quota 200000 centos:stress /bin/bash
[root@2474ee0f7e62 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us
1000000
[root@2474ee0f7e62 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
200000
4、CPU核心限制
多核CPU的服务器Docker还可以控制容器运行限定使用哪些CPU内核,可以使用--cpuset-cpus选项来使某些程序独享CPU核心,以便提高其处理速度,对应的Cgroup文件为/sys/fs/cgroup/cpuset/docker/容器ID号/cpuset.cpus。选项后直接跟参数0、1、2……表示第1个内核,第2个内核,第3个内核,与/proc/cpuinfo中的标号相同。
如果服务器有16个核心,那么CPU编号为0~15,使新建容器绑定第1~4的核心使用:
docker run -itd --cpuset-cpus 0,1,2,3 centos:stress /bin/bash
或者
docker run --itd --cpuset-cpus 0-3 centos:stress /bin/bash
那么该容器内的进程只会在0,1,2,3的CPU上运行。
通过下面指令也可以看到容器中进程与CPU 内核的绑定关系,可以认为达到了绑定CPU 内核的目的。
[root@localhost ~]# docker exec 2474ee0f7e62 taskset -c -p 1
pid 1's current affinity list: 0-3 //容器内部第一个进程编号一般为1
尽量使用绑定内核的方式分配CPU资源给容器进程使用,然后在配合--cpu-shares选项动态调整CPU使用资源的比例。
5、CPU 配额控制参数的混合案例
通过--cpuset-cpus 指定容器A 使用CPU 内核0,容器B 只是用CPU 内核1。在主机上只有这两个容器使用对应CPU 内核的情况,它们各自占用全部的内核资源,--cpu-shares 没有明显效果。
--cpuset-cpus、--cpuset-mems 参数只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
在系统具有多个CPU 内核的情况下,需要通过cpuset-cpus 为容器CPU 内核才能比较方便地进行测试。
用下列命令创建测试用的容器:
[root@localhost ~]# docker run -itd --name cpu3 --cpuset-cpus 3 --cpu-shares 512 centos:stress stress -c 1
[root@localhost ~]# docker exec -it容器ID /bin/bash
输入top 命令查看
[root@localhost ~]# docker run -itd --name cpu4 --cpuset-cpus 3 --cpu-shares 1024 centos:stress stress -c 1
[root@localhost ~]# docker exec -it容器ID /bin/bash
输入top 命令查看
上面的centos:stress 镜像安装了stress 工具,用来测试CPU 和内存的负载。通过在两个容器上分别执行stress -c 1 命令,将会给系统一个随机负载,产生1 个进程。这个进程都反复不停的计算由rand() 产生随机数的平方根,直到资源耗尽。
观察到宿主机上的CPU 试用率,第三个内核的使用率接近100%,并且一批进程的CPU 使用率明显存在2:1 的使用比例的对比。
6、内存限制
与操作系统类似,容器可使用的内存包括两部分:物理内存和swap。Docker 通过下面两组参数来控制容器内存的使用量。
- -m 或--memory:设置内存的使用限额,例如100M, 1024M。
- --memory-swap:设置内存swap 的使用限额。
当执行如下命令:
其含义是允许该容器最多使用200M 的内存和300M 的swap。
[root@localhost ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
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 293601280 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] freed 293601280 bytes
stress: dbug: [6] allocating 293601280 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
- --vm 1:启动1 个内存工作线程。
- --vm-bytes 280M:每个线程分配280M 内存。
默认情况下,容器可以使用主机上的所有空闲内存。与CPU 的cgroups 配置类似,docker 会自动为容器在目录/sys/fs/cgroup/memory/docker/<容器的完整长ID>中创建相应cgroup 配置文件。
因为280M 在可分配的范围(300M)内,所以工作线程能够正常工作,其过程是:
- 分配280M 内存。
- 释放280M 内存。
- 再分配280M 内存。
- 再释放280M 内存。
- 一直循环......
如果让工作线程分配的内存超过300M,分配的内存超过限额,stress 线程报错,容器退出。
[root@localhost ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 380M
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 [7] forked
stress: dbug: [7] allocating 398458880 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 7 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 1s
7、Block IO 的限制
默认情况下,所有容器能平等地读写磁盘,可以通过设置--blkio-weight 参数来改变容器block IO 的优先级。
--blkio-weight 与--cpu-shares 类似,设置的是相对权重值,默认为500。在下面的例子中,容器A 读写磁盘的带宽是容器B 的两倍。
[root@localhost ~]# docker run -it --name container_A --blkio-weight 600 centos:stress /bin/bash
[root@a4b63e72a03b /]# cat /sys/fs/cgroup/blkio/blkio.weight
600
[root@localhost ~]# docker run -it --name container_B --blkio-weight 300 centos:stress /bin/bash
[root@ee688a3bba2b /]# cat /sys/fs/cgroup/blkio/blkio.weight
300
8、限制bps 和iops
如果在一台服务器上进行容器的混合部署,那么会存在同时几个程序写磁盘数据的情况,这时可以通过--device-write-iops选项来限制每秒写io次数来限制制定设备的写速度。相应的还有--device-read-iops选项可以限制读取IO的速度,但是这种方法只能限制设备,而不能限制分区,相应的Cgroup写配置文件为/sys/fs/cgroup/blkio/容器ID/ blkio.throttle.write_iops_device。
- 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 的速率为5 MB/s。
[root@localhost ~]# docker run -it --device-write-bps /dev/sda:5MB centos:stress /bin/bash [root@7fc31cd1514b /]# dd if=/dev/zero of=test bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 20.0051 s, 5.2 MB/s
通过dd 测试在容器中写磁盘的速度。因为容器的文件系统是在host /dev/sda 上的,在容器中写文件相当于对宿主机/dev/sda 进行写操作。另外,oflag=direct 指定用direct
IO 方式写文件,这样--device-write-bps 才能生效。
结果表明,bps 5.2 MB/s 在限速5MB/s 左右。作为对比测试,如果不限速,结果如下:
[root@localhost ~]# docker run -it centos:stress /bin/bash
[root@9128099a8947 /]# dd if=/dev/zero of=test bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.384372 s, 273 MB/s