一、资源限制

一个docker host. 上会运行若干容器,每个容器都需要CPU、内存和IO资源。对于KVM、VMware 等虚拟化技术,用户可以控制分配多少CPU、内存资源给每个虚拟机。对于容器,Docker 也提供了类似的机制避免某个容器因占用太多资源而影响其他容器乃至整个host
的性能。

内存限制

与操作系统类似,容器可使用的内存包括两部分:物理内存和 swap。Docker通过下面两组参数来控制容器内存的使用量。

  • -m--memory:设置内存的使用限额,例如100MB,2GB。
  • --memory-swap:设置内存+swap的使用限额。
1
2
# 允许容器最多使用200MB内存和100MB的swap,默认为-1,对资源没有限制
docker run -m 200M --memory-swap=300M ubuntu
1
2
3
4
# 通过progrium/stress镜像学习如何为容器分配内存,这个镜像可以用来对容器执行压力测试
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
# --vm1:启动1个内存工作线程。
# --vm-bytes 280M:每个线程分配280MB内存。

280MB在可分配的范围(300MB),所以工作线程能够正常工作:

1
2
3
4
5
(1)分配280MB内存。
(2)释放280MB内存。
(3)再分配280MB内存。
(4)再释放280MB内存。
(5)一直循环.....

如果工作线程分配超过300MB

1
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M

分配的内存超过限额,stress 线程报错,容器退出。

如果在启动容器时只指定-m而不指定-memoryswap, 那么-memory-swap 默认为-m的两倍:

1
docker run -it -m 200M ubuntu

容器使用200MB的物理内存和200MB的swap。

CPU限额

默认设置下,所有容器可以平等地使用host CPU资源并且没有限制

Docker可以通过-c或–cpu-shares设置容器使用CPU的权重。如果不指定,默认值为1024。

与内存限额不同,通过-c设置的cpu share 并不是CPU资源的绝对数量,而是一个相对的权重值。某个容器最终能分配到的CPU资源取决于它的cpu share占所有容器cpu share总和的比例。

通过cpu share可以设置容器使用CPU的优先级。

比如在host中启动了两个容器:

1
2
docker run --name "cont_A" -c 1024 ubuntu 
docker run --name "cont_B" -c 512 ubuntu

containerA的cpu share是containerB 的两倍。当两个容器都需要CPU资源时,containerA可以得到的CPU是containerB 的两倍。

权重分配CPU只会发生在CPU资源紧张的情况下。如果containerA处于空闲状态,这时,为了充分利用CPU资源,containerB 也可以分配到全部可用的CPU.

用progrium/stress 做实验。

  1. 启动(container_ A, cpu share为1024

    1
    docker run --name "cont_A" -it -c 1024  progrium/stress --cpu 1

    –cpu用来设置工作线程的数量。因为当前host 只有1颗CPU,所以一个工作线程就能将CPU压满。如果host有多颗CPU,则需要相应增加–cpu的数量。

  2. 启动(container_B, cpu share为512

    1
    docker run --name "cont_B" -it -c 512  progrium/stress --cpu 1
  3. 使用Top,查看CPU的使用情况:containerA消耗的CPU是containerB 的两倍。

  4. 现在暂停containerA:containerB在containerA空闲的情况下能够用满整颗CPU。

Block IO 带宽限额

Block IO可以限制容器是磁盘的读写,docker 可通过设置权重、限制bpsiops 的方式控制容器读写磁盘的带宽。

  1. 权重

    默认情况下,所有容器能平等地读写磁盘,可以通过设置-blkio-weight参数来改变容器block Io的优先级。
    -blkio-weight与–cpu-shares 类似,设置的是相对权重值,默认为500

    1
    2
    3
    containerA 读写磁盘的带宽是containerB 的两倍。
    docker run -it --name cont_A --blkip-weight 600 ubuntu
    docker run -it --name cont_B --blkip-weight 300 ubuntu
  2. 限制bps和iops

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

    • --device-read-bps:限制读某个设备的bps.

    • --devce-write-bps:限制写某个设备的bps.

    • --device- read-iops:限制读某个设备的iops.

    • --device-write-iops: 限制写某个设备的iops。

    1
    2
    #限制容器写/dev/sda 的速率为30MB/s:
    docker run -it --device-write-bps /dev/sda:30MB ubuntu

二、底层技术

cgroup和 namespace是最重要的两种技术。cgroup实现资源限额,namespace实现资源隔离。

cgroup

cgroup全称 Control Group。Linux操作系统通过cgroup可以设置进程使用CPU、内存和IO资源的限额。前面的—cpu-shares、-m、–device-write-bps实际上就是在配置cgroup。

cgroup可以在/sys/fs/cgroup找到。

如果创建cpu的限制,docker会在/sys/fs/cgroup/cpu/docker目录中为每一个容器创建一个cgroup目录,以容器的ID命名。这个目录中会包含所有cpu相关的cgroup配置。

同样的,/sys/fs/cgroup/memory/docker/sys/fs/cgroup/blkio/docker中保存的是内存以及Block IO的配置。

namespace

在每个容器中,我们都可以看到文件系统、网卡等资源,这些资源看上去是容器自己的。拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使host 上只有一块物理网卡。这种方式使得容器更像一个独立的计算机。

Linux实现这种方式的技术是namespace。namespace管理着host中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。namespace实现了容器间资源的隔离。

Linux使用了6种 namespace,分别对应6种资源:Mount、UTS、IPC、PID、Network和User。

namespace 隔离内容
Mount 让容器拥有整个文件系统。容器有自己的/根目录,可以mount和umount
UTS 让容器有自己的主机名,默认是容器的短ID,用过-h或–hostname设置
IPC 让容器拥有自己的共享内存和信号量实现进程间通信
PID 容器在host中以进程形式运行,ps axf在主机中可以看到容器进程,所有进程都挂载dockerd下,每个容器内的PID都不同于host的中的PID
Network 容器拥有自己独立的网卡、IP、路由等资源
User host 不能看到容器创建的用户