docker容器资源限制:限制容器对内存/CPU的访问

一.系统环境

服务器版本 docker软件版本 CPU架构
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 x86_64

二.前言

默认情况下,容器没有资源限制,并且可以使用主机内核调度程序允许的尽可能多的给定资源。Docker 提供了控制容器可以使用多少内存或 CPU 的方法。

三.docker对于CPU和内存的限制

3.1 限制容器对内存的访问

重要的是不要让正在运行的容器消耗过多的主机内存。在 Linux 主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它会抛出一个OOME或 Out Of Memory Exception,并开始杀死进程以释放内存。任何进程都可能被杀死,包括 Docker 和其他重要的应用程序。如果错误的进程被杀死,这可以会导致关闭整个系统。

Docker 可以强制执行内存硬限制,允许容器使用不超过给定数量的用户或系统内存,或软限制,允许容器根据需要使用尽可能多的内存,除非满足某些条件,例如何时内核检测到主机内存不足或争用。其中一些选项在单独使用或设置多个选项时具有不同的效果。这些选项中的大多数采用正整数,后跟b, k, m, g, 后缀来表示字节、千字节、兆字节或千兆字节。

常用的参数有

参数 参数解释
-m或者–memory= 容器可以使用的最大内存量。如果设置此选项,则允许的最小值为6m(6 兆字节)。也就是说,您必须将该值设置为至少 6 兆字节。
–memory-swap* 允许此容器交换到磁盘的内存量。
–memory-swappiness 默认情况下,主机内核可以换出容器使用的一定百分比的匿名页面。您可以设置–memory-swappiness为 0 到 100 之间的值,以调整此百分比。
–memory-reservation 允许您指定一个小于–memory在 Docker 检测到主机上的争用或内存不足时激活的软限制。如果使用–memory-reservation,则必须将其设置为低于–memory它才能优先。因为是软限制,所以不保证容器不超过限制。
–kernel-memory 容器可以使用的最大内核内存量。允许的最小值是4m。因为内核内存不能被换出,内核内存不足的容器可能会阻塞主机资源,这会对主机和其他容器产生副作用。
–oom-kill-disable 默认情况下,如果发生内存不足 (OOM) 错误,内核会终止容器中的进程。要更改此行为,请使用该–oom-kill-disable选项。仅在您还设置了该-m/–memory选项的容器上禁用 OOM kill。如果-m未设置该标志,主机可能会耗尽内存,内核可能需要终止主机系统的进程以释放内存。

本次主要使用hub.c.163.com/library/centos:latest镜像测试,没有该镜像需要自己docker pull一下

[root@k8smaster harbor]# docker images
REPOSITORY                                                        TAG        IMAGE ID       CREATED         SIZE
hub.c.163.com/library/centos                                      latest     328edcd84f1b   4 years ago     193MB

上传memload-7.0-1.r29766.x86_64.rpm安装包,用于模拟内存的使用

[root@k8smaster ~]# mkdir /memload

[root@k8smaster ~]# mv memload-7.0-1.r29766.x86_64.rpm /memload/

使用centos创建一个临时容器,-v /memload:/memload进行数据卷挂载,使容器/memload目录下有memload-7.0-1.r29766.x86_64.rpm安装包

[root@k8smaster ~]# docker run -it --rm -v /memload:/memload hub.c.163.com/library/centos:latest

#在容器里安装memload
[root@3c9083a7b318 /]# rpm -ivh /memload/memload-7.0-1.r29766.x86_64.rpm 
Preparing...                          ################################# [100%]
Updating / installing...
   1:memload-7.0-1.r29766             ################################# [100%]

#memload 1000使内存使用1000M
[root@3c9083a7b318 /]# memload 1000
Attempting to allocate 1000 Mebibytes of resident memory...
^C

[root@3c9083a7b318 /]# exit
exit

当在容器执行memload 1000时,打开另外一个Linux终端,使用docker stats查看该容器的内存使用,发现此容器使用了1013MiB内存,3.32GiB表示物理机的最大内存

CONTAINER ID   NAME                                                                                                            CPU %     MEM USAGE / LIMIT    MEM %     NET I/O           BLOCK I/O         PIDS
3c9083a7b318   friendly_roentgen                                                                                               100.71%   1013MiB / 3.32GiB    29.79%    648B / 0B         32.5MB / 19.5MB   2

使用-m对容器的内存进行限制,-m 512m:表示容器内存最大使用512M,这时如果我们执行memload 1000,使用docker stats查看,最大内存也只能是512M

[root@k8smaster ~]# docker run -it --rm -m 512m -v /memload:/memload hub.c.163.com/library/centos:latest
[root@a81686360ba1 /]# 
[root@a81686360ba1 /]# exit
exit

3.2 限制容器对CPU的访问

默认情况下,每个容器对主机 CPU 周期的访问是无限制的。您可以设置各种约束来限制给定容器对主机 CPU 周期的访问。大多数用户使用和配置 默认的 CFS 调度程序。您还可以配置实时调度程序。

CFS 是用于普通 Linux 进程的 Linux 内核 CPU 调度程序。几个运行时标志允许您配置对容器拥有的 CPU 资源的访问量。当您使用这些设置时,Docker 会修改主机上容器的 cgroup 的设置。

常用的参数有

参数 参数解释
–cpus= 指定容器可以使用多少可用 CPU 资源。例如,如果主机有两个 CPU,并且您设置–cpus=”1.5″了 ,则容器最多可以保证一个半的 CPU。这相当于设置–cpu-period=”100000″和–cpu-quota=”150000″。
–cpu-period= 指定 CPU CFS 调度程序周期,它与 –cpu-quota. 默认为 100000 微秒(100 毫秒)。大多数用户不会更改默认设置。对于大多数用例,–cpus是一种更方便的选择。
–cpu-quota= 对容器施加 CPU CFS 配额。–cpu-period容器在被限制之前被限制的每微秒数。因此充当有效上限。对于大多数用例,–cpus是一种更方便的选择。
–cpuset-cpus 限制容器可以使用的特定 CPU 或内核。如果您有多个 CPU,则容器可以使用的逗号分隔列表或连字符分隔的 CPU 范围。第一个 CPU 编号为 0。有效值可能是0-3(使用第一个、第二个、第三个和第四个 CPU)或1,3(使用第二个和第四个 CPU)。
–cpu-shares 将此标志设置为大于或小于默认值 1024 的值,以增加或减少容器的重量,并允许它访问或多或少比例的主机 CPU 周期。这仅在 CPU 周期受到限制时才会强制执行。当有足够多的 CPU 周期可用时,所有容器都会根据需要使用尽可能多的 CPU。这样,这是一个软限制。–cpu-shares不会阻止容器以 swarm 模式调度。它优先考虑可用 CPU 周期的容器 CPU 资源。它不保证或保留任何特定的 CPU 访问权限。

对CPU的限制一般是设置CPU的亲和性,查看cpu信息:两个CPU:On-line CPU(s) list: 0,1

[root@k8smaster ~]# lscpu 
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                2
On-line CPU(s) list:   0,1
Thread(s) per core:    1
Core(s) per socket:    2
座:                 1
NUMA 节点:         1
厂商 ID:           GenuineIntel
CPU 系列:          6
型号:              142
型号名称:        Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
步进:              10
CPU MHz:             1991.381
BogoMIPS:            3984.01
超管理器厂商:  VMware
虚拟化类型:     完全
L1d 缓存:          32K
L1i 缓存:          32K
L2 缓存:           256K
L3 缓存:           8192K
NUMA 节点0 CPU:    0,1
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid mpx rdseed adx smap clflushopt xsaveopt xsavec arat

使用centos镜像创建容器,里面运行5个cat进程

[root@k8smaster ~]# docker run -it --rm  -v /memload:/memload hub.c.163.com/library/centos:latest
[root@1a4fff793014 /]# 
[root@1a4fff793014 /]# cat /dev/zero > /dev/null &
[1] 15
[root@1a4fff793014 /]# cat /dev/zero > /dev/null &
[2] 16
[root@1a4fff793014 /]# cat /dev/zero > /dev/null &
[3] 17
[root@1a4fff793014 /]# cat /dev/zero > /dev/null &
[4] 18
[root@1a4fff793014 /]# cat /dev/zero > /dev/null &
[5] 19
[root@1a4fff793014 /]# exit 
exit

当在容器执行5个cat进程之后,打开另外一个Linux终端,查看每个cat进程运行在哪个CPU上,可以看到cat进程都运行在0号和1号CPU上

[root@k8smaster ~]# ps mo pid,comm,psr `pgrep cat`
   PID COMMAND         PSR
130120 cat               -
     - -                 0
130143 cat               -
     - -                 1
130144 cat               -
     - -                 0
130145 cat               -
     - -                 1
130146 cat               -
     - -                 1

–cpuset-cpus=0 设置容器里的进程都运行在0号CPU上

[root@k8smaster ~]# docker run -it --rm  --cpuset-cpus=0 -v /memload:/memload hub.c.163.com/library/centos:latest
[root@0c3b37f8e679 /]# cat /dev/zero > /dev/null &
[1] 15
[root@0c3b37f8e679 /]# cat /dev/zero > /dev/null &
[2] 16
[root@0c3b37f8e679 /]# cat /dev/zero > /dev/null &
[3] 17
[root@0c3b37f8e679 /]# cat /dev/zero > /dev/null &
[4] 18
[root@0c3b37f8e679 /]# cat /dev/zero > /dev/null &
[5] 19
[root@0c3b37f8e679 /]# 
[root@0c3b37f8e679 /]# exit
exit

–cpuset-cpus=1 设置容器里的进程都运行在1号CPU上

[root@k8smaster ~]# docker run -it --rm  --cpuset-cpus=1 -v /memload:/memload hub.c.163.com/library/centos:latest
[root@dac513abce35 /]# 
[root@dac513abce35 /]# cat /dev/zero > /dev/null &
[1] 15
[root@dac513abce35 /]# cat /dev/zero > /dev/null &
[2] 16
[root@dac513abce35 /]# 
[root@dac513abce35 /]# cat /dev/zero > /dev/null &
[3] 17
[root@dac513abce35 /]# cat /dev/zero > /dev/null &
[4] 18
[root@dac513abce35 /]# exit
exit

–cpuset-cpus=0,1 设置容器里的进程都运行在0,1号CPU上

[root@k8smaster ~]# docker run -it --rm  --cpuset-cpus=0,1 -v /memload:/memload hub.c.163.com/library/centos:latest
[root@244fe7eb8707 /]# cat /dev/zero > /dev/null &
[1] 15
[root@244fe7eb8707 /]# cat /dev/zero > /dev/null &
[2] 16
[root@244fe7eb8707 /]# cat /dev/zero > /dev/null &
[3] 17
[root@244fe7eb8707 /]# cat /dev/zero > /dev/null &
[4] 18
[root@244fe7eb8707 /]# cat /dev/zero > /dev/null &
[5] 19
[root@244fe7eb8707 /]# 
[root@244fe7eb8707 /]# exit
exit

–cpuset-cpus=0-7,14 设置容器里的进程都运行在0-7,14号CPU上,由于没有那么多CPU所以报错

[root@k8smaster ~]# docker run -it --rm  --cpuset-cpus=0-7,14 -v /memload:/memload hub.c.163.com/library/centos:latest
docker: Error response from daemon: Requested CPUs are not available - requested 0-7,14, available: 0-1.
See 'docker run --help'.

注意:还可以设置容器对NVIDIA GPU 的访问,不过目前暂时没用到。