7 Docker 资源限制

7.1 Docker 资源限制

7.1.1 容器资源限制介绍

官方文档: https://docs.docker.com/config/containers/resource_constraints/

默认情况下,容器没有资源的使用限制,可以使用主机内核调度程序允许的尽可能多的资源

Docker 提供了控制容器使用资源的方法,可以限制容器使用多少内存或 CPU等, 在docker run 命令的运行时配置标志实现资源限制功能。

其中许多功能都要求宿主机的内核支持,要检查是否支持这些功能,可以使用 docker info 命令 ,如果内核中的某项特性可能会在输出结尾处看到警告, 如下所示:

WARNING: No swap limit support

可通过修改内核参数消除以上警告

官方文档: https://docs.docker.com/install/linux/linux-postinstall/#your-kernel-does-not-support-cgroup-swap-limit-capabilities

范例: 修改内核参数消除以上警告

[root@ubuntu1804 ~]#docker info 
Client:
 Debug Mode: false
Server:
 Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
 Images: 1
 Server Version: 19.03.5
 Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
 Volume: local
 Network: bridge host ipvlan macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk 
syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: b34a5c8af56e510852c35414db4c1f4fa6172339
 runc version: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
 init version: fec3683
 Security Options:
 apparmor
 seccomp
   Profile: default
 Kernel Version: 4.15.0-29-generic
 Operating System: Ubuntu 18.04.1 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 1
 Total Memory: 962MiB
 Name: ubuntu1804.test.org
 ID: 4LUN:CS4Y:SGYQ:ZAXU:M7C5:JYSB:ZFTO:JZ6D:CD7Q:TOVD:JBFS:AQM3
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  10.0.0.102
  127.0.0.0/8
 Registry Mirrors:
 https://si7y70hh.mirror.aliyuncs.com/
 Live Restore Enabled: false
WARNING: No swap limit support

#修改内核参数
[root@ubuntu1804 ~]#vim /etc/default/grub
GRUB_CMDLINE_LINUX="cgroup_enable=memory net.ifnames=0 swapaccount=1"

[root@ubuntu1804 ~]#update-grub
[root@ubuntu1804 ~]#reboot
[root@ubuntu1804 ~]#docker info
Client:
 Debug Mode: false
Server:
 Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
 Images: 1
 Server Version: 19.03.5
 Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
 Volume: local
 Network: bridge host ipvlan macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk 
syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: b34a5c8af56e510852c35414db4c1f4fa6172339
 runc version: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
 init version: fec3683
 Security Options:
 apparmor
 seccomp
   Profile: default
 Kernel Version: 4.15.0-29-generic
 Operating System: Ubuntu 18.04.1 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 1
 Total Memory: 962MiB
 Name: ubuntu1804.test.org
 ID: 4LUN:CS4Y:SGYQ:ZAXU:M7C5:JYSB:ZFTO:JZ6D:CD7Q:TOVD:JBFS:AQM3
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  10.0.0.102
  127.0.0.0/8
 Registry Mirrors:
 https://si7y70hh.mirror.aliyuncs.com/
 Live Restore Enabled: false
[root@ubuntu1804 ~]#

7.2 OOM (Out of Memory Exception)

对于 Linux 主机,如果没有足够的内存来执行其他重要的系统任务,将会抛出OOM (Out of Memory Exception,内存溢出、内存泄漏、内存异常 ),随后系统会开始杀死进程以释放内存, 凡是运行在宿主机的进程都有可能被 kill ,包括 Dockerd和其它的应用程序, 如果重要的系统进程被 Kill,会导致和该进程相关的服务全部宕机。通常越消耗内存比较大的应用越容易被kill,比如: MySQL数据库,Java程序等

范例: OOM发生后的日志信息

image-20240216160735995

产生 OOM 异常时, Dockerd尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死但是容器 的 OOM 优先级未调整, 这使得单个容器被杀死的可能性比 Docker守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动设置-- oom -score-adj为极端负数,或通过在容器上设置 --oom-kill-disable 来绕过这些安全措施

OOM 优先级机制:

linux会为每个进程算一个分数,最终将分数最高的kill

/proc/PID/oom_score_adj 
#范围为 -1000 到 1000,值越高容易被宿主机 kill掉,如果将该值设置为 -1000 ,则进程永远不会被宿主机 kernel kill

/proc/PID/oom_adj 
#范围为 -17 到 +15 ,取值越高越容易被干掉,如果是 -17 , 则表示不能被 kill ,该设置参数的存在是为了和旧版本的 Linux 内核兼容。

/proc/PID/oom_score 
#这个值是系统综合进程的内存消耗量、 CPU 时间 (utime + 、存活时间 (uptime - start time) 和 oom_adj 计算出的进程得分 ,消耗内存越多得分越高,容易被宿主机 kernel 强制杀死

范例: 查看OOM相关值

#按内存排序
[root@ubuntu1804 ~]#top

[root@ubuntu1804 ~]#cat /proc/19674/oom_adj 
0
[root@ubuntu1804 ~]#cat /proc/19674/oom_score
32
[root@ubuntu1804 ~]#cat /proc/19674/oom_score_adj 
0
[root@ubuntu1804 ~]#cat /proc/7108/oom_adj 
0
[root@ubuntu1804 ~]#cat /proc/7108/oom_score
1
[root@ubuntu1804 ~]#cat /proc/7108/oom_score_adj 

#docker服务进程的OOM默认值
[root@ubuntu1804 ~]#cat /proc/`pidof dockerd`/oom_adj
-8

[root@ubuntu1804 ~]#cat /proc/`pidof dockerd`/oom_score
0

[root@ubuntu1804 ~]#cat /proc/`pidof dockerd`/oom_score_adj
-500

7.3 容器的内存限制

Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。

Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了

7.3.1 内存相关选项

官文文档: https://docs.docker.com/config/containers/resource_constraints/

以下设置大部分的选项取正整数,跟着一个后缀 b , k , m , g ,表示字节,千字节,兆字节或千兆字节

选项描述
-m , --memory=容器可以使用的最大物理内存量,硬限制,此选项最小允许值为
4m (4 MB),此项较常用
--memory-swap允许此容器交换到磁盘的内存量,必须先用-m 对内存限制才可以使用,详
细说明如下
--memory-swappiness设置容器使用交换分区的倾向性,值越高表示越倾向于使用swap分
区,范围为0-100,0为能不用就不用,100为能用就用
--memoryreservation允许指定小于 --memory 的软限制 ,当 Docker 检测到主机上的争用
或内存不足时会激活该限制,如果使-- memory-reservation,则必须
将其设置为低于 --memory 才能使其优先生效。 因为它是软限制,所
以不能保证容器不超过限制
--kernel-memory容器可以使用的最大内核内存量,最小为 4m,由于内核内存与用户空
间内存隔离,因此无法与用户空间内存直接交换,因此内核内存不足的
容器可能会阻塞宿主机资源,这会对主机和其他容器或者其他服务进程
产生影响,因此不建议设置内核内存大小
--oom-kill-disable默认情况下,如果发生内存不足(OOM)错误,则内核将终止容器中
的进程。要更改此行为,请使用该 --oom-kill-disable 选项。仅在设
置了该 -m/--memory 选项的容器上禁用OOM。如果 -m 未设置该标志,
则主机可能会用完内存,内核可能需要终止主机系统的进程以释放内存

范例:

[root@ubuntu1804 ~]#docker run -e MYSQL_ROOT_PASSWORD=123456 -it --rm -m 1g --oom-kill-disable mysql:5.7.29
2020-02-04 13:11:54+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.29-1debian9 started.
2020-02-04 13:11:54+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2020-02-04 13:11:54+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.29-1debian9 started.
2020-02-04 13:11:54+00:00 [Note] [Entrypoint]: Initializing database files......
Version: '5.7.29' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)

范例:

[root@ubuntu1804 ~]#sysctl -a |grep swappiness
sysctl: reading key "net.ipv6.conf.all.stable_secret"
sysctl: reading key "net.ipv6.conf.default.stable_secret"
sysctl: reading key "net.ipv6.conf.docker0.stable_secret"
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
vm.swappiness = 60

7.4 swap限制

--memory-swap #只有在设置了 --memory 后才会有意义。使用 Swap,可以让容器将超出限制部分的内存置换到磁盘上,WARNING: 经常将内存交换到磁盘的应用程序会降低性能

不同的--memory-swap 设置会产生不同的效果:

--memoryswap--memory功能
正数S正数M容器可用内存总空间为S,其中ram为M,swap为 S-M,若S=M,则无可用swap资源
0正数M相当于未设置swap(unset)
unset正数M若主机(Docker Host) 启用于swap , 则容器的可用swap 为2*M
-1正数M若主机(Docker Host)启用了swap ,则容器可使用最大至主机上所有swap空间
-memory-swap #值为正数, 那么--memory 和--memory-swap 都必须要设置,--memory-swap 表示你能使用的内存和 swap 分区大小的总和,例如:   --memory=300m, --memory-swap=1g, 那么该容器能够使用 300m 物理内存和 700m swap,即--memory 是实际物理内存大小值不变,而 swap 的实际大小计算方式为(--memory-swap)-(--memory)=容器可用 swap

--memory-swap #如果设置为 0,则忽略该设置,并将该值视为未设置,即未设置交换分区

--memory-swap #如果等于--memory 的值,并且--memory 设置为正整数,容器无权访问 swap 

-memory-swap #如果未设置,如果宿主机开启了 swap,则实际容器的swap 值最大为 2x( --memory),即两倍于物理内存大小,例如,如果--memory="300m"与--memory-swap没有设置,该容器可以使用300m总的内存和600m交撒空间,但是并不准确(在容器中使用free 命令所看到的 swap 空间并不精确,毕竟每个容器都可以看到具体大小,宿主机的 swap 是有上限的,而且不是所有容器看到的累计大小)

--memory-swap #如果设置为-1,如果宿主机开启了 swap,则容器可以使用主机上 swap 的最大空间

注意: 在容器中执行free命令看到的是宿主机的内存和swap使用,而非容器自身的swap使用情况

范例: 在容器中查看内存

[root@ubuntu1804 ~]#free 
             total       used       free     shared buff/cache   available
Mem:        3049484      278484     1352932       10384     1418068     2598932
Swap:       1951740           0     1951740

[root@ubuntu1804 ~]#docker run -it --rm -m 2G centos:centos7.7.1908 bash

[root@f5d387b5022f /]# free 
             total       used       free     shared buff/cache   available
Mem:        3049484      310312     1320884       10544     1418288     2566872
Swap:       1951740           0     1951740
[root@f5d387b5022f /]#

7.5 stress-ng 压力测试工具

image-20240216160750480

stress-ng是一个压力测试工具,可以通过软件仓库进行安装,也提供了docker版本的容器

Explore Docker's Container Image Repository | Docker Hub

image-20240216160800374

范例: 软件包方式安装

[root@centos7 ~]#yum -y install stress-ng
[root@ubuntu1804 ~]#apt -y install stress-ng

范例: 容器方式安装

[root@ubuntu1804 ~]#docker pull lorel/docker-stress-ng
Using default tag: latest
latest: Pulling from lorel/docker-stress-ng
Image docker.io/lorel/docker-stress-ng:latest uses outdated schema1 manifest 
format. Please upgrade to a schema2 image for better future compatibility. More 
information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
c52e3ed763ff: Pull complete 
a3ed95caeb02: Pull complete 
7f831269c70e: Pull complete 
Digest: sha256:c8776b750869e274b340f8e8eb9a7d8fb2472edd5b25ff5b7d55728bca681322
Status: Downloaded newer image for lorel/docker-stress-ng:latest
docker.io/lorel/docker-stress-ng:latest


[root@ubuntu1804 ~]#docker images 
REPOSITORY               TAG                 IMAGE ID           CREATED         SIZE
lorel/docker-stress-ng   latest             1ae56ccafe55        3 years ago     8.1MB

假如一个容器未做内存使用限制,则该容器可以利用到系统内存最大空间,默认创建的容器没有做内存资源限制。

范例: 默认一个workers 分配256M内存,2个即占512M内存

[root@ubuntu1804 ~]#docker run --name c1 -it --rm lorel/docker-stress-ng --vm 2 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

#因上一个命令是前台执行,下面在另一个终端窗口中执行,可以看到占用512M左右内存
[root@ubuntu1804 ~]#docker stats 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
fd184869ff7e       c1                  91.00%              524.3MiB / 962MiB   
54.50%             766B / 0B           860kB / 0B          5

范例: 指定内存最大值

[root@ubuntu1804 ~]#docker run --name c1 -it --rm -m 300m lorel/docker-stress-ng --vm 2 
WARNING: Your kernel does not support swap limit capabilities or the cgroup is 
not mounted. Memory limited without swap.
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

[root@ubuntu1804 ~]#vim /etc/default/grub
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1 net.ifnames=0"

[root@ubuntu1804 ~]#update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.0-29-generic
Found initrd image: /boot/initrd.img-4.15.0-29-generic
done

[root@ubuntu1804 ~]#reboot

[root@ubuntu1804 ~]#docker run --name c1 -it --rm -m 300m lorel/docker-stress-ng --vm 2 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

#在另一个终端窗口执行
[root@ubuntu1804 ~]#docker stats --no-stream
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
6a93f6b22034       c1                  27.06%              297.2MiB / 300MiB   
99.07%              1.45kB / 0B         4.98GB / 5.44GB     5

范例:

[root@ubuntu1804 ~]#docker run --name c2 -it --rm lorel/docker-stress-ng --vm 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 vm

#一次性查看资源使用情况
[root@ubuntu1804 ~]#docker stats --no-stream
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
fd5fff3c04f7       c2                  21.20%              591.1MiB / 962MiB   
61.45%              1.31kB / 0B         1.07GB / 46.6MB     9
[root@ubuntu1804 ~]#

范例: 容器占用内存造成OOM

[root@ubuntu1804 ~]#docker run -it --rm lorel/docker-stress-ng --vm 6 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 6 vm

#另一个终端窗中同时执行下面命令
[root@ubuntu1804 ~]#docker run -it --rm lorel/docker-stress-ng --vm 6
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 6 vm

[root@ubuntu1804 ~]#docker stats 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
f33cebf5b55d       c2                  --                  -- / --             
--                  --                  --                  --
b14b597c5a4f       cool_banach         --                  -- / --             
--                  --                  --                  --

#观察日志出现OOM现象
[root@ubuntu1804 ~]#tail /var/log/syslog 
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928835] [ 2575]     0  2575    67104   
39218   544768    22906          1000 stress-ng-vm
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928836] [ 2594]     0  2594    67104   
37503   409600     7725          1000 stress-ng-vm
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928837] [ 2601]     0  2601    67104   
38815   438272     9779          1000 stress-ng-vm
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928838] [ 2602]     0  2602     1568   
   861    49152        0          1000 stress-ng-vm
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928839] [ 2610]     0  2610     1568   
   861    49152        0          1000 stress-ng-vm
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928840] [ 2614]     0  2614     1157   
   174    53248        0             0 update-motd-hwe
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928841] [ 2615]     0  2615     3100   
    15    61440        0             0 apt-config
Feb  4 22:59:40 ubuntu1804 kernel: [  785.928842] Out of memory: Kill process 
2570 (stress-ng-vm) score 1090 or sacrifice child
Feb  4 22:59:40 ubuntu1804 kernel: [  785.929493] Killed process 2570 (stressng-vm) total-vm:268416kB, anon-rss:170352kB, file-rss:632kB, shmem-rss:28kB
Feb  4 22:59:40 ubuntu1804 kernel: [  786.018319] oom_reaper: reaped process 
2570 (stress-ng-vm), now anon-rss:0kB, file-rss:0kB, shmem-rss:28kB

范例: 查看内存限制

#启动两个工作进程,每个工作进程最大允许使用内存 256M,且宿主机不限制当前容器最大内存
[root@ubuntu1804 ~]#docker run -it --rm lorel/docker-stress-ng --vm 2
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID       IMAGE                   COMMAND                 CREATED   
          STATUS             PORTS               NAMES
13e46172e1ae       lorel/docker-stress-ng   "/usr/bin/stress-ng …"   24 seconds 
ago     Up 22 seconds                           gallant_moore

[root@ubuntu1804 ~]#ls /sys/fs/cgroup/memory/docker/13e46172e1ae8593569f05a3bebc7b41b7839da44369d43b29102661364ac2cd 
memory.kmem.tcp.limit_in_bytes     memory.numa_stat
cgroup.clone_children                                             
memory.kmem.tcp.max_usage_in_bytes memory.oom_control
cgroup.event_control                                             
memory.kmem.tcp.usage_in_bytes     memory.pressure_level
cgroup.procs                                                     
memory.kmem.usage_in_bytes         memory.soft_limit_in_bytes
memory.failcnt                                                   
memory.limit_in_bytes               memory.stat
memory.force_empty                                               
memory.max_usage_in_bytes           memory.swappiness
memory.kmem.failcnt                                               
memory.memsw.failcnt               memory.usage_in_bytes
memory.kmem.limit_in_bytes                                       
memory.memsw.limit_in_bytes         memory.use_hierarchy
memory.kmem.max_usage_in_bytes                                   
memory.memsw.max_usage_in_bytes     notify_on_release
memory.kmem.slabinfo                                             
memory.memsw.usage_in_bytes         tasks
memory.kmem.tcp.failcnt                                           
memory.move_charge_at_immigrate

[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/13e46172e1ae8593569f05a3bebc7b41b7839da44369d43b29102661364ac2cd/memory.limit_in_bytes
9223372036854771712

[root@ubuntu1804 ~]#echo 2^63|bc
9223372036854775808

范例: 内存限制200m

#宿主机限制容器最大内存使用:   
[root@ubuntu1804 ~]#docker run -it --rm -m 200M lorel/docker-stress-ng --vm 2 --vm-bytes 256M 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
f69729b2acc1       sleepy_haibt        85.71%             198MiB / 200MiB     
98.98%              1.05kB / 0B         697MB / 60.4GB      5
[root@ubuntu1804 ~]#

#查看宿主机基于 cgroup 对容器进行内存资源的大小限制
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/f69729b2acc16e032658a4efdab64d21ff97dcb6746d1cef451ed82d5c98a81f/memory.limit_in_bytes
209715200

[root@ubuntu1804 ~]#echo 209715200/1024/1024|bc
200

#动态修改内存限制
[root@ubuntu1804 ~]#echo 300*1024*1024|bc
314572800

[root@ubuntu1804 ~]#echo 314572800 > /sys/fs/cgroup/memory/docker/f69729b2acc16e032658a4efdab64d21ff97dcb6746d1cef451ed82d5c98a81f/memory.limit_in_bytes

[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/f69729b2acc16e032658a4efdab64d21ff97dcb6746d1cef451ed82d5c98a81f/memory.limit_in_bytes
314572800

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
f69729b2acc1       sleepy_haibt        76.69%              297.9MiB / 300MiB   
99.31%              1.05kB / 0B         1.11GB / 89.1GB     5

#通过 echo 命令可以改内存限制的值,但是可以在原基础之上增大内存限制,缩小内存限制会报错write error: Device or resource busy 
[root@ubuntu1804 ~]#echo 209715200 > /sys/fs/cgroup/memory/docker/f69729b2acc16e032658a4efdab64d21ff97dcb6746d1cef451ed82d5c98a81f/memory.limit_in_bytes
-bash: echo: write error: Device or resource busy

[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/f69729b2acc16e032658a4efdab64d21ff97dcb6746d1cef451ed82d5c98a81f/memory.limit_in_bytes
314572800

范例: 内存大小软限制

[root@ubuntu1804 ~]#docker run -it --rm -m 256m --memory-reservation 128m --name test-c1 lorel/docker-stress-ng --vm 2 --vm-bytes 256M 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
aeb38acde581       test-c1           72.45%              253.9MiB / 256MiB   
99.20%             976B / 0B           9.47GB / 39.4GB     5

#查看硬限制
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/aeb38acde58155d421f998a54e9a99ab60635fe00c9070da050cc49a2f62d274/memory.limit_in_bytes 
268435456

#查看软限制
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/aeb38acde58155d421f998a54e9a99ab60635fe00c9070da050cc49a2f62d274/memory.soft_limit_in_bytes 
134217728

#软限制不能高于硬限制
[root@ubuntu1804 ~]#docker run -it --rm -m 256m --memory-reservation 257m --
name test-c1 lorel/docker-stress-ng --vm 2 --vm-bytes 256M 
docker: Error response from daemon: Minimum memory limit can not be less than 
memory reservation limit, see usage.
See 'docker run --help'.

关闭OOM 机制:

# docker run -it --rm -m 256m --oom-kill-disable --name test-c1 lorel/docker-stress-ng --vm 2 --vm-bytes 256M 
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.oom_control 
oom_kill_disable 1 
under_oom 1 
oom_kill 0 

#查看docker OOM机制默认值
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/memory.oom_control 
oom_kill_disable 0
under_oom 0
oom_kill 0

#启动容器时关闭OOM机制
[root@ubuntu1804 ~]#docker run -it --rm -m 200m --oom-kill-disable lorel/docker-stress-ng --vm 2 --vm-bytes 256M
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
MEM %               NET I/O             BLOCK I/O           PIDS
b655d88228c0       silly_borg          0.00%               197.2MiB / 200MiB   
98.58%              1.31kB / 0B         1.84MB / 484MB      5

[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/b655d88228c04d7db6a6ad833ed3d05d4cd596ef09834382e17942db0295dc0c/memory.oom_control 
oom_kill_disable 1
under_oom 1
oom_kill 0
[root@ubuntu1804 ~]#

交换分区限制:

# docker run -it --rm -m 256m --memory-swap 512m --name demo-c1 centos bash 
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.memsw.limit_in_bytes 536870912 
#返回值

范例:

[root@ubuntu1804 ~]#docker run -it --rm -m 200m --memory-swap 512m lorel/docker-stress-ng --vm 2 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm

#宿主机cgroup验证:   
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/memory/docker/23733a0cafa21f3e94ca8c96110978b12e53076261f1b92fd2052bafe659c8ab/memory.memsw.limit_in_bytes 
536870912

kubernets 对swap的要求

K8s 1.8.3更新日志: 宿主机开启交换分区,会在安装之前的预检查环节提示相应错误信息: https://github.com/kubernetes/kubernetes/blob/release-1.8/CHANGELOG-1.8.md

image-20240216160821562

7.6 容器的CPU限制

7.6.1 容器的CPU限制介绍

官方文档说明: https://docs.docker.com/config/containers/resource_constraints/ 一个宿主机,有几十个核心的CPU,但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务,多进程共用一个 CPU 的核心为可压缩资源,即一个核心的 CPU 可以通过调度而运行多个进程,但是同一个单位时间内只能有一个进程在 CPU 上运行,那么这么多的进程怎么在 CPU 上执行和调度的呢?

Linux kernel 进程的调度基于CFS(Completely Fair Scheduler),完全公平调度

服务器资源密集型

  • CPU 密集型的场景: 优先级越低越好,计算密集型任务的特点是要进行大量的计算,消耗CPU 资源,比如计算圆周率、数据处理、对视频进行高清解码等等,全靠CPU 的运算能力。
  • IO 密集型的场景: 优先级值高点,涉及到网络、磁盘IO 的任务都是IO 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度),比如 Web 应用,高并发,数据量大的动态网站来说,数据库应该为IO 密集型

CFS原理

cfs定义了进程调度的新模型,它给cfs_rq(cfs的run queue)中的每一个进程安排一个虚拟时钟vruntime。如果一个进程得以执行,随着时间的增长,其vruntime将不断增大。没有得到执行的进程vruntime不变, 而调度器总是选择vruntime跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它可能得到更多的运行机会。CFS的意义在于, 在一个混杂着大量计算型进程和IO交互进程的系统中,CFS调度器相对其它调度器在对待IO交互进程要更加友善和公平。

7.6.2 配置默认的CFS调度程序

默认情况下,每个容器对主机的CPU周期的访问都是不受限制的。可以设置各种约束,以限制给定容器对主机CPU周期的访问。大多数用户使用并配置默认的CFS调度程序。在Docker 1.13及更高版本中,还可以配置 realtime scheduler。

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

选项描述
--cpus=指定一个容器可以使用多少个可用的CPU核心资源。例如,如果主机有两个
CPU,如果设置了 --cpus="1.5" ,则可以保证容器最多使用1.5个的CPU(如果
是4核CPU,那么还可以是4核心上每核用一点,但是总计是1.5核心的CPU)。这
相当于设置 --cpu-period="100000" 和 --cpu-quota="150000" 。此设置可在
Docker 1.13及更高版本中可用,目的是替代--cpu-period和--cpu-quota两个参
数,从而使配置更简单,但是最大不能超出宿主机的CPU总核心数(在操作系统
看到的CPU超线程后的数值),此项较常用
--cpu-period=过时选项,指定CPU CFS调度程序周期,必须与 --cpu-quota 一起使用 。默认为
100微秒。大多数用户不会更改默认设置。如果使用Docker 1.13或更高版本,
请改用 --cpus
--cpu-quota=过时选项,在容器上添加 CPU CFS 配额,计算方式为 cpu-quota / cpu-period的
结果值,docker1.13 及以上版本通常使用--cpus 设置此值
--cpusetcpus用于指定容器运行的 CPU 编号,也就是所谓的CPU绑定。如果一个或多个
CPU,则容器可以使用逗号分隔的列表或用连字符分隔的CPU范围。第一个
CPU的编号为0。有效值可能是 0-3 (使用第一,第二,第三和第四CPU)或
1,3 (使用第二和第四CPU)
--cpu-shares用于设置 cfs 中调度的相对最大比例权重,cpu-share 的值越高的容器,将会分
得更多的时间片(宿主机多核 CPU 总数为 100%,假如容器 A 为1024,容器 B
为 2048,那么容器 B 将最大是容器 A 的可用 CPU 的两倍 ),默认的时间片
1024,最大 262144。这是一个软限制。

7.6.3 使用stress-ng测试cpu配置

范例: 查看 stress-n 关于cpu的帮助

[root@ubuntu1804 ~]#docker run -it --rm --name demo-c1 lorel/docker-stressng |grep cpu
-c N, --cpu N            start N workers spinning on sqrt(rand())
       --cpu-ops N        stop when N cpu bogo operations completed
-l P, --cpu-load P       load CPU by P %%, 0=sleep, 100=full load (see -c)
       --cpu-method m     specify stress cpu method m, default is all
Example: stress-ng --cpu 8 --io 4 --vm 2 --vm-bytes 128M --fork 4 --timeout 10s
[root@ubuntu1804 ~]#

范例: 不限制容器CPU

[root@ubuntu1804 ~]#lscpu |grep CPU
CPU op-mode(s):      32-bit, 64-bit
CPU(s):              6
On-line CPU(s) list: 0-5
CPU family:          6
Model name:         Intel(R) Core(TM) i7-4710HQ CPU @ 2.50GHz
CPU MHz:             2494.236
NUMA node0 CPU(s):   0-5

#占用4个CPU资源.但只是平均的使用CPU资源
[root@ubuntu1804 ~]#docker run -it --rm lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
818a85e1da2f       frosty_taussig      595.57%             1.037GiB / 2.908GiB 
  35.64%              1.12kB / 0B         0B / 0B             13
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/cpuset/docker/818a85e1da2f9a4ef297178a9dc09b338b2308108195ad8d4197a1c47febcbff/cpuset.cpus
0-5
[root@ubuntu1804 ~]#top

image-20240216160837389

范例: 限制使用CPU

[root@ubuntu1804 ~]#docker run -it --rm --cpus 1.5 lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
9f8b2e693113       busy_hodgkin        147.71%             786.8MiB / 2.908GiB 
  26.42%             836B / 0B           0B / 0B             13
[root@ubuntu1804 ~]#top

image-20240216160847282

范例: 限制CPU

[root@ubuntu1804 ~]#docker run -it --rm --cpu-quota 2000 --cpu-period 1000 lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME                     CPU %               MEM USAGE / 
LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
bd949bb6698e       affectionate_chebyshev   185.03%             1.037GiB / 
2.908GiB   35.64%             836B / 0B           0B / 0B             13
[root@ubuntu1804 ~]#

范例: 绑定CPU

#一般不建议绑在0号CPU上,因0号CPU一般会较忙
[root@ubuntu1804 ~]#docker run -it --rm --cpus 1.5 --cpuset-cpus 2,4-5 lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
585879094e73       hungry_albattani    154.35%             1.099GiB / 2.908GiB 
  37.79%             906B / 0B           0B / 0B             13
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/cpuset/docker/585879094e7382d2ef700947b4454426eee7f943f8d1438fe42ce34df789227b/cpuset.cpus 2,4-5
[root@ubuntu1804 ~]#top

image-20240216160858907

范例: 多个容器的CPU利用率比例

#同时开两个容器
[root@ubuntu1804 ~]#docker run -it --rm --name c1 --cpu-shares 1000 lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker run -it --rm --name c2 --cpu-shares 500 lorel/docker-stress-ng --cpu 4 
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 4 vm

[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
a1d4c6e6802d       c2                  195.88%             925.3MiB / 2.908GiB 
  31.07%             726B / 0B           0B / 0B             13
d5944104aff4       c1                  398.20%             1.036GiB / 2.908GiB 
  35.64%             906B / 0B           0B / 0B             13
[root@ubuntu1804 ~]#

#查看c1容器的cpu利用比例
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/cpu,cpuacct/docker/d5944104aff40b7b76f536c45a68cd4b98ce466a73416b68819b9643e3f49da7/cpu.shares 
1000

#查看c2容器的cpu利用比例
[root@ubuntu1804 ~]#cat /sys/fs/cgroup/cpu,cpuacct/docker/a1d4c6e6802d1b846b33075f3c1e1696376009e85d9ff8756f9a8d93d3da3ca6/cpu.shares 
500

#再打开新的容器,cpu分配比例会动态调整
[root@ubuntu1804 ~]#docker run -it --rm --name c3 --cpu-shares 2000 lorel/docker-stress-ng --cpu 4 
[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
c2d54818e1fe       c3                  360.15%             664.5MiB / 2.908GiB 
  22.31%             726B / 0B           1.64GB / 150MB      13
a1d4c6e6802d       c2                  82.94%              845.2MiB / 2.908GiB 
  28.38%             936B / 0B           103MB / 4.54MB      13
d5944104aff4       c1                  181.18%             930.1MiB / 2.908GiB 
  31.23%              1.12kB / 0B         303MB / 19.8MB      13

范例: 动态调整cpu shares值

[root@ubuntu1804 ~]#echo 2000 > /sys/fs/cgroup/cpu,cpuacct/docker/a1d4c6e6802d1b846b33075f3c1e1696376009e85d9ff8756f9a8d93d3da3ca6/cpu.shares
[root@ubuntu1804 ~]#docker stats --no-stream 
CONTAINER ID       NAME               CPU %               MEM USAGE / LIMIT   
  MEM %               NET I/O             BLOCK I/O           PIDS
a1d4c6e6802d       c2                  389.31%             1.037GiB / 2.908GiB 
  35.64%              1.01kB / 0B         1.16GB / 14MB       13
d5944104aff4       c1                  200.28%             1.036GiB / 2.908GiB 
  35.63%              1.19kB / 0B         2.66GB / 26.7MB     13
[root@ubuntu1804 ~]#