Docker的系统资源限制及验证

1 、限制容器的资源

默认情况下,容器没有资源限制 ,可以使用主机内核调度程序允许的尽可能多的给定资源。 Docker 提供了控制容器可以使用多少内存或 CPU 的方法 ,设置 docker run 命令的运行时配置标志。本篇提供有关何时应设置此类限制的详细信息以及设置这些限制的可能含义。

其中许多功能都要求您的内核支持 Linux 功能 。要检查支持,可以使用该 docker info 命令。如果内核中禁用了某项功能,您可能会在输出结尾处看到一条警告,如下所示: WARNING: No swap limit support ,请参阅操作系统的文档以启用它们,了解更多 

[root@along ~]# docker info

Containers: 0

Running: 0

Paused: 0

Stopped: 0

Images: 43

Server Version: 17.03.2-ce

Storage Driver: overlay

Backing Filesystem: xfs

Supports d_type: true

Logging Driver: json-file

Cgroup Driver: cgroupfs

Plugins:

Volume: local

Network: bridge host macvlan null overlay

Swarm: inactive

Runtimes: runc

Default Runtime: runc

Init Binary: docker-init

containerd version: 4ab9917febca54791c5f071a9d1f404867857fcc

runc version: 54296cf40ad8143b62dbcaa1d90e520a2136ddfe

init version: 949e6fa

Security Options:

seccomp

  Profile: default

Kernel Version: 3.10.0-514.el7.x86_64

Operating System: CentOS Linux 7 (Core)

OSType: linux

Architecture: x86_64

CPUs: 1

Total Memory: 976.5 MiB

Name: along

ID: KGWA:GDGT:PTK7:PAX4:A3JZ:LV6N:U5QD:UQCY:AGZV:P32E:V73T:JJHR

Docker Root Dir: /var/lib/docker

Debug Mode (client): false

Debug Mode (server): false

Username: alongedu

Registry: https://index.docker.io/v1/

Experimental: false

Insecure Registries:

docker2:80

127.0.0.0/8

Registry Mirrors:

https://registry.docker-cn.com

Live Restore Enabled: false

2 、内存

2.1 内存不足的风险

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

Docker 尝试通过调整 Docker 守护程序上的OOM 优先级 来降低这些风险,以便它比系统上的其他进程更不可能被杀死 。容器上的 OOM 优先级未调整。这使得单个容器被杀死的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大。您不应试图通过--oom-score-adj在守护程序或容器上手动设置为极端负数或通过设置容器来绕过这些安全措施--oom-kill-disable

有关 Linux 内核的 OOM 管理的更多信息,请参阅内存不足管理 

您可以通过以下方式降低 OOME 导致系统不稳定的风险:

在将应用程序投入生产之前,请执行测试 以了解应用程序的内存要求。

确保您的应用程序仅在具有足够资源 的主机上运行。

限制容器可以使用的内存量 ,如下所述。

在 Docker 主机上配置交换时要小心。交换比内存更慢且性能更低,但可以提供缓冲 以防止系统内存耗尽。

  考虑将容器转换为 服务 ,并使用服务级别约束和节点标签来确保应用程序仅在具有足够内存的主机上运行

2.2 限制容器对内存设有的设置

Docker 可以强制执行硬内存限制 ,允许容器使用不超过给定数量的用户或系统内存或软限制,这允许容器使用尽可能多的内存,除非满足某些条件,例如内核检测到主机上的低内存或争用。当单独使用或设置了多个选项时,其中一些选项会产生不同的效果。

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

选项描述

-m or --memory=容器可以使用的最大内存量。如果设置此选项,则允许的最小值为4m

--memory-swap*允许此容器交换到磁盘的内存量。

--memory-swappiness默认情况下,主机内核可以交换容器使用的匿名页面的百分比。您可以设置--memory-swappiness0 到 100 之间的值,以调整此百分比。

--memory-reservation允许您指定小于软件限制的软限制--memory,当 Docker 检测到主机上的争用或内存不足时,该限制将被激活。如果使用--memory-reservation,则必须将其设置为低于--memory优先级。因为它是软限制,所以不保证容器不超过限制。

--kernel-memory容器可以使用的最大内核内存量。允许的最小值是4m。由于内核内存无法换出,因此内核内存不足的容器可能会阻塞主机资源,这可能会对主机和其他容器产生副作用。

--oom-kill-disable默认情况下,如果发生内存不足( OOM )错误,内核会终止容器中的进程。要更改此行为,请使用该--oom-kill-disable选项。仅在已设置-m/--memory选项的容器上禁用 OOM 杀手。如果-m未设置该标志,则主机可能会耗尽内存,并且内核可能需要终止主机系统的进程才能释放内存。

2.2.1 --memory-swap 设置

( 1 )介绍

--memory-swap 是一个修饰符标志,只有在 --memory 设置时才有意义。使用 swap允许容器在容器耗尽可用的所有 RAM 时将多余的内存需求写入磁盘。对于经常将内存交换到磁盘的应用程序,性能会受到影响。

( 2 )它的设置会产生复杂的效果:

如果 --memory-swap 设置为正整数 ,那么这两个 --memory 和 --memory-swap必须设置。--memory-swap 表示可以使用的 memory and swap ,并--memory控制非交换内存 ( 物理内存 ) 使用的量。所以如果 --memory="300m" 和 --memory-swap="1g" ,容器可以使用 300 米的内存和 700 米(1g - 300m) swap 。

如果--memory-swap 设置为 0 ,则忽略该设置 ,并将该值视为未设置。

如果--memory-swap 设置为与值相同的值 --memory ,并且--memory 设置为正整数 ,则容器无权访问 swap 。请参考下面阻止容器使用交换。

如果--memory-swap 未设置 --memory 设置 ,则容器可以使用两倍于 --memory 设置 的 swap ,主机容器需要配置有 swap 。例如,如果设置 --memory="300m" 和 --memory-swap 未设置,容器可以使用 300 米的内存和 600 米的 swap。

如果--memory-swap 明确设置为 -1 ,则允许容器使用无限制 swap ,最多可达宿主机系统上可用的数量 

在容器内部,工具如 free 报告主机的 swap ,而不是容器内真正可用的内存。不要依赖 free 或类似工具来确定是否存在 swap 。

( 3 )防止容器使用交换

如果--memory 和 --memory-swap 设置为相同的值 ,则可以防止容器使用 swap。这是因为 --memory-swap 可以使用的 memory and swap ,而 --memory 只是可以使用的物理内存量。

2.2.2 --memory-swappiness 设置

  值为 0 将关闭匿名页面交换。

  值 100 将所有匿名页面设置为可交换。

  默认情况下,如果未设置 --memory-swappiness ,则值将从主机继承。

2.2.3 --kernel-memory 设置

( 1 )介绍

内核内存限制以分配给容器的总内存表示。请考虑以下方案:

无限内存,无限内核内存 :这是默认设置。

无限内存,有限的内核内存 :当所有 cgroup 所需的内存量大于主机上实际存在的内存量时,这是合适的。您可以将内核内存配置为永远不会覆盖主机上可用的内容,而需要更多内存的容器需要等待它。

有限的内存,无限的内核内存 :整体内存有限,但内核内存不受限制。

有限的内存,有限的内核内存 :限制用户和内核内存对于调试与内存相关的问题非常有用。如果容器使用意外数量的任一类型的内存,则内存不足而不会影响其他容器或主机。在此设置中,如果内核内存限制低于用户内存限制,则内核内存不足会导致容器遇到 OOM 错误。如果内核内存限制高于用户内存限制,则内核限制不会导致容器遇到 OOM 。

当您打开任何内核内存限制时,主机会根据每个进程跟踪 “ 高水位线 ” 统计信息,因此您可以跟踪哪些进程(在本例中为容器)正在使用多余的内存。通过 /proc/<PID>/status 在主机上查看,可以在每个过程中看到这一点。

3 、 CPU

默认情况下,每个容器对主机 CPU 周期的访问权限是不受限制的 

  您可以设置各种约束来限制给定容器访问主机的 CPU 周期。

  大多数用户使用和配置 默认 CFS 调度程序。

  在 Docker 1.13 及更高版本中,您还可以配置 实时调度程序。

3.1 配置默认 CFS 调度程序

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

选项描述

--cpus=<value>指定容器可以使用的可用 CPU 资源量 。例如,如果主机有两个 CPU 并且你已设置 --cpus="1.5" ,则容器最多保证一个半 CPU 。这相当于设置 --cpu-period="100000"和 --cpu-quota="150000" 。可在 Docker 1.13 及更高版本中使用。

--cpu-period=<value>指定 CPU CFS 调度程序周期,它与并用  --cpu-quota。默认为 100 微秒。大多数用户不会更改默认设置。如果您使用 Docker 1.13 或更高版本,请 --cpus 使用。

--cpu-quota=<value>对容器施加 CPU CFS 配额。 --cpu-period 限制前容器限制为每秒的微秒数。作为有效上限。如果您使用 Docker 1.13 或更高版本,请 --cpus 改用。

--cpuset-cpus限制容器可以使用的特定 CPU 或核心 。如果您有多个CPU ,则容器可以使用逗号分隔列表或连字符分隔的 CPU 范围。第一个 CPU 编号为 0. 有效值可能是 0-3 (使用第一个,第二个,第三个和第四个 CPU )或 1,3 (使用第二个和第四个 CPU )。

--cpu-shares将此标志设置为大于或小于默认值 1024 的值,以增加或减少容器的重量,并使其可以访问主机的 CPU 周期的较大或较小比例。仅在 CPU 周期受限时才会强制执行此操作。当有足够的 CPU 周期时,所有容器都会根据需要使用尽可能多的 CPU 。这样,这是一个软限制。 --cpu-shares 不会阻止容器以群集模式进行调度。它为可用的 CPU 周期优先考虑容器 CPU 资源。它不保证或保留任何特定的 CPU 访问权限。

4 、操作演示

4.1 准备工作

( 1 )先查询宿主机的资源:

[root@docker ~]# lscpu  CPU资源

Architecture:          x86_64

CPU op-mode(s):        32-bit, 64-bit

Byte Order:            Little Endian

CPU(s):                4

On-line CPU(s) list:  0-3

Thread(s) per core:    1

Core(s) per socket:    1

Socket(s):            4

NUMA node(s):          1

Vendor ID:            GenuineIntel

CPU family:            6

Model:                60

Model name:            Intel(R) Xeon(R) CPU E3-1231 v3 @ 3.40GHz

Stepping:              3

CPU MHz:              3395.854

BogoMIPS:              6792.17

Hypervisor vendor:    VMware

Virtualization type:  full

L1d cache:            32K

L1i cache:            32K

L2 cache:              256K

L3 cache:              8192K

NUMA node0 CPU(s):    0-3

[root@docker ~]# free -h  内存、swap资源

              total        used        free      shared  buff/cache  available

Mem:          7.8G        193M        7.2G        8.6M        438M        7.3G

Swap:          2.0G        400K        2.0G

( 2 )在 dockerhub 下载一个用于压测的镜像

[root@docker ~]# docker pull lorel/docker-stress-ng

( 3 )该压测镜像的使用方法

[root@docker ~]# docker run --name stress --rm lorel/docker-stress-ng:latest stress --help

使用 --help 可以查询此压测镜像的用法

例:

stress-ng --cpu 8 --io 4 --vm 2 --vm-bytes 128M --fork 4 --timeout 10s

语法:

 -c N, --cpu N 启动 N 个子进程( cpu )

 --vm N 启动 N 个进程对内存进行压测

 --vm-bytes 128M 每个子进程使用多少内存(默认 256M )

4.2 测试内存限制

( 1 )现在最大使用内存启动容器

[root@docker ~]# docker run --name stress --rm -m 256m lorel/docker-stress-ng:latest stress --vm 2

stress-ng: info: [1] defaulting to a 86400 second run per stressor

stress-ng: info: [1] dispatching hogs: 2 vm

[root@docker ~]# docker stats stress

CONTAINER ID        NAME                CPU %              MEM USAGE / LIMIT  MEM %              NET I/O            BLOCK I/O          PIDS

e1fdb0520bad        stress              8.22%              254MiB / 256MiB    99.22%              648B / 0B          46.9MB / 3.63GB    5

注释:

 -m 256m 限制此容器最大只能使用 256m 内存;

 --vm 2    启动压测容器,使用 256x2=512m 的内存;

 docker stats 结果查询,容器实际使用内存不能超过 256m

4.3 测试 CPU 限制

( 1 )限制最大使用 2 核 CPU

[root@docker ~]# docker run --name stress --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8

stress-ng: info: [1] defaulting to a 86400 second run per stressor

stress-ng: info: [1] dispatching hogs: 8 cpu

[root@docker ~]# docker stats stress

CONTAINER ID        NAME                CPU %              MEM USAGE / LIMIT    MEM %              NET I/O            BLOCK I/O          PIDS

ca86c0de6431        stress              199.85%            15.81MiB / 7.781GiB  0.20%              648B / 0B          0B / 0B            9

( 2 )不限制使用 CPU 核数

[root@docker ~]# docker run --name stress --rm lorel/docker-stress-ng:latest stress --cpu 8

stress-ng: info: [1] defaulting to a 86400 second run per stressor

stress-ng: info: [1] dispatching hogs: 8 cpu

[root@docker ~]# docker stats stress

CONTAINER ID        NAME                CPU %              MEM USAGE / LIMIT    MEM %              NET I/O            BLOCK I/O          PIDS

167afeac7c97        stress              399.44%            15.81MiB / 7.781GiB  0.20%              508B / 0B          0B / 0B            9

欢迎欢迎学Java的朋友们加入java架构交流: 855835163

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

推荐阅读更多精彩内容