Docker之Linux Cgroups详解(资源限制)

1、导言:

Docker 容器使用 linux namespace 来隔离其运行环境,使得容器中的进程看起来像是在一个独立的环境下运行。但是光有环境隔离还不够,因为容器中的进程还是可以不受控制地使用计算机资源,如内存、CPU等;另外,linux资源耗尽时会时,会引起错杀进程;因此docker引入了linux cgroups来控制进程资源,让进程更可控。

 

想了解linux cgroups请前往:Linux Cgroups详解

 

2、Docker 资源管理接口概览

       通过docker run –help命令查看

格式

描述

-m, --memory=" < 数字 >[< 单位 >]"

内存使用限制。 数字需要使用整数,对应的单位是 b, k, m, g 中的一个。最小取值是 4M。

--memory-swap="< 数字 >[< 单位 >]"

总内存使用限制 (物理内存 + 交换分区,数字需要使用整数,对应的单位是 b, k, m, g 中的一个。

--memory-reservation="< 数字 >[< 单位 >]"

内存软限制。 数字需要使用正整数,对应的单位是 b, k, m, g 中的一个。

--kernel-memory="< 数字 >[< 单位 >]"

内核内存限制。 数字需要使用正整数,对应的单位是 b, k, m, g 中的一个。最小取值是 4M。

--oom-kill-disable=false

内存耗尽时是否杀掉容器

--memory-swappiness=""

调节容器内存使用交换分区的选项,取值为 0 和 100 之间的整数 (含 0 和 100)。

-c, --cpu-shares=0

CPU 份额 (相对权重)

--cpu-period=0

完全公平算法中的 period 值

--cpu-quota=0

完全公平算法中的 quota 值

--cpuset-cpus="< 数字 >"

限制容器使用的 cpu 核 (0-3, 0,1)

3、docker资源管理

       Docker资源管理主要涉及4个subsystem

3.1 memory

用于限制cgroup中的进程所使用的内存

子系统常用 cgroups 接口

描述

对应的 docker 接口

cgroup/memory/memory.

limit_in_bytes

设定内存上限,单位是字节,也可以使用 k/K、m/M 或者 g/G 表示要设置数值的单位。

-m, --memory=""

cgroup/memory/memory.

memsw.limit_in_bytes

设定内存加上交换分区的使用总量。通过设置这个值,可以防止进程把交换分区用光。

--memory-swap=""

cgroup/memory/memory.

soft_limit_in_bytes

设定内存限制,但这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会优先回收超过限额的进程占用的内存,使之向限定值靠拢。

--memory-reservation=""

cgroup/memory/memory.

kmem.limit_in_bytes

设定内核内存上限。

--kernel-memory=""

cgroup/memory/memory.

oom_control

如果设置为 0,那么在内存使用量超过上限时,系统不会杀死进程,而是阻塞进程直到有内存被释放可供使用时,另一方面,系统会向用户态发送事件通知,用户态的监控程序可以根据该事件来做相应的处理,例如提高内存上限等。

--oom-kill-disable=""

cgroup/memory/memory.

swappiness

控制内核使用交换分区的倾向。取值范围是 0 至 100 之间的整数(包含 0 和 100)。值越小,越倾向使用物理内存。

--memory-swappiness=""

3.2 cpu

用于限制cgroup中的进程所使用的cpu情况。

子系统常用 cgroups 接口

描述

对应的 docker 接口

cgroup/cpu/cpu.
shares

负责 CPU 比重分配的接口。假设我们在 cgroupfs 的根目录下创建了两个 cgroup(C1 和 C2),并且将 cpu.shares 分别配置为 512 和 1024,那么当 C1 和 C2 争用 CPU 时,C2 将会比 C1 得到多一倍的 CPU 占用率。要注意的是,只有当它们争用 CPU 时 CPU share 才会起作用,如果 C2 是空闲的,那么 C1 可以得到全部的 CPU 资源。

-c, --cpu-shares=""

cgroup/cpu/cpu.

cfs_period_us

负责 CPU 带宽限制,需要与 cpu.cfs_quota_us 搭配使用。我们可以将 period 设置为 1 秒,将 quota 设置为 0.5 秒,那么 cgroup 中的进程在 1 秒内最多只能运行 0.5 秒,然后就会被强制睡眠,直到下一个 1 秒才能继续运行。

--cpu-period=""

cgroup/cpu/cpu.

cfs_quota_us

负责 CPU 带宽限制,需要与 cpu.cfs_period_us 搭配使用。

--cpu-quota=""

3.3 cpuset

为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点

子系统常用 cgroups 接口

描述

对应的 docker 接口

cgroup/cpuset/cpuset.cpus

允许进程使用的 CPU 列表(例如:0-4,9)。

--cpuset-cpus=""

cgroup/cpuset/cpuset.mems

允许进程使用的内存节点列表(例如:0-1)。

--cpuset-mems=""

3.4 blkio

为块设备设定输入 / 输出限制,比如物理设备(磁盘、固态硬盘、USB 等)

子系统常用 cgroups 接口

描述

对应的 docker 接口

cgroup/blkio/blkio.

weight

设置权重值,取值范围是 10 至 1000 之间的整数(包含 10 和 1000)。这跟 cpu.shares 类似,是比重分配,而不是绝对带宽的限制,因此只有当不同的 cgroup 在争用同一个块设备的带宽时,才会起作用。

--blkio-weight=""

cgroup/blkio/blkio.

weight_device

对具体的设备设置权重值,这个值会覆盖上述的 blkio.weight。

--blkio-weight-device=""

cgroup/blkio/blkio.

throttle.read_bps_device

对具体的设备,设置每秒读块设备的带宽上限。

--device-read-bps=""

cgroup/blkio/blkio.

throttle.write_bps_device

设置每秒写块设备的带宽上限。同样需要指定设备。

--device-write-bps=""

cgroup/blkio/blkio.

throttle.read_iops_device

设置每秒读块设备的 IO 次数的上限。同样需要指定设备。

--device-read-iops=""

cgroup/blkio/blkio.

throttle.write_iops_device

设置每秒写块设备的 IO 次数的上限。同样需要指定设备。

--device-write-iops=""

4、Docker 对 cgroups 的使用

默认情况下,Docker 启动一个容器后,会在 /sys/fs/cgroup 目录下的各个资源目录下生成以容器 ID 为名字的目录(group),比如:

/sys/fs/cgroup/cpu/docker/03dd196f415276375f754d51ce29b418b170bd92d88c5e420d6901c32f93dc14

此时 cpu.cfs_quota_us 的内容为 -1,表示默认情况下并没有限制容器的 CPU 使用。在容器被 stopped 后,该目录被删除。

例1:cpu && memory

运行命令:

docker run -d --name web41 --cpu-quota 25000 --cpu-period 100 --cpu-shares 30 training/webapp python app.py

启动一个新的容器,进入该目录结果:

$ cat cpu.cfs_quota_us

25000

$ cat tasks

3704

$ cat cpu.cfs_period_us

2000

Docker 会将容器中的进程的 ID 加入到各个资源对应的 tasks 文件中。表示 Docker 也是以上面的机制来使用 cgroups 对容器的 CPU 使用进行限制。

 

相似地,可以通过 docker run 中 mem 相关的参数对容器的内存使用进行限制:

如:

docker run -d --name web42 --blkio-weight 100 --memory 10M --cpu-quota 25000 --cpu-period 2000 --cpu-shares 30 training/webapp python app.py:

结果:

$ cat memory.limit_in_bytes

10485760

$ cat blkio.weight

100

例2: net_cls && tc

net_cls 和 tc 一起使用可用于限制进程发出的网络包所使用的网络带宽。当使用 cgroups network controll net_cls 后,指定进程发出的所有网络包都会被加一个 tag,然后就可以使用其他工具比如 iptables 或者 traffic controller (TC)来根据网络包上的 tag 进行流量控制。

 

关于 classid,它的格式是 0xAAAABBBB,其中,AAAA 是十六进制的主ID(major number),BBBB 是十六进制的次ID(minor number)。因此,0X10001 表示 10:1,而 0x00010001 表示 1:1。

 

1)首先在host 的网卡 eth0 上做如下设置:

tc qdisc del dev eth0 root                                        #删除已有的规则

tc qdisc add dev eth0 root handle 10: htb default 12             

tc class add dev eth0 parent 10: classid 10:1 htb rate 1500kbit ceil 1500kbit burst 10k         #限速

tc filter add dev eth0 protocol ip parent 10:0 prio 1 u32 match ip protocol 1 0xff flowid 10:1  #只处理 ping 参数的网络包

 

其结果是:

 

在网卡 eth0 上创建了一个 HTB root 队列,handle 10: 表示队列句柄也就是major number 为 10

创建一个分类 10:1,限制它的出发网络带宽为 80 kbit (千比特每秒)

创建一个分类器,将 eth0 上 IP IMCP 协议 的 major ID 为 10 的 prio 为 1 的网络流量都分类到 10:1 类别

 

2)启动容器

 

容器启动后,其 init 进程在host 上的 PID 就被加入到 tasks 文件中了:

$ ps -ef | grep 10047

231072   10047 10013  1 07:08 ?        00:00:00 python app.py

 

设置 net_cls classid:

echo 0x100001 > net_cls.classid

 

再在容器启动一个 ping 进程,其 ID 也被加入到 tasks 文件中了。

 

3)查看tc 情况: tc -s -d class show dev eth0

class htb 10:1 root prio 0 rate 1500Kbit ceil 1500Kbit burst 10Kb cburst 1599b

Sent 17836 bytes 182 pkt (dropped 0, overlimits 0 requeues 0)

rate 0bit 0pps backlog 0b 0p requeues 0

lended: 182 borrowed: 0 giants: 0

tokens: 845161 ctokens: 125161

 

我们可以看到 tc 已经在处理 ping 进程产生的数据包了。再来看一下 net_cls 和 ts 合作的限速效果:

设置:tc class add dev eth0 parent 10: classid 10:1 htb rate 1500kbit ceil 10Mbit burst 10k

10488 bytes from 192.168.1.1: icmp_seq=35 ttl=63 time=12.7 ms

10488 bytes from 192.168.1.1: icmp_seq=36 ttl=63 time=15.2 ms

 

设置:tc class add dev eth0 parent 10: classid 10:1 htb rate 1500kbit ceil 15kbit burst 10k

10488 bytes from 192.168.1.1: icmp_seq=37 ttl=63 time=4805 ms

10488 bytes from 192.168.1.1: icmp_seq=38 ttl=63 time=9543 ms

例3:--memory-reservation

取值范围: 大于等于 0 的整数

单位:b,k,m,g

对应的 cgroup 文件是 cgroup/memory/memory.soft_limit_in_bytes。

 

通常情况下,容器能够使用的内存量仅仅由 -m/--memory 选项限定。如果设置了 --memory-reservation 选项,当内存使用量超过 --memory-reservation 选项所设定的值时,系统会强制容器执行回收内存的操作,使得容器内存消耗不会长时间超过 --memory-reservation 的限定值。

 

这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会回收部分内存,使内存使用量向限定值靠拢。

 

在以下命令中,容器对内存的使用量不会超过 500M,这是硬性限制。当内存使用量大于 200M 而小于 500M 时,系统会尝试回收部分内存,使得内存使用量低于 200M。

$ docker run -it -m 500M --memory-reservation 200M ubuntu:14.04 bash

 

在如下命令中,容器使用的内存量不受限制,但容器消耗的内存量不会长时间超过 1G,因为当容器内存使用量超过 1G 时,系统会尝试回收内存使内存使用量低于 1G。

$ docker run -it --memory-reservation 1G ubuntu:14.04 bash

 

你可能感兴趣的:(Docker,Docker之Linux,Cgroups,Docker使用Linux,Cgroups,Linux)