【云原生|Docker】04-docker的资源限制
目录
前言
容器的生命周期
1. 容器的启动过程
2. 容器的生命周期
编辑
内存限制
1. 内存限制的相关参数
2. 内存限制方式
2.1 设置-m,--memory,不设置--memory-swap
2.2 设置-m,--memory=a,--memory-swap=b,且b >a
2.3 设置-m,--memory=a,--memory-swap=-1
2.4 内存软限制(设置--memory-reservation)
2.5 OOM killer
2.6 设置-m=a,--memory-swappiness=0
CPU限制
1. cpu限制相关参数
2. cpu限制方式
2.1 设置容器可以在哪个CPU上运行
2.2 cpu权重设置
Block io限制
1. 限制io的参数
2. IO限制操作
2.1 block IO 权重
2.2 限制bps和iops
前言
用户内存限制就是对容器能使用的内存和交换分区的大小作出限制;主机运行若干容器,每个容器都需要cpu、内存以及IO资源,为避免因为单个容器占用过多资源而影响到所有其他容器乃至整个宿主机的性能,因此需要对容器资源进行限制。
容器的生命周期
在说容器的资源限制之前,我们需要补充两点知识点。
1. 容器的启动过程
容器的启动过程说明:
- 检查本地是否存在指定的镜像,如果没有就从指定的仓库下载
- 利用镜像启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个IP给容器
- 执行用户指定的程序
- 执行完毕后停止容器
2. 容器的生命周期
下面这张图实际上展现了容器的各个状态和操作,比如操作:start,kill,stop等。
一个docker host上会运行若干容器,每个容器都需要CPU、内存和 IO 资源。对于 KVM,VMware等虚拟化技术,用户可以控制分配多少 CPU、内存资源给每个虚拟机。对于容器,Docker 也提供了类似的机制避免某个容器因占用太多资源而影响其他容器乃至整个 host 的性能。因此我们需要对容器的内存,cpu,io,磁盘等做适当的限制。
内存限制
1. 内存限制的相关参数
参数 | 说明 |
-m,--memory | 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为4M |
--memory-swap | 内存+交换分区大小总限制,格式同上,必须比-m设置的值大 |
--memory-reservation | 内存的软性限制,格式同上 |
--oom-kill-disable | 是否阻止 OOM killer 杀死容器,默认没设置 |
--oom-score-adj | 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0 |
--memory-swappiness | 用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数 |
--kernel-memory | 核心内存限制。格式同上,最小为 4M |
提示:
· -m,--memory 选项的参数最小为 4 M;
· --memory-swap不是交换分区,而是内存加交换分区的总大小,所以--memory-swap必须比-m,--memory大;
· 若不设置-m,--memory和--memory-swap,容器默认可用完宿主机所有内存和swap分区。但容器占宿主机所有内存和swap分区超过一段时间后,会被宿主机系统杀死(若没有设置--oom-kill-disable=true)。
2. 内存限制方式
2.1 设置-m,--memory,不设置--memory-swap
使用-m或--memory设置一个不小于 4M 的a值,不设置--memory-swap,或将--memory-swap设置为 0。则表示容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因为 Docker 默认容器交换分区的大小和内存相同。若容器中运行一个一直不停申请内存的程序,则该程序最终能使用的内存大小为 2a。
示例: 使用progrium/stress容器来资源测试。
当设置-m=200m,不设置--memory-swap,则容器最多使用内存大小测试如下:
Step1:测试内存为200m
[root@clinet ~]# docker run -it -m 200m progrium/stress --vm 1 --vm-bytes 200m
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 209715200 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 209715200 bytes
stress: dbug: [7] allocating 209715200 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 209715200 bytes
stress: dbug: [7] allocating 209715200 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 209715200 bytes
stress: dbug: [7] allocating 209715200 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
Step2:测试内存为399m
[root@clinet ~]#
[root@clinet ~]# docker run -it -m 200m progrium/stress --vm 1 --vm-bytes 399m
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 418381824 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 418381824 bytes
stress: dbug: [7] allocating 418381824 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 418381824 bytes
stress: dbug: [7] allocating 418381824 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
Step3:测试内存为401M
[root@clinet ~]# docker run -it -m 200m progrium/stress --vm 1 --vm-bytes 401m
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 420478976 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
总结:
使用-m或--memory设置一个不小于 4M 的a值,不设置--memory-swap,或将--memory-swap设置为 0。则表示容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因此容器最大使用的内存数为2a
2.2 设置-m,--memory=a,--memory-swap=b,且b >a
示例: 当设置-m=200m,设置--memory-swap=300m时,此时的swap=100m
Step1: 测试内存为299m(容器正常运行)
[root@clinet ~]# docker run -it -m 200m --memory-swap 300m progrium/stress --vm 1 --vm-bytes 299m
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 313524224 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] freed 313524224 bytes
stress: dbug: [6] allocating 313524224 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
step2:测试内存为301时(容器运行异常)
[root@clinet ~]# docker run -it -m 200m --memory-swap 300m progrium/stress --vm 1 --vm-bytes 301m
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 315621376 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 0s
使用-m或--memory设置一个不小于 4M 的a值,且b必须大于a,使用--memory-swap设置一个参数 b。则表示容器能使用的内存大小为 a,能使用的交换分区+内存大小为 b,b-a 即为容器能使用的 swap 分区大小。此时容器最大使用内存为b
2.3 设置-m,--memory=a,--memory-swap=-1
使用-m或--memory设置一个不小于 4M 的a值,使用--memory-swap设置一个参数-1,则表示限制容器能使用的内存大小为 a,且不限制容器使用 swap 分区大小。
提示:若出现如下提示:
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
可打开内核内存限制的操作:
[root@docker docker]# vi /etc/default/grub
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
root@docker:~# update-grub #重启系统
2.4 内存软限制(设置--memory-reservation)
Memory reservation是一种软性限制,用于节制容器内存使用。使用--memory-reservation设置一个比-m小的值后,虽然容器最多可以使用-m设置的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到--memory-reservation设置的值大小。没有设置时(默认情况下)--memory-reservation的值则--memory-reservation和-m的限定的值相同。将它设置为 0 或设置的比-m的参数大等同于没有设置。这种软性机制,它不保证任何时刻容器使用的内存不会超过--memory-reservation限定的值,它只是确保容器不会长时间占用超过--memory-reservation限制的内存大小。
[root@docker docker]# docker run -d --name ubuntu_04 -m 2G --memory-reservation 1G ubuntu:16.04
#该容器能使用的内存大小为 2G,当宿主机内存资源紧张时,系统会回收1G内存,以便尝试将容器的内存锁紧到 1G 以下。
2.5 OOM killer
OOM killer机制指默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。通过设置--oom-kill-disable选项来禁止 OOM killer 杀死容器内进程。但请确保只有在使用了-m/--memory选项时才使用--oom-kill-disable禁用 OOM killer。如果没有设置-m选项,却禁用了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。
2.6 设置-m=a,--memory-swappiness=0
默认情况下,容器的内核可以交换出一定比例的swap。--memory-swappiness可设置从 0 到 100这个比例。0 表示关闭swap交换。100 表示所有的swap都可以交换。默认情况下,如果不使用--memory-swappiness,则该值从父进程继承而来。
注意:--memory-swappiness=0 表示禁用容器 swap 功能。
示例:设置-m=200m和--memory-swappiness=0
Step·:测试内存为199m(容器正常运行)
[root@clinet ~]# docker run -it -m 200m --memory-swappiness 0 progrium/stress --vm 1 --vm-bytes 199m
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 208666624 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] freed 208666624 bytes
stress: dbug: [6] allocating 208666624 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] freed 208666624 bytes
stress: dbug: [6] allocating 208666624 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
Step2:测试内存为201m(容器终止运行)
[root@clinet ~]# docker run -it -m 200m --memory-swappiness 0 progrium/stress --vm 1 --vm-bytes 201m
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 210763776 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 0s
总结:
以上多种内存限制方式中,我们一般只使用设置-m=a,--memory-swappiness=0的方式来限制容器内存的使用,此时-m设置的a值,即为容器可使用的最大值。
CPU限制
1. cpu限制相关参数
参数 | 说明 |
--cpuset-cpus="0,3" | 允许使用的 CPU 集,值可以为 0-3,0,1 |
-c,--cpu-shares=0 | CPU 共享权值(相对权重) |
--cpu-period=0 | 限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000] |
--cpu-quota=0 | 限制 CPU CFS 配额,必须不小于1ms,即 >= 1000 |
--cpuset-mems="" | 允许在上执行的内存节点(MEMs),只对 NUMA 系统有效 |
2. cpu限制方式
2.1 设置容器可以在哪个CPU上运行
表示容器中的进程可以在 cpu 1 和 cpu 3 上执行。
docker run -d --name ubuntu_07 --cpuset-cpus="1,3" ubuntu:16.04
2.2 cpu权重设置
默认情况下,所有的容器得到同等比例的 CPU 周期。在有多个容器竞争 CPU 时可设置每个容器能使用的 CPU 时间比例。这个比例叫作共享权值,通过-c或--cpu-shares设置。Docker 默认每个容器的权值为 1024。不设置或将其设置为 0,都将使用这个默认值。系统会根据每个容器的共享权值和所有容器共享权值和比例来给容器分配 CPU 时间。
举例:
假设有三个正在运行的容器,这三个容器中的任务都是 CPU 密集型的。第一个容器的 cpu 共享权值是 1024,其它两个容器的 cpu 共享权值是 512。第一个容器将得到 50% 的 CPU 时间,而其它两个容器就只能各得到 25% 的 CPU 时间了。如果再添加第四个 cpu 共享值为 1024 的容器,每个容器得到的 CPU 时间将重新计算。第一个容器的CPU 时间变为 33%,其它容器分得的 CPU 时间分别为 16.5%、16.5%、33%。
[root@clinet ~]# docker run -d -c 1024 progrium/stress -c 1
9423b7ae9b21f86ee22f2ab569f8deadef1657a7fcf526791037f10db063bf66
[root@clinet ~]# docker run -d -c 512 progrium/stress -c 1
ea17c016c1377d75a9af85df6c66181f53e4fef753780cc639a85234adfe89b7
[root@clinet ~]# docker run -d -c 512 progrium/stress -c 1
cc45e9082c218a6dc7a6405fd2ab4f38203bc78be2bc104f3cd8eccd6578af0d
[root@clinet ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc45e9082c21 progrium/stress "/usr/bin/stress --v…" 22 seconds ago Up 20 seconds pedantic_moore
ea17c016c137 progrium/stress "/usr/bin/stress --v…" 25 seconds ago Up 24 seconds pensive_wing
9423b7ae9b21 progrium/stress "/usr/bin/stress --v…" 31 seconds ago Up 30 seconds sleepy_moore
[root@clinet ~]#
在多核系统上,CPU 时间权值是在所有 CPU 核上计算的。即使某个容器的 CPU 时间限制少于 100%,它也能使用各个 CPU 核的 100% 时间。
例如,假设有一个2核的系统。用-c=512的选项启动容器{C0},并且该容器只有一个进程,用-c=1024的启动选项为启动容器C1,此时C0也可以是占用100%
[root@clinet ~]# docker run -d -c 512 progrium/stress -c 1
6bda65f951693eec80455a32db301dbbf0350c1347f9ea70d50d8ac858e8fe56
[root@clinet ~]# docker run -d -c 1024 progrium/stress -c 1
41e7bc74b2c3dc04c6d45986121ebdd5f93f9f89f1ab805020084cdeb9b86fd2
[root@clinet ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41e7bc74b2c3 progrium/stress "/usr/bin/stress --v…" About a minute ago Up About a minute sleepy_raman
6bda65f95169 progrium/stress "/usr/bin/stress --v…" About a minute ago Up About a minute peaceful_noyce
[root@clinet ~]#
总结:
在实际生产中几乎不会设置容器在某个cpu上运行,使用最多的就是通过权重限制cpu的使用。
Block io限制
Block IO 是另一种可以限制容器使用的资源。Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽。
1. 限制io的参数
参数 | 说明 |
--blkio-weight | 设置块 IO 权重,可以为容器指定一个 0 到 1000 的值,值越高,表示容器在 IO 资源分配中拥有更高的优先级。默认值为 0,表示不受限制。 |
--device-read-bps | 限制设备的读速率,可以为容器中的设备指定一个最大的读速率,单位是字节/秒,例如 --device-read-bps=/dev/sda:1mb 表示限制容器中 /dev/sda 设备的读速率为 1MB/s |
--device-write-bps | 限制设备的写速率,同上 |
--device-read-iops | 限制设备的 IOPS(每秒输入操作数),可以为容器中的设备指定一个最大的 IOPS 数量,例如 --device-read-iops=/dev/sda:100 表示限制容器中 /dev/sda 设备的读 IOPS 为 100。 |
--device-write-iops | 限制设备的 IOPS(每秒输出操作数),同上 |
bps是byte per second,每秒读写的数据量 ;iops是io per second,每秒io的次数。
注意:目前 Block IO 限额只对 direct IO(不使用文件缓存)有效
2. IO限制操作
2.1 block IO 权重
ubuntu_10容器的读写磁盘的带宽是ubuntu_11的两倍。
[root@docker docker]# docker run -d --name ubuntu_10 --blkio-weight 600 ubuntu:16.04
[root@docker docker]# docker run -d --name ubuntu_11 --blkio-weight 300 ubuntu:16.04
2.2 限制bps和iops
ubuntu_12容器写 /dev/sda 的速率为 30 MB/s。
bps:byte per second,每秒读写的数据量。
iops:io per second,每秒 IO 的次数。
[root@docker docker]#docker run -d --name ubuntu_12 --device-write-bps /dev/sda:30MB ubuntu:16.04
容器常用的资源限制就是上面所说的:1. -m=a,--memory-swappiness=0的方式来限制容器内存的使用;2. 通过-c或--cpu-shares设置权重限制cpu的使用;3. 通过bps和iops和权重限制block io。