Docker 009 容器的资源限制

默认情况下,容器是没有资源限制的,可以使用宿主机上所有可分配的资源,简单来说就是,容器可以耗尽宿主机资源。Docker 提供了一个可以控制内存、CPU的方法。

资源限制特性需要 Linux 内核支持 capabilities。可以使用 docker info 命令查看是否支持,如果 capability 被关闭,会看到如下信息:

WARNING: No swap limit support

 

内存 Memory

不允许正在运行的容器占用过多的主机内存,在 Linux 主机上,如果内核检测到没有足够的内存来运行重要的系统功能,则会抛出 OOME(Out Of Memory Exception),并杀掉进程来释放内存,包括 docker 或者其他重要进程在内,任何一个进程都可能被会 kill 掉,这个异常甚至可能会导致系统宕机。

Docker 尝试通过吊证守护进程的 OOM 优先级来降低这些风行,使其比其他进程容易被 kill。容器的 OOM 优先级未作调整,这使得单个容器更容易被杀死(相比 docker 守护进程或其他系统进程),一般不建议手动将守护进程或容器的--oom-score-adj设置为一个极端的负数,或者在容器上设置 --oom-kill-disable 来规避这些安全措施。

你可以通过下面的方法来降低 OOM 带来的风险:

  • 投入生产前,执行测试以便了解程序的内存要求
  • 确保程序运行在有充足资源的主机上
  • 限制容器的内存使用量
  • 留意配置的 swap,swap 性能比内存低,但可以提供一个缓冲
  • 考虑将容器转换为服务(swaem service),使用服务级别的约束和节点标签,以确保应用程序仅在有足够内存的主机上运行

 

限制容器的内存用量

Docker 可强制执行硬性的内存限制,即允许容器使用不超过给定数量的用户或系统的内存;或软性限制,除非满足某些条件,否则允许容器根据需要使用尽可能多的内存,例如当内核检测到内存不足或争用时。

下面的选项中,当单独使用或设置多个选项时,其中一些选项会产生不同的效果。这些选项大多使用正整数,单位是 b(bytes), k(kilobytes), m(megabytes), g(gigabytes)。

选项

描述

-m 或 --memory=

容器可以使用的最大内存量。如果设置此选项,则最小允许值为4m(4兆字节)。

--memory-swap*

允许此容器使用的swap的用量,仅在还设置了--memory时才有意义

--memory-swappiness

默认情况下,主机内核可以换出一定比例的容器使用的匿名页面,可将该选项的值设置为0-100间,来指定这个比例

--memory-reservation

允许指定一个比--memory更小的软限制,当 docker 检测到主机内存不存或争用时,会激活该软限制。如果使用该选项,则必须将其设置为低于--memory的值才会有优先权,因其是软限制,所以不能保证容器不超过限制,

--kernel-memory

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

--oom-kill-disable

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

 

CPU

默认情况下,每个容器 都可以无限制的使用 CPU,你可以设置各种约束来限制容器对 CPU 的访问,大多数用户使用配置的是默认的CFS调度器。

配置默认的 CFS 调度器

CFS 是用于常规Linux 进程的内核 CPU 调度器。有几个 runtime 标志可以让你去配置容器可使用的 CPU 资源量。当你配置之后,Docker 会修改主机上容器的cgroup 的设置。

 

选项

描述

--cpus=

指定一个容器可以使用多少有效的 CPU资源。例如,如果宿主机有 2 个 CPU,而你设置了--cpus="1.5",那么容器最多使用一个半的 CPU 可使用。这等价于设置了 --cpu-period =“ 100000” 和 --cpu-quota =“ 150000”

--cpu-period=

指定CPU CFS调度器的周期,默认为100微秒,和--cpu-quota 一起使用。大多数用不会修改此默认设置,Docker1.13 及之后的版本使用 --cpus 替代。

--cpu-quota=

在容器上设置 CPU CFS 的配额。容器在每个--cpu-period中被限制使用的微秒数。Docker1.13 及之后的版本使用 --cpus 替代。

--cpuset-cpus

限制容器使用指定的 CPU 或内核,如果有多个 CPU,可使用逗号或者连接符进行分隔。第一个 CPU 序号是 0。 设置的值可能是0-3(使用第一,第二,第三和第四CPU)或1,3(使用第二和第四CPU)

--cpu-shares

默认值为 1024,设置大于或小于默认值,可增加或减少容器的权重,使容器可可以使用更多或更少的 CPU周期。只有在限制 CPU 周期的时候才会执行此操作。当有足够的 CPU周期可用时,所有的容器都会使用尽可能多的 CPU。--cpu-shares不会影响 swarm 模式的调度。他为有效的CPU周期确定容器 CPU资源的使用优先级。它不保证或保留任何特定的 CPU 访问权限。

--cpu-period 和--cpu-quota两个参数控制容器可以分配到的CPU时钟周期。–cpu-period是用来指定容器对CPU的使用要在多长时间内做一次重新分配,而–cpu-quota是用来指定在这个周期内,最多可以有多少时间用来跑这个容器。跟–cpu-shares不同的是这种配置是指定一个绝对值,而且没有弹性在里面,容器对CPU资源的使用绝对不会超过配置的值。

# 如果有1 个 CPU,下面的命保证容器每秒最多占用 50%的 CPU。

# Docker 1.13 and higher:
$ docker run -it --cpus=".5" ubuntu /bin/bash

# Docker 1.12 and lower:
$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash

 

配置实时调度器

Docker 1.13 及以后的版本,你可以配置容器使用实时调度器,来执行无法使用 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微秒可以运行非实时任务。要使配置永久生效需要配置daemon 文件——/etc/docker/daemon.json。

 

配置单独的容器

在使用 docker run 启动容器时,可以指定多个参数来控制容器的优先级,

选项

描述

--cap-add=sys_nice

授权给容器 CAP_SYS_NICE功能,这样可以允许容器提高 nice 值,设置实时调度策略、CPU 亲和力和其他操作。

--cpu-rt-runtime=

在 docker守护程序的实时调度周期内,容器按运行实时优先级运行的最大微秒数,,同时需要设置 --cap-add=sys_nice

--ulimit rtprio=

允许容器使用的最大优先级,同时需要设置--cap-add=sys_nice

# 下面的命令在容器 debian:jessie 上设置了三个参数,如果内核或 docker daemon 未正确配置,会报错。
$ docker run -it --cpu-rt-runtime=950000 \
                  --ulimit rtprio=99 \
                  --cap-add=sys_nice \
                  debian:jessie

 

 

GPU

使用 NVIDIA GPU

前提条件

访问官方NVIDIA 驱动页面,下载并安装对应的驱动,完成后需要重启系统。

验证 GPU 正常运行且可以访问。

安装 NVIDIA-CONTAINER-RUNTIME

按照https://nvidia.github.io/nvidia-container-runtime/上的说明操作,并执行如下命令:

$ apt-get install nvidia-container-runtime
# 确保可以通过 $PATH 找到 nvidia-container-runtime-hook
$ which nvidia-container-runtime-hook

重启docker daemon。

公布并使用GPUS

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

启动容器时,使用 --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 nvidia-smi

 

设置 NVIDIA功能

你可以手动设置 NVIDIA 功能,例如,在 Ubuntu 上你可以执行如下命令:

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

这会启用 utility 驱动功能,从而将 nvidia-smi 工具添加到容器中。

通过环境变量可以在容器中设置功能和其他的配置,更多信息可以查看GitHub 上的nvidia-container-runtime,这些半两可以在 Dockerfile 中使用。