具有内存、CPU 和 GPU 的运行时选项

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

其中许多功能要求您的内核支持 Linux 功能。要检查支持,您可以使用该 docker info命令。如果内核中禁用了某项功能,您可能会在输出末尾看到如下警告:

WARNING: No swap limit support

请参阅操作系统的文档以启用它们。另请参阅 Docker 引擎故障排除指南 以获取更多信息。

记忆

了解内存不足的风险

重要的是不要让正在运行的容器消耗过多的主机内存。在 Linux 主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它会抛出OOME, 或 Out Of Memory Exception, 并开始终止进程​​以释放内存。任何进程都会被杀掉,包括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 MB。
--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选项。仅在您也设置了该选项的容器上禁用 OOM Killer -m/--memory。如果-m未设置该标志,主机可能会耗尽内存,并且内核可能需要终止主机系统的进程以释放内存。

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

--内存交换详细信息

--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。请参阅 防止容器使用交换

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

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

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

防止容器使用交换

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

--内存交换详细信息

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

--内核内存详细信息

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

  • 无限内存、无限内核内存:这是默认行为。
  • 无限内存、有限内核内存:当所有 cgroup 所需的内存量大于主机上实际存在的内存量时,这是合适的。您可以将内核内存配置为永远不会超过主机上的可用内存,并且需要更多内存的容器需要等待它。
  • 有限内存,无限内核内存:总体内存是有限的,但内核内存不是。
  • 有限的内存,有限的内核内存:限制用户和内核内存对于调试与内存相关的问题很有用。如果容器使用任一类型内存的数量超出预期,它就会耗尽内存,而不会影响其他容器或主机。在此设置中,如果内核内存限制低于用户内存限制,则内核内存耗尽会导致容器遇到 OOM 错误。如果内核内存限制高于用户内存限制,则内核限制不会导致容器遇到 OOM。

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

中央处理器

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

配置默认 CFS 调度程序

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

选项描述
--cpus=<value>指定容器可以使用多少可用 CPU 资源。例如,如果主机有两个CPU,并且您设置了--cpus="1.5",则保证容器最多有1个半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-periodand --cpu-quota;

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

配置实时调度程序

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

警告

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

配置主机的内核

通过运行或检查该文件是否存在来验证CONFIG_RT_GROUP_SCHEDLinux 内核中是否启用了 该功能。有关配置内核实时调度程序的指导,请参阅适用于您的操作系统的文档。zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED/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 runulimit有关适当值的信息,请参阅操作系统的文档或命令。

选项描述
--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-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

这启用了utilitynvidia-smi工具添加到容器的驱动程序功能。

可以通过环境变量在映像中设置功能以及其他配置。有关有效变量的更多信息可以在nvidia-container-toolkit文档中找到 。这些变量可以在 Dockerfile 中设置。

您还可以使用自动设置这些变量的 CUDA 图像。请参阅官方 CUDA 图像 NGC 目录页面。