资源限制

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

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

WARNING: No swap limit support

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

内存

了解内存耗尽的风险

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

Docker 试图通过调整 Docker 守护进程的 OOM 优先级来降低这些风险,使其比系统上的其他进程更不容易被杀死。容器的 OOM 优先级没有被调整。这使得单个容器比 Docker 守护进程或其他系统进程更有可能被杀死。您不应该试图通过在守护进程或容器上手动将 --oom-score-adj 设置为一个极端的负数,或者在容器上设置 --oom-kill-disable 来规避这些保护措施。

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

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

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

限制容器对内存的访问

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

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

其中一些选项单独使用或多个选项同时设置时会有不同的效果。

这些选项中的大多数接受一个正整数,后跟 bkmg 后缀,以表示字节、千字节、兆字节或吉字节。

选项描述
-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/<PID>/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 守护进程

要使用实时调度器运行容器,请在运行 Docker 守护进程时,将 --cpu-rt-runtime 标志设置为每个运行时周期为实时任务保留的最大微秒数。例如,对于默认的 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 目录页面。

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.