内存,CPU和GPU的运行时选项

预计阅读时间:16分钟

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

其中许多功能都要求您的内核支持Linux功能。要检查支持,可以使用 docker info命令。如果您的内核中禁用了功能,则可能在输出末尾显示警告,如下所示:

WARNING: No swap limit support

请参阅您操作系统的文档以启用它们。 了解更多

记忆

了解内存不足的风险

重要的是不允许正在运行的容器占用过多的主机内存。在Linux主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它将抛出OOMEOut Of Memory Exception,并开始终止进程​​以释放内存。任何进程都将被杀死,包括Docker和其他重要应用程序。如果杀死错误的进程,则可以有效地使整个系统瘫痪。

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

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

您可以通过以下方法减轻由于OOME引起的系统不稳定的风险:

  • 在将其投入生产之前,请执行测试以了解您的应用程序的内存要求。
  • 确保您的应用程序仅在具有足够资源的主机上运行。
  • 限制容器可以使用的内存量,如下所述。
  • 在Docker主机上配置交换时要小心。交换的速度比内存慢,性能也较差,但可以提供缓冲区以防系统内存耗尽。
  • 考虑将容器转换为服务,并使用服务级别的约束和节点标签以确保应用程序仅在具有足够内存的主机上运行

限制容器对内存的访问

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

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

选项 描述
-m 或者 --memory= 容器可以使用的最大内存量。如果设置此选项,则允许的最小值为4m(4 MB)。
--memory-swap* 允许此容器交换到磁盘的内存量。查看--memory-swap详细信息
--memory-swappiness 默认情况下,主机内核可以换出一定比例的容器使用的匿名页面。您可以设置--memory-swappiness一个介于0到100之间的值来调整此百分比。查看--memory-swappiness详细信息
--memory-reservation 允许您指定一个小于--memoryDocker在主机上检测到争用或内存不足时激活的软限制。如果使用--memory-reservation,则必须将其设置为低于,--memory以使其具有优先权。因为这是一个软限制,所以不能保证容器不超过该限制。
--kernel-memory 容器可以使用的最大内核内存量。允许的最小值是4m。由于无法交换内核内存,因此内核内存不足的容器可能会阻塞主机资源,这可能会对主机和其他容器产生副作用。查看--kernel-memory详细信息
--oom-kill-disable 默认情况下,如果发生内存不足(OOM)错误,则内核将终止容器中的进程。要更改此行为,请使用该--oom-kill-disable选项。仅在还设置了该-m/--memory选项的容器上禁用OOM杀手。如果-m未设置该标志,则主机可能会用完内存,内核可能需要终止主机系统的进程以释放内存。

有关一般cgroup和内存的更多信息,请参见Memory Resource Controller的文档。

--memory-swap 细节

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

其设置可能会产生复杂的影响:

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

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

  • 如果--memory-swap将设置为与相同的值--memory,并且--memory将其设置为正整数,则该容器无权访问swap。请参阅 防止容器使用swap

  • 如果--memory-swap未设置,并且--memory--memory设置,则在主机容器配置了交换内存的情况下,容器可以使用与设置一样多的交换。例如,如果--memory="300m"--memory-swap没有设置,该容器可以在总的内存和交换使用600米。

  • 如果--memory-swap将显式设置为-1,则允许该容器使用无限交换,最高不超过主机系统上可用的数量。

  • 在容器内部,诸如free报告主机的可用交换之类的工具,而不是容器内部可用的工具,都会报告出来。不要依靠free或类似工具的输出来确定是否存在交换。

防止容器使用交换

如果--memory--memory-swap设置为相同的值,这将防止容器使用任何交换。这是因为--memory-swap是可以使用的组合内存和交换--memory的数量,而仅仅是可以使用的物理内存的数量。

--memory-swappiness 细节

  • 值为0将关闭匿名页面交换。
  • 值100会将所有匿名页面设置为可交换。
  • 默认情况下,如果不设置--memory-swappiness,则该值将从主机继承。

--kernel-memory 细节

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

  • 内存无限,内核内存无限:这是默认行为。
  • 无限的内存,有限的内核内存:当所有cgroup所需的内存量大于主机上实际存在的内存量时,此方法适用。您可以将内核内存配置为永不遍历主机上可用的内存,并且需要更多内存的容器需要等待它。
  • 有限的内存,无限的内核内存:整体内存有限,但内核内存不受限制。
  • 内存有限,内核内存有限:限制用户和内核内存对于调试与内存相关的问题很有用。如果某个容器使用了意外数量的任一类型的内存,则它将用完内存而不会影响其他容器或主机。在此设置内,如果内核内存限制低于用户内存限制,则内核内存用完会导致容器出现OOM错误。如果内核内存限制高于用户内存限制,则内核限制不会导致容器出现OOM。

当您打开任何内核内存限制时,主机将按进程跟踪“高水位标记”统计信息,因此您可以跟踪哪些进程(在这种情况下为容器)正在使用过多的内存。通过/proc/<PID>/status在主机上查看,可以按进程查看。

中央处理器

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

配置默认的CFS调度程序

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

选项 描述
--cpus=<value> 指定一个容器可以使用多少可用的CPU资源。例如,如果主机有两个CPU,并且您已设置--cpus="1.5",则可以保证容器最多容纳一半的CPU。这相当于设置--cpu-period="100000"--cpu-quota="150000"
--cpu-period=<value> 指定CPU CFS调度程序周期,该周期与一起使用 --cpu-quota。默认为100000微秒(100毫秒)。大多数用户不会更改默认设置。对于大多数用例,--cpus是一种更方便的替代方法。
--cpu-quota=<value> 在容器上设置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不会阻止以群集模式调度容器。它将容器CPU资源的优先级分配给可用的CPU周期。它不保证或保留任何特定的CPU访问权限。

如果您有1个CPU,则以下每个命令都会保证容器每秒最多占用50%的CPU。

docker run -it --cpus=".5" ubuntu /bin/bash

这等效于手动指定--cpu-period--cpu-quota;

$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash

配置实时调度程序

您可以将容器配置为使用实时调度程序,以执行无法使用CFS调度程序的任务。 在配置Docker守护程序配置单个容器之前,您需要 确保正确配置了主机的内核

警告

CPU调度和优先级划分是高级内核级功能。大多数用户不需要更改其默认值。错误地设置这些值可能会导致您的主机系统变得不稳定或无法使用。

配置主机的内核

CONFIG_RT_GROUP_SCHED通过运行zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED或检查文件是否存在来验证Linux内核中已启用该功能 /sys/fs/cgroup/cpu.rt_runtime_us。有关配置内核实时调度程序的指导,请参阅您的操作系统的文档。

配置Docker守护程序

要使用实时调度程序运行容器,请运行Docker守护程序,并将该--cpu-rt-runtime标志设置为每个运行时间段为实时任务保留的最大微秒数。例如,在默认时间段为1000000微秒(1秒)的情况下,设置--cpu-rt-runtime=950000可确保使用实时调度程序的容器在每1000000微秒的时间段内可以运行950000微秒,从而至少有50000微秒可用于非实时任务。要使该配置在使用的系统上永久存在 systemd,请参阅使用systemd控制和配置Docker

配置单个容器

使用启动容器时,可以传递多个标志来控制容器的CPU优先级docker run。请查阅操作系统的说明文件或ulimit命令以获取有关适当值的信息。

选项 描述
--cap-add=sys_nice 向容器授予该CAP_SYS_NICE功能,该功能允许容器提高过程nice值,设置实时调度策略,设置CPU亲和力以及其他操作。
--cpu-rt-runtime=<value> 容器可以在Docker守护程序的实时调度程序周期内以实时优先级运行的最大微秒数。您还需要--cap-add=sys_nice标志。
--ulimit rtprio=<value> 容器允许的最大实时优先级。您还需要--cap-add=sys_nice标志。

以下示例命令在debian:jessie 容器上设置这三个标志中的每个标志。

$ docker run -it \
    --cpu-rt-runtime=950000 \
    --ulimit rtprio=99 \
    --cap-add=sys_nice \
    debian:jessie

如果未正确配置内核或Docker守护程序,则会发生错误。

显卡

访问NVIDIA GPU

先决条件

访问官方的NVIDIA驱动程序页面 ,下载并安装适当的驱动程序。完成后重新引导系统。

验证您的GPU是否正在运行并且可以访问。

安装nvidia-container-runtime

按照(https://nvidia.github.io/nvidia-container-runtime/)上的说明进行操作,然后运行以下命令:

$ apt-get install nvidia-container-runtime

确保nvidia-container-runtime-hook可以从访问$PATH

$ which nvidia-container-runtime-hook

重新启动Docker守护程序。

公开GPU以供使用

--gpus启动容器访问GPU资源时,请包含该标志。指定要使用的GPU数量。例如:

$ docker run -it --rm --gpus all ubuntu nvidia-smi

公开所有可用的GPU,并返回类似于以下内容的结果:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.130            	Driver Version: 384.130               	|
|-------------------------------+----------------------+----------------------+
| GPU  Name 	   Persistence-M| Bus-Id    	Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GRID K520       	Off  | 00000000:00:03.0 Off |                  N/A |
| N/A   36C	P0    39W / 125W |  	0MiB /  4036MiB |      0%  	Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU   	PID   Type   Process name                         	Usage  	|
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

使用该device选项指定GPU。例如:

$ docker run -it --rm --gpus device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a ubuntu nvidia-smi

公开该特定的GPU。

$ docker run -it --rm --gpus '"device=0,2"' ubuntu nvidia-smi

公开第一个和第三个GPU。

笔记

只有运行单个引擎的系统才能访问NVIDIA GPU。

设置NVIDIA功能

您可以手动设置功能。例如,在Ubuntu上,您可以运行以下命令:

$ docker run --gpus 'all,capabilities=utility' --rm ubuntu nvidia-smi

这使得utility驱动程序具有将nvidia-smi工具添加到容器的功能。

可以通过环境变量在图像中设置功能以及其他配置。有关有效变量的更多信息,请参见 nvidia-container-runtime GitHub页面。这些变量可以在Dockerfile中设置。

您也可以使用CUDA图像,它会自动设置这些变量。有关更多信息,请参见CUDA图片GitHub页面。

码头工人守护进程配置运行时