资源限制

默认情况下,容器没有资源限制,可以使用主机内核调度程序允许的任意数量的给定资源。Docker 提供了控制容器可以使用多少内存或 CPU 的方法,设置 `docker run` 命令的运行时配置标志。本节详细介绍了何时应设置此类限制以及设置它们的可能影响。

许多这些功能都需要您的内核支持 Linux 功能。要检查支持情况,您可以使用 docker info 命令。如果您的内核中禁用了某个功能,您可能会在输出结尾看到如下警告

WARNING: No swap limit support

请查阅您的操作系统的文档以启用它们。有关更多信息,另请参见 Docker Engine 故障排除指南

内存

了解内存不足的风险

重要的是不要允许运行的容器消耗太多主机内存。在 Linux 主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它会抛出 `OOME` 或 `内存不足异常`,并开始终止进程以释放内存。任何进程都可能被终止,包括 Docker 和其他重要的应用程序。如果终止了错误的进程,这可能会有效地导致整个系统崩溃。

Docker 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,使其比系统上的其他进程不太可能被终止。容器上的 OOM 优先级不会被调整。这使得单个容器比 Docker 守护程序或其他系统进程更有可能被终止。您不应该尝试通过手动将守护程序或容器上的 `--oom-score-adj` 设置为极小的负数,或在容器上设置 `--oom-kill-disable` 来规避这些安全措施。

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

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

  • 执行测试以了解应用程序的内存需求,然后再将其投入生产。
  • 确保您的应用程序仅在具有足够资源的主机上运行。
  • 限制容器可以使用多少内存,如下所述。
  • 在配置 Docker 主机的交换空间时要谨慎。交换空间比内存慢,但可以提供防止系统内存不足的缓冲。
  • 考虑将您的容器转换为 服务,并使用服务级别约束和节点标签来确保应用程序仅在具有足够内存的主机上运行

限制容器对内存的访问

Docker 可以强制执行硬内存限制或软内存限制。

  • 硬限制允许容器使用的内存不超过固定数量。
  • 软限制允许容器根据需要使用尽可能多的内存,除非满足某些条件,例如内核检测到主机内存不足或竞争。

单独使用这些选项或设置多个选项时,它们的效果会有所不同。

大多数这些选项采用正整数,后跟 `b`、`k`、`m`、`g` 后缀,分别表示字节、千字节、兆字节或千兆字节。

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

有关 cgroups 和内存的更多信息,请参见内存资源控制器的文档。

--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设置为正整数,则**容器无法访问交换空间**。请参见阻止容器使用交换空间

  • 如果--memory-swap未设置,而--memory已设置,则如果主机容器配置了交换内存,则容器可以使用与--memory设置一样多的交换空间。例如,如果--memory="300m"并且--memory-swap未设置,则容器总共可以使用 600m 的内存和交换空间。

  • 如果--memory-swap显式设置为-1,则允许容器使用无限的交换空间,直到主机系统上可用的数量。

  • 在容器内部,像free这样的工具报告的是主机可用的交换空间,而不是容器内部可用的交换空间。不要依赖free或类似工具的输出来确定交换空间是否存在。

阻止容器使用交换空间

如果--memory--memory-swap设置为相同的值,这将阻止容器使用任何交换空间。这是因为--memory-swap是可使用的内存和交换空间的总量,而--memory只是可使用的物理内存量。

--memory-swappiness 细节

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

--kernel-memory 细节

内核内存限制以分配给容器的总内存量来表示。考虑以下场景

  • **无限内存,无限内核内存**:这是默认行为。
  • **无限内存,有限内核内存**:当所有 cgroups 需要的内存量大于主机上实际存在的内存量时,这很合适。您可以将内核内存配置为永远不会超过主机上可用的内存量,而需要更多内存的容器需要等待。
  • **有限内存,无限内核内存**:总内存是有限制的,但内核内存不是。
  • **有限内存,有限内核内存**:限制用户内存和内核内存对于调试与内存相关的問題都很有用。如果容器使用了意外数量的任何一种类型的内存,它会在不影响其他容器或主机的情况下耗尽内存。在此设置中,如果内核内存限制低于用户内存限制,则耗尽内核内存会导致容器遇到 OOM 错误。如果内核内存限制高于用户内存限制,则内核限制不会导致容器遇到 OOM。

启用内核内存限制后,主机将基于每个进程跟踪“高水位线”统计信息,以便您可以跟踪哪些进程(在本例中为容器)使用了过多的内存。可以通过查看主机上的/proc//status来按进程查看此信息。

CPU

默认情况下,每个容器对主机 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不会阻止容器在 Swarm 模式下被调度。它会根据可用的 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 调度和优先级是高级内核级功能。大多数用户不需要将其值从默认值更改。错误地设置这些值可能会导致主机系统变得不稳定或无法使用。

配置主机内核

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

配置 Docker 守护程序

要使用实时调度程序运行容器,请使用--cpu-rt-runtime标志运行 Docker 守护程序,该标志设置为每个运行时周期为实时任务保留的微秒数的最大值。例如,使用默认周期 1000000 微秒(1 秒),设置--cpu-rt-runtime=950000可确保使用实时调度程序的容器可以每 1000000 微秒周期运行 950000 微秒,为非实时任务保留至少 50000 微秒。要在使用systemd的系统上使此配置永久生效,请为docker服务创建一个 systemd 单元文件。例如,请参见有关如何配置守护程序以使用带有systemd 单元文件的代理的说明。

配置单个容器

使用docker run启动容器时,您可以传递多个标志来控制容器的CPU优先级。有关适当值的详细信息,请参阅您的操作系统的文档或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守护进程配置不正确,则会发生错误。

GPU

访问 NVIDIA GPU

先决条件

访问官方的NVIDIA驱动程序页面下载并安装正确的驱动程序。完成后重新启动系统。

验证您的GPU正在运行并可访问。

安装nvidia-container-toolkit

按照官方NVIDIA Container Toolkit 安装说明

公开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-toolkit文档。这些变量可以在Dockerfile中设置。

您也可以使用CUDA镜像,它会自动设置这些变量。请参阅官方的CUDA镜像 NGC目录页面。