Hello Docker(六)——Docker资源配额

一、Docker资源配额

1、Docker资源配额简介

Docker默认没有对容器进行硬件资源的限制,当容器负载过高时会尽可能占用宿主机资源。Docker基于Linux内核提供的cgroups功能,可以限制容器在运行时使用的资源,比如内存、CPU、IO、网络等。 在Linux系统上,如果Linux内核探测到当前宿主机已经没有可用内存使用,会抛出一个OOME(Out Of Memory Exception:内存异常 ),并且会开启killing去杀掉一些进程。 一旦发生OOME,任何进程都有可能被杀死,包括Docker daemon,因此,Docker特地调整了Docker daemon的OOM_Odj优先级,以免被杀掉,但容器的优先级并未被调整。 经过Linux系统内部复杂计算后,每个系统进程都会有一个OOM_Score得分,OOM_Odj越高,得分越高,得分最高的进程优先被kill掉。 Docker在启动容器时可以调整OOM_Odj,也可以指定重要容器禁止被OMM杀掉,在启动容器时使用–oom-kill-disable=true指定。

2、cgroup简介

Control Groups是Linux内核提供的一种可以限制、记录、隔离进程组所使用物理资源(如CPU、Memory、磁盘IO等等) 的机制,被LXC、Docker等项目用于实现进程资源控制。cgroup是提供将进程进行分组化管理的功能和接口的基础结构,用于实现IO或内存配控制等具体的资源管理功能。cgroup实现的资源管理功能称为cgroup子系统,如下: (1)blkio,设置限制每个块设备的输入输出控制,如磁盘、光盘、USB等。 (2)cpu,使用调度程序为cgroup任务提供CPU访问。 (3)cpuacct,产生cgroup任务的CPU资源报告。 (4)cpuset,对于多核心CPU,会为cgroup任务分配单独CPU和内存。 (5)devices,允许或拒绝cgroup任务对设备的访问。 (6)freezer,暂停和恢复cgroup任务。 (7)memory,设置每个cgroup的内存限制以及产生内存资源报告。 (8)net_cls,标记每个网络包以供cgroup方便使用。 (9)ns,命名空间子系统。 (10)perf_event,增加对每个group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。

二、Docker内存配额

1、Docker内存限额

Docker提供的内存限制功能如下: (1)容器能使用的内存和交换分区大小。 (2)容器的核心内存大小。 (3)容器虚拟内存的交换行为。 (4)容器内存的软性限制。 (5)是否杀死占用过多内存的容器。 (6)容器被杀死的优先级。

2、Docker内存限制参数

(1)memory -m,--memory,内存限制,格式为数字加单位,单位可以为b,k,m,g,最小为4M。 --memory-swap,内存+交换分区大小总限制,必须比memory设置的大。 如果不设置memory和memory-swap,Docker容器默认可以用完宿主机的所有内存和swap分区。Docker容器用尽宿主机的所有内存和swap分区超过一段时间后,会被宿主机系统杀死。 如果memory设置一个不小于4M的值n,不设置memory-swap或将memory-swap设置为 0,则Docker容器能使用的内存大小为 n,能使用的交换分区大小也为 n,Docker默认容器交换分区的大小和内存相同;如果在Docker容器中运行一个一直不停申请内存的程序,Docker容器最终能占用的内存大小为2n;如果memory设置为a,memory-swap设置为b,则Docker容器能使用的内存大小为a,Docker容器能使用的swap分区大小为b-a;如果memory设置a,memory-swap设置为-1,则Docker容器能使用的内存大小为a,不限制Docker容器使用swap分区大小。 --memory-reservation,内存的软性限制。 Memory reservation 是一种软性机制,不保证任何时刻容器使用的内存不会超过memory-reservation限定值,只确保容器不会长时间占用超过memory-reservation限制的内存大小。虽然Docker容器最多可以使用memory的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到memory-reservation的大小。 默认情况下,未设置memory-reservation值,其值与memory限定值相同。如果memory-reservation设置为0,则等同于没有设置。 (2)OOM killer --oom-kill-disable,是否阻止OOM killer杀死容器,默认未设置。 默认情况下,在出现out-of-memory(OOM)错误时,OOM killer进程会杀死Docker容器内的进程来释放内存。通过设置--oom-kill-disable选项来禁止OOM killer杀死容器内进程。确保只有在使用memory选项时才使用oom-kill-disable选项禁用OOM killer。如果没有设置memory选项,但禁用OOM-killer,可能会造成出现OOM错误时,通过杀死宿主机进程获取更多内存。 --oom-score-adj,容器被OOM killer杀死的优先级,范围是[-1000, 1000],默认为0,值越大越容易被kill。 --memory-swappiness,用于设置容器的虚拟内存控制行为。值为0~100之间的整数。 (3)kernel memory --kernel-memory,核心内存限制,最小为4M。 核心内存和用户内存不同的地方在于核心内存不能被交换出,因此Docker容器可以通过消耗太多内存来堵塞一些系统服务。核心内存包括:stack pages(栈页面)、slab pages、socket memory pressure、tcp memory pressure。 可以通过设置核心内存限制来约束核心内存。例如,每个进程都要消耗一些栈页面,通过限制核心内存,可以在核心内存使用过多时阻止新进程被创建。 核心内存和用户内存并不是独立的,必须在用户内存限制的上下文中限制核心内存。 假设用户内存的限制值为U,核心内存的限制值为K。有三种可能地限制核心内存的方式: U != 0,不限制核心内存,是默认的标准设置方式 K < U,核心内存是用户内存的子集,每个cgroup的内存总量被过度使用,可以设置K,groups总数就不会超过总内存。 K > U,核心内存的变化也会导致用户计数器的变化,容器核心内存和用户内存都会触发回收行为。 docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash 启动容器,Docker容器的进程最多能使用500M内存、50M核心内存。 docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash 没用设置用户内存限制,Docker容器中的进程可以使用尽可能多的内存,但最多能使用50M核心内存。 (4)Swappiness 默认情况下,Docker容器的内核可以交换出一定比例的匿名页,使用memory-swappiness参数进行设置。--memory-swappiness可以设置为从 0到100。0表示关闭匿名页面交换。100表示所有的匿名页都可以交换。默认情况下,如果设置--memory-swappiness,则从父进程继承而来。 docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash memory-swappiness设置为0可以保持容器的工作集,避免交换代理的性能损失。

三、Docker CPU配额

1、Docker CPU限制

默认情况,所有Docker容器可以平等地使用宿主机CPU资源并且没有限制。 Docker提供的CPU资源限制选项可以在多核系统上限制容器能利用哪些 CPU。而Docker容器最多能使用CPU时间有两种限制方式:一是有多个CPU 密集型的容器竞争CPU时,设置各个容器能使用CPU时间相对比例;二是以绝对的方式设置容器在每个调度周期内最多能使用的CPU时间。 Docker可以通过-c或--cpu-shares设置容器使用CPU的权重。如果不指定,默认值为1024。

2、CPU限制参数

docker run与CPU限制相关选项如下: --cpuset-cpus="" 允许使用的CPU集,值可以为0-3,0,1 -c,--cpu-shares=0 CPU权重 --cpu-period=0 限制CPU CFS周期,范围从100ms~1s,即[1000, 1000000] --cpu-quota=0 限制CPU CFS配额,必须不小于1ms,即大于等于1000 --cpuset-mems="" 允许在上执行的内存节点(MEMs),只对NUMA系统有效 --cpus=value 指定容器可以使用的CPU资源量。如果主机有两个CPU并且设置 --cpus="1.5",则容器最多可以使用1.5个CPU, --cap-add=sys_nice 开启Docker容器CAP_SYS_NICE功能,允许容器引发进程良好值,设置实时调度策略,设置CPU亲和性以及其他操作。 --cpu-rt-runtime=value 容器可以在Docker守护程序的实时调度程序周期内以实时优先级运行的最大微秒数,需要开启CAP_SYS_NICE功能 。 --ulimit rtprio=value 容器允许的最大实时优先级,需要开启CAP_SYS_NICE功能。 docker run --it --cpu-rt-runtime=950000 --ulimit rtprio=99 --cap-add=sys_nice ubuntu 实时调度程序运行容器,需要运行docker daemon,并将--cpu-rt-runtime设置为每个运行时间段为实时任务保留的最大微秒数。默认周期为1000000微秒(1秒),设置--cpu-rt-runtime=950000可确保使用实时调度程序的容器每1000000微秒可运行950000微秒,并保留至少50000微秒用于非实时任务。 其中--cpuset-cpus用于设置容器可以使用的 CPU核。 -c,--cpu-shares用于设置多个容器竞争CPU时,各个容器能分配到的CPU权重。 --cpu-period和--cpu-quata用于绝对设置容器能使用 CPU 时间。 docker run -it --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash 容器中的进程可以在cpu 1和cpu 3上执行。 docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash 容器中的进程可以在 cpu 0、cpu 1 及 cpu 2上执行。 docker run -it --cpuset-mems="1,3" ubuntu:14.04 /bin/bash 容器中的进程只能使用内存节点1和3上的内存 在多核系统上,CPU时间权值是在所有CPU核上计算的。即使某个容器的 CPU 时间限制少于100%,也能使用各个CPU核的100%时间。

四、Docker磁盘IO配额

Docker对磁盘IO的控制需要依赖宿主机设备,主要参数如下: --device-read-bps 限制设备上的读速度(bytes per second),单位可以是kb、mb或者gb。 --device-read-iops 通过每秒读IO次数来限制指定设备的读速度。 --device-write-bps 限制设备的写速度(bytes per second),单位可以是kb、mb或者gb。 --device-write-iops 通过每秒写IO次数来限制指定设备的写速度。 --blkio-weight 容器默认磁盘IO的加权值,有效值范围为10-100。 --blkio-weight-device 针对特定设备的IO加权控制,其格式为DEVICE_NAME:WEIGHT docker run -tid –name disk1 –device-write-bps /dev/sda:1mb ubuntu:stress

五、Docker容器空间配额

docker使用devicemapper作为存储驱动时,默认每个容器和镜像的最大大小为10G。在docker daemon启动时可以使用dm.basesize来指定,但会导致Docker Host上的所有本地镜像和容器都被清理掉,使用aufs或者overlay等其它存储驱动时,没有限制。