一、背景
在使用 docker 运行容器时,默认的情况下,docker没有对容器进行硬件资源的限制,当一台主机上运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。
docker 作为容器的管理者,自然提供了控制容器资源的功能。正如使用内核的 namespace 来做容器之间的隔离,docker 也是通过内核的 cgroups 来做容器的资源限制;包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。
Docker内存控制OOME在linxu系统上,如果内核探测到当前宿主机已经没有可用内存使用,那么会抛出一个OOME(Out Of Memory Exception:内存异常 ),并且会开启killing去杀掉一些进程。
一旦发生OOME,任何进程都有可能被杀死,包括docker daemon在内,为此,docker特地调整了docker daemon的OOM_Odj优先级,以免他被杀掉,但容器的优先级并未被调整。经过系统内部复制的计算后,每个系统进程都会有一个OOM_Score得分,OOM_Odj越高,得分越高,(在docker run的时候可以调整OOM_Odj)得分最高的优先被kill掉,当然,也可以指定一些特定的重要的容器禁止被OMM杀掉,在启动容器时使用 –oom-kill-disable=true指定。
二、cgroup简介
cgroup是Control Groups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被LXC、docker等很多项目用于实现进程资源控制。cgroup将任意进程进行分组化管理的 Linux 内核功能。cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现:
blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu:使用调度程序为cgroup任务提供cpu的访问。
cpuacct:产生cgroup任务的cpu资源报告。
cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices:允许或拒绝cgroup任务对设备的访问。
freezer:暂停和恢复cgroup任务。
memory:设置每个cgroup的内存限制以及产生内存资源报告。
net_cls:标记每个网络包以供cgroup方便使用。
ns:命名空间子系统。
perf_event:增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。
Linux Cgroups 给用户暴露出来的操作接口是文件系统。他以文件和目录的方式组织在操作系统的 /sys/fs/cgroup
路径下。执行以下命令可以查看:
[root@server2 ~]# mount -t cgroup
- 1
三、cpu限额
Docker 的资源限制和隔离完全基于 Linux cgroups。对 CPU 资源的限制方式也和 cgroups 相同。Docker 提供的 CPU 资源限制选项可以在多核系统上限制容器能利用哪些 vCPU。而对容器最多能使用的 CPU 时间有两种限制方式:一是有多个 CPU 密集型的容器竞争 CPU 时,设置各个容器能使用的 CPU 时间相对比例。二是以绝对的方式设置容器在每个调度周期内最多能使用的 CPU 时间。
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 系统有效 |
其中–cpuset-cpus用于设置容器可以使用的 vCPU 核。-c,–cpu-shares用于设置多个容器竞争 CPU 时,各个容器相对能分配到的 CPU 时间比例。–cpu-period和–cpu-quata用于绝对设置容器能使用 CPU 时间。
CPU 集
我们可以设置容器可以在哪些 CPU 核上运行。
例如:
docker run -it --cpuset-cpus="1,3" ubuntu /bin/bash
- 1
表示容器中的进程可以在 cpu 1 和 cpu 3 上执行。
docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash
cat /sys/fs/cgroup/cpuset/docker/<容器的完整长ID>/cpuset.cpus
- 1
- 2
表示容器中的进程可以在 cpu 0、cpu 1 及 cpu 2 上执行。
CPU 资源的绝对限制
Linux 通过 CFS(Completely Fair Scheduler,完全公平调度器)来调度各个进程对 CPU 的使用。CFS 默认的调度周期是 100ms。
我们可以设置每个容器进程的调度周期,以及在这个周期内各个容器最多能使用多少 CPU 时间。使用--cpu-period
即可设置调度周期,使用--cpu-quota
即可设置在每个周期内容器能使用的 CPU 时间。两者一般配合使用。
例如:
运行一个容器并将其打入后台:
[root@server2 ~]# docker run --cpu-period 100000 --cpu-quota 20000 -it --name demo busyboxplus
- 1
将 CFS 调度的周期设为 100000,将容器在每个周期内的 CPU 配额设置为 20000,表示该容器每 100ms 可以得到 20% 的 CPU 运行时间,可以看出这两个选项的单位都是 us。
可以查看文件:
上图中cpu.cfs_quota_us文件中默认为-1,表示不做限制。
现在进行测试:
ctrl+pq使其后台运行,top命令查看cpu占用:
可以看出容器被限制在了20%。
注意:此实验最好给虚拟机分配1个cpu核心,可以更好的看出实验效果。
其他例子:
docker run -it --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash
- 1
将容器的 CPU 配额设置为 CFS 周期的两倍,CPU 使用时间怎么会比周期大呢?其实很好解释,给容器分配两个 vCPU 就可以了。该配置表示容器可以在每个周期内使用两个 vCPU 的 100% 时间。
CFS 周期的有效范围是 1ms~1s,对应的–cpu-period的数值范围是 1000~1000000。而容器的 CPU 配额必须不小于 1ms,即–cpu-quota的值必须 >= 1000。
CPU资源优先级配置
注意前面我们用--cpu-quota
设置容器在一个调度周期内能使用的 CPU 时间时实际上设置的是一个上限。并不是说容器一定会使用这么长的 CPU 时间。比如,我们先启动一个容器,将其绑定到 cpu 1 上执行。给其–cpu-quota和–cpu-period都设置为 50000。
[root@server2 docker]# docker run -it --name t