Docker运行时的容器本质是进程。在Linux中,通过Namespace进行资源隔离,Cgroups进行资源限制。
容器资源CPU限制设置测试
默认所有的容器对于 CPU 的利用占比都是一样的,-c 或者 –cpu-shares 可以设置 CPU 利用率权重,默认为 1024,可以设置权重为 2 或者更高(单个 CPU 为 1024,两个为 2048,以此类推)。如果设置选项为 0,则系统会忽略该选项并且使用默认值 1024。通过以上设置,只会在 CPU 密集(繁忙)型运行进程时体现出来。当一个 container 空闲时,其它容器都是可以占用 CPU 的。cpu-shares 值为一个相对值,实际 CPU 利用率则取决于系统上运行容器的数量。
假如一个 1core 的主机运行 3 个 container,其中一个 cpu-shares 设置为 1024,而其它 cpu-shares 被设置成 512。当 3 个容器中的进程尝试使用 100% CPU 的时候「尝试使用 100% CPU 很重要,此时才可以体现设置值」,则设置 1024 的容器会占用 50% 的 CPU 时间。如果又添加一个 cpu-shares 为 1024 的 container,那么两个设置为 1024 的容器 CPU 利用占比为 33%,而另外两个则为 16.5%。简单的算法就是,所有设置的值相加,每个容器的占比就是 CPU 的利用率,如果只有一个容器,那么此时它无论设置 512 或者 1024,CPU 利用率都将是 100%。当然,如果主机是 3core,运行 3 个容器,两个 cpu-shares 设置为 512,一个设置为 1024,则此时每个 container 都能占用其中一个 CPU 为 100%。
现在运行两个测试 container,一个权重设置为 2,一个权重设置 4,启动命令如下:
[root@ok188 ~]# docker run -it -d --cpu-shares 2 --name 2_cpu centos:7 /bin/bash
f3f125f7455974be77e58c0864d045b3b56ae2d007bd9095c47faca50893547c
[root@ok188 ~]# docker run -it -d --cpu-shares 4 --name 4_cpu centos:7 /bin/bash
5e623b55a22ef6d1e41e5978dd1c5d05d743b3a91498697db3b4b9c493f03f8b
通过压测工具如Stress进行压测,Stress使用实例:
[root@ok188 ~]# stress -c 13 -i 4 --verbose --timeout 1m
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 800k
[root@ok188 ~]# stress -c 13
# 生成小文件
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 13
# 生成大文件
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 3G
对两个容器同时进行 CPU压测,在宿主机中查看两个容器CPU占用情况
[root@ok188 ~]# docker stats 2_cpu
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
2_cpu 33.31% 24.34MiB / 1.932GiB 1.23% 18.2MB / 322kB 49.1MB / 27.7MB
[root@ok188 ~]# docker stats 4_cpu
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
4_cpu 65.96% 38.82MiB / 1.932GiB 1.96% 18.3MB / 451kB 8.19kB / 27.7MB
观察以上结果发现容器名为4_cpu权重比2_cpu大2倍,所以4_cpu可使用的cpu更多。
停止压测名为4_cpu的容器, 在宿主机中查看两个容器CPU占用情况
[root@ok188 ~]# docker stats 2_cpu
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
2_cpu 99.50% 24.34MiB / 1.932GiB 1.23% 18.2MB / 322kB 49.1MB / 27.7MB
[root@ok188 ~]# docker stats 4_cpu
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
4_cpu 0.00% 37.5MiB / 1.932GiB 1.90% 18.3MB / 452kB 8.19kB / 27.7MB
结论参数cpu-shares 只是设置cpu使用权重,只会在 CPU 密集(繁忙)型运行进程时体现出来。当一个 container 空闲时,其它容器都是可以占用 CPU 的。
备注:物理主机为多核的情况下,显示的cpu使用比例有差异,本人测试主机为单核。
默认的 CPU CFS「Completely Fair Scheduler」period 是 100ms。我们可以通过 –cpu-period 值限制容器的 CPU 使用。一般 –cpu-period 配合 –cpu-quota 一起使用。
为啥把这两个参数放一起呢?因为这两个参数是相互配合的,–cpu-period和–cpu-quota的这种配置叫Ceiling Enforcement Tunable Parameters,–cpu-shares的这种配置叫Relative Shares Tunable Parameters。–cpu-period是用来指定容器对CPU的使用要在多长时间内做一次重新分配,而–cpu-quota是用来指定在这个周期内,最多可以有多少时间用来跑这个容器。跟–cpu-shares不同的是这种配置是指定一个绝对值,而且没有弹性在里面,容器对CPU资源的使用绝对不会超过配置的值。
设置 cpu-period 为 100000,cpu-quota 为 50000,表示最多可以使用 cpu到50%。
[root@nsj-13-58 ~]# docker run -it --cpu-period=100000 --cpu-quota=50000 --name 0.5_p_cpu centos:7 /bin/bash
压测容器,并查看CPU占用情况:
[root@ok188 ~]# docker stats 0.5_p_cpu
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
0.5_p_cpu 50.33% 22.12MiB / 1.932GiB 1.12% 18.3MB / 348kB 524kB / 27.7MB
通过以上测试可以得知,–cpu-period 结合 –cpu-quota 配置是固定的,无论宿主机系统 CPU 是闲还是繁忙,如上配置,容器最多只能使用 CPU 到 50%。
默认启动一个container,对于容器的内存是没有任何限制的。
[root@ok188 ~]# docker run -it -d --name no_limit_memory centos:7 /bin/bash
[root@ok188 ~]# docker stats no_limit_memory
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
no_limit_memory 0.00% 932KiB / 1.932GiB 0.05% 586B / 0B 8.19kB / 0B
[root@ok188 ~]# free -g
total used free shared buff/cache available
Mem: 1 0 0 0 1 1
Swap: 1 0 1
MEM LIMIT显示的是容器宿主机的内存大小,Mem+Swap的总大小
设置-m值为500Mb,表示容器程序使用内存受限。按照官方文档的理解,如果指定 -m 内存限制时不添加 –memory-swap 选项,则表示容器中程序可以使用 500m内存和500m swap 内存。那么容器里程序可以跑到500m*2=1g后才会被oom给杀死。
[root@ok188 ~]# docker run -it -d -m 500m --name limit_memory_1 centos:7 /bin/bash
21eb95cffa45972603cb0e67b7ee0724d019cd7182fa5668bf07665ddf4f83cc
[root@ok188 ~]# docker stats limit_memory_1
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
limit_memory_1 0.00% 916KiB / 500MiB 0.09% 586B / 0B 0B / 0B
在libcontainer源码里
memory.memsw.limit_in_bytes
值是被设置成我们指定的内存参数的两倍。
其中代码如下:
// By default, MemorySwap is set to twice the size of RAM.
// If you want to omit MemorySwap, set it to `-1'.
if d.c.MemorySwap != -1 {
if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
return err
}
使用压测工具进行压测,当压测值是 memory + swap之和上限时,则容器中的进程会被直接 OOM kill。
[root@ok188 ~]# docker run -it -d -m 500m --memory-swappiness=0 --name limit_memory_noswap_1 centos:7 /bin/bash
使用压测工具进行压测,当压测值是 1G ,则容器中的进程会被直接 OOM kill。查看容器内系统日志:
表示容器程序使用内存受限,而 swap 空间使用不受限制(宿主 swap 支持使用多少则容器即可使用多少。如果 –memory-swap 设置小于 –memory 则设置不生效,使用默认设置)。–memory-swap -1
[root@ok188 ~]# docker run -it -d -m 500m --memory-swap -1 --name limit_memory_2 centos:7 /bin/bash
指定限制内存大小500Mb并且设置 memory-swap 值400Mb当压测值是900Mb时,则容器中的进程会被直接 OOM kill。
[root@ok188 ~]# docker run -it -d -m 500m --memory-swap 400m --name limit_memory_3 centos:7 /bin/bash
备注:实际生产环境不推荐使用swap功能,建议直接禁用swap功能。
正常情况不添加 –oom-kill-disable 容器程序内存使用超过限制后则会直接 OOM kill,加上之后则达到限制内存之后也不会被 kill。
[root@ok188 ~]# docker run -it -d -m 500m --oom-kill-disable --name limit_memory_4 centos:7 /bin/bash
==注意如果是以下的这种没有对容器作任何资源限制的情况,添加 –oom-kill-disable 选项就比较危险了:==
[root@ok188 ~]# docker run -it -d --oom-kill-disable --name limit_memory_5 centos:7 /bin/bash
因为此时容器内存没有限制,使用的上限值是物理内存的上限值。 并且不会被 oom kill,此时系统则会 kill 系统进程用于释放内存。
后记:目前 Docker 支持的资源限制选项
附Docker容器限制源码路径:
github.com/opencontainers/runc/libcontainer/cgroups/fs