docker | 计算机资源的限制

1,378 阅读6分钟

在使用 Docker 运行容器时,一台主机上可能会运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。

程序运行时内存不够:程序无法运行起来\程序崩溃

Linux中Swap(即:交换分区),类似于Windows的虚拟内存,就是当内存不足的时候,把一部分硬盘空间虚拟成内存使用,从而解决内存容量不足的情况。

默认情况下容器可以使用的主机 CPU 资源是不受限制的。和内存资源的使用一样,如果不对容器可以使用的 CPU 资源进行限制,一旦发生容器内程序异常使用 CPU 的情况,很可能把整个主机的 CPU 资源耗尽,从而导致更大的灾难。

内存不足的危害

对于 linux 主机来说,一旦内核检测到没有足够的内存可以分配,就会扔出 OOME(Out Of Memmory Exception),并开始杀死一些进程用于释放内存空间。糟糕的是任何进程都可能成为内核猎杀的对象,包括 docker daemon 和其它一些重要的程序。更危险的是如果某个支持系统运行的重要进程被干掉了,整个系统也就宕掉了!

docker 尝试通过调整 docker daemon 的 OOM 优先级来进行缓解。内核在选择要杀死的进程时会对所有的进程打分,直接杀死得分最高的进程,接着是下一个。当 docker daemon 的 OOM 优先级被降低后(注意容器进程的 OOM 优先级并没有被调整),docker daemon 进程的得分不仅会低于容器进程的得分,还会低于其它一些进程的得分。这样 docker daemon 进程就安全多了。

一、内存

-m,--memory 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M --memory-swap 内存+交换分区大小总限制。格式同上。必须必-m设置的大 4种设置方式

  1. 不设置 如果不设置-m,--memory和--memory-swap,容器默认可以用完宿舍机的所有内存和 swap 分区。 不过注意,如果容器占用宿主机的所有内存和 swap 分区超过一段时间后,会被宿主机系统杀死(如果没有设置--00m-kill-disable=true的话)。
  2. 设置-m,--memory,不设置--memory-swap 给-m或--memory设置一个不小于 4M 的值,假设为 a,不设置--memory-swap,或将--memory-swap设置为 0。 这种情况下,容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因为 Docker 默认容器交换分区的大小和内存相同。 如果在容器中运行一个一直不停申请内存的程序,你会观察到该程序最终能占用的内存大小为 2a。 比如$ docker run -m 1G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小也为 1G。容器内的进程能申请到的总内存大小为 2G。
  3. 设置-m,--memory=a,--memory-swap=b,且b > a 给-m设置一个参数 a,给--memory-swap设置一个参数 b。 a是容器能使用的内存大小,b是容器能使用的内存大小 + swap 分区大小。所以 b 必须大于 a。b -a 即为容器能使用的 swap 分区大小。 比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小为 2G。容器内的进程能申请到的总内存大小为 3G。
  4. 设置-m,--memory=a,--memory-swap=-1 给-m参数设置一个正常值,而给--memory-swap设置成 -1。 这种情况表示限制容器能使用的内存大小为 a,而不限制容器能使用的 swap 分区大小。 这时候,容器内进程能申请到的内存大小为 a + 宿主机的 swap 大小。

如果容器的物理内存被限制在 300M,但是进程却希望申请到 500M 的物理内存。在没有 swap 可用的情况下,进程直接被 OOM kill 了。如果有足够的 swap,程序至少还可以正常的运行。

二、CPU

1、--cpus=2 表示容器最多可以使用主机上两个 CPU。

2、--cpuset-cpus 选项让容器始终在一个或某几个 CPU 上运行:这是非常有意义的,因为现在的多核系统中每个核心都有自己的缓存,如果频繁的调度进程在不同的核心上执行势必会带来缓存失效等开销。

3、--cpu-shares 设置使用CPU的权重。

当 CPU 资源充足时,设置 CPU 的权重是没有意义的。只有在容器争用 CPU 资源的情况下, CPU 的权重才能让不同的容器分到不同的 CPU 用量。

docker run -it --rm --cpuset-cpus="0" --cpu-shares=512 u-stress:latest /bin/bash docker run -it --rm --cpuset-cpus="0" --cpu-shares=1024 u-stress:latest /bin/bash

4、CPU资源的绝对限制

cpu-period=0 限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000] --cpu-quota=0 限制 CPU CFS 配额,必须不小于1ms,即 >= 1000

## 将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000
## 表示该容器每 50ms 可以得到 50% 的 CPU 运行时间。
docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

## 将容器的 CPU 配额设置为 CFS 周期的两倍,CPU 使用时间怎么会比周期大呢?
## 其实很好解释,给容器分配两个 vCPU 就可以了。
## 该配置表示容器可以在每个周期内使用两个 vCPU 的 100% 时间。
docker run -it --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash

三、磁盘读写

Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽;

限制 bps 和 iops

bps 是 byte per second,每秒读写的数据量。 iops 是 io per second,每秒 IO 的次数。

可通过以下参数控制容器的 bps 和 iops: --device-read-bps,限制读某个设备的 bps。 --device-write-bps,限制写某个设备的 bps。 --device-read-iops,限制读某个设备的 iops。 --device-write-iops,限制写某个设备的 iops。

默认情况下,所有容器能平等地读写磁盘,可以通过设置--blkio-weight参数来改变容器 block IO 的优先级。

--blkio-weight与--cpu-shares类似,设置的是相对权重值,默认为 500

docker run -it --name container_A --blkio-weight 600 ubuntu

docker run -it --name container_B --blkio-weight 300 ubuntu