未加星标

docker容器资源配额控制详解

字体大小 | |
[运维安全 所属分类 运维安全 | 发布者 店小二05 | 时间 | 作者 红领巾 ] 0人收藏点击收藏
docker通过cgroup来控制容器使用的资源配额,包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。

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上的线程。
目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。
可以使用stress工具来测试CPU和内存。使用下面的Dockerfile来创建一个基于Ubuntu的stress工具镜像。

FROM ubuntu:14.04
RUN apt-get update &&apt-get install stress

CPU资源配额控制

CPU份额控制

docker提供了Ccpu-shares参数,在创建容器时指定容器所使用的CPU份额值。使用示例:
使用命令docker run -tid Ccpu-shares 100 ubuntu:stress,创建容器,则最终生成的cgroup的cpu份额配置可以下面的文件中找到:

[email protected]:~# cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.shares
100
cpu-shares的值不能保证可以获得1个vcpu或者多少GHz的CPU资源,仅仅只是一个弹性的加权值。
默认情况下,每个docker容器的cpu份额都是1024。单独一个容器的份额是没有意义的,只有在同时运行多个容器时,容器的cpu加权的效果才能体现出来。例如,两个容器A、B的cpu份额分别为1000和500,在cpu进行时间片分配的时候,容器A比容器B多一倍的机会获得CPU的时间片,但分配的结果取决于当时主机和其他容器的运行状态,实际上也无法保证容器A一定能获得CPU时间片。比如容器A的进程一直是空闲的,那么容器B是可以获取比容器A更多的CPU时间片的。极端情况下,比如说主机上只运行了一个容器,即使它的cpu份额只有50,它也可以独占整个主机的cpu资源。
cgroups只在容器分配的资源紧缺时,也就是说在需要对容器使用的资源进行限制时,才会生效。因此,无法单纯根据某个容器的cpu份额来确定有多少cpu资源分配给它,资源分配结果取决于同时运行的其他容器的cpu分配和容器中进程运行情况。

CPU周期控制

docker提供了Ccpu-period、Ccpu-quota两个参数控制容器可以分配到的CPU时钟周期。Ccpu-period是用来指定容器对CPU的使用要在多长时间内做一次重新分配,而Ccpu-quota是用来指定在这个周期内,最多可以有多少时间用来跑这个容器。跟Ccpu-shares不同的是这种配置是指定一个绝对值,而且没有弹性在里面,容器对CPU资源的使用绝对不会超过配置的值。
cpu-period和cpu-quota的单位为微秒(μs)。cpu-period的最小值为1000微秒,最大值为1秒(10^6 μs),默认值为0.1秒(100000 μs)。cpu-quota的值默认为-1,表示不做控制。
举个例子,如果容器进程需要每1秒使用单个CPU的0.2秒时间,可以将cpu-period设置为1000000(即1秒),cpu-quota设置为200000(0.2秒)。当然,在多核情况下,如果允许容器进程需要完全占用两个CPU,则可以将cpu-period设置为100000(即0.1秒),cpu-quota设置为200000(0.2秒)。
使用示例:
使用命令docker run -tid Ccpu-period 100000 Ccpu-quota 200000 ubuntu,创建容器,则最终生成的cgroup的cpu周期配置可以下面的文件中找到:

[email protected]:~# cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.cfs_period_us
100000
[email protected]:~# cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.cfs_quota_us
200000
关于cpu-shares、cpu-period、cpu-quota这些配置的详细介绍,大家可以深入阅读RedHat文档中关于CPU的这一章。

CPU core控制

对多核CPU的服务器,docker还可以控制容器运行限定使用哪些cpu内核和内存节点,即使用Ccpuset-cpus和Ccpuset-mems参数。对具有NUMA拓扑(具有多CPU、多内存节点)的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置。如果服务器只有一个内存节点,则Ccpuset-mems的配置基本上不会有明显效果。
使用示例:
命令docker run -tid Cname cpu1 Ccpuset-cpus 0-2 ubuntu,表示创建的容器只能用0、1、2这三个内核。最终生成的cgroup的cpu内核配置如下:

[email protected]:~# cat /sys/fs/cgroup/cpuset/docker/<容器的完整长ID>/cpuset.cpus
0-2
通过docker exec <容器ID> taskset -c -p 1(容器内部第一个进程编号一般为1),可以看到容器中进程与CPU内核的绑定关系,可以认为达到了绑定CPU内核的目的。
docker容器资源配额控制详解

CPU配额控制参数的混合使用

当上面这些参数中时,cpu-shares控制只发生在容器竞争同一个内核的时间片时,如果通过cpuset-cpus指定容器A使用内核0,容器B只是用内核1,在主机上只有这两个容器使用对应内核的情况,它们各自占用全部的内核资源,cpu-shares没有明显效果。
cpu-period、cpu-quota这两个参数一般联合使用,在单核情况或者通过cpuset-cpus强制容器使用一个cpu内核的情况下,即使cpu-quota超过cpu-period,也不会使容器使用更多的CPU资源。
cpuset-cpus、cpuset-mems只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
在系统具有多个CPU内核的情况下,需要通过cpuset-cpus为容器CPU内核才能比较方便地进行测试。
试用下列命令创建测试用的容器:

docker run -tid Cname cpu2 Ccpuset-cpus 3 Ccpu-shares 512 ubuntu:stress stress -c 10
docker run -tid Cname cpu3 Ccpuset-cpus 3 Ccpu-shares 1024 ubuntu:stress stress -c 10
上面的ubuntu:stress镜像安装了stress工具来测试CPU和内存的负载。两个容器的命令stress -c 10&,这个命令将会给系统一个随机负载,产生10个进程,每个进程都反复不停的计算由rand()产生随机数的平方根,直到资源耗尽。
观察到宿主机上的CPU试用率如下图所示,第三个内核的使用率接近100%,并且一批进程的CPU使用率明显存在2:1的使用比例的对比:
docker容器资源配额控制详解
容器cpu2的CPU使用如下所示:
docker容器资源配额控制详解
容器cpu3的CPU使用如下图示:
docker容器资源配额控制详解
分别进入容器后,使用top命令可以明显地看出容器之间的资源使用对比,并且也达到了绑定CPU内核的目的。
注意:如果使用nsenter之类的工具进入容器,再使用stress -c 10进行测试,就可以发现cpuset-cpus的限制是可以被突破的,从而使stress测试进程使用宿主机的所有CPU内核。这是因为nsenter使用挂载的方式直接进入了容器的命名空间,突破了命名空间中的cgroup控制。

内存配额控制

和CPU控制一样,docker也提供了若干参数来控制容器的内存使用配额,可以控制容器的swap大小、可用内存大小等各种内存方面的控制。主要有以下参数:
memory-swappiness:控制进程将物理内存交换到swap分区的倾向,默认系数为60。系数越小,就越倾向于使用物理内存。值范围为0-100。当值为100时,表示尽量使用swap分区;当值为0时,表示禁用容器 swap 功能(这点不同于宿主机,宿主机 swappiness 设置为 0 也不保证 swap 不会被使用)。
Ckernel-memory:内核内存,不会被交换到swap上。一般情况下,不建议修改,可以直接参考docker的官方文档。
Cmemory:设置容器使用的最大内存上限。默认单位为byte,可以使用K、G、M等带单位的字符串。
Cmemory-reservation:启用弹性的内存共享,当宿主机资源充足时,允许容器尽量多地使用内存,当检测到内存竞争或者低内存时,强制将容器的内存降低到memory-reservation所指定的内存大小。按照官方说法,不设置此选项时,有可能出现某些容器长时间占用大量内存,导致性能上的损失。
Cmemory-swap:等于内存和swap分区大小的总和,设置为-1时,表示swap分区的大小是无限的。默认单位为byte,可以使用K、G、M等带单位的字符串。如果Cmemory-swap的设置值小于Cmemory的值,则使用默认值,为Cmemory-swap值的两倍。
默认情况下,容器可以使用主机上的所有空闲内存。
与CPU的cgroups配置类似,docker会自动为容器在目录/sys/fs/cgroup/memory/docker/<容器的完整长ID>中创建相应cgroup配置文件,例如下面的文件:
docker容器资源配额控制详解
这些文件与docker的相关配置是一一对应的,可以参考RedHat的文档Resource_Management_Guide的内存部分来查看它们的作用。

内存配额控制使用示例

设置容器的内存上限,参考命令如下所示:

docker run -tid ―name mem1 ―memory 128m ubuntu:stress /bin/bash
默认情况下,除了Cmemory指定的内存大小以外,docker还为容器分配了同样大小的swap分区,也就是说,上面的命令创建出的容器实际上最多可以使用256MB内存,而不是128MB内存。如果需要自定义swap分区大小,则可以通过联合使用CmemoryCswap参数来实现控制。
对上面的命令创建的容器,可以查看到在cgroups的配置文件中,查看到容器的内存大小为128MB (128×1024×1024=134217728B),内存和swap加起来大小为256MB (256×1024×1024=268435456B)。

cat /sys/fs/cgroup/memory/docker/<容器的完整ID>/memory.limit_in_bytes
134217728
cat /sys/fs/cgroup/memory/docker/<容器的完整ID>/memory.memsw.limit_in_bytes
268435456
注意:执行上述命令时,命令行可能会输出下面的警告:
WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.
这是因为主机上默认不启用cgroup来控制swap分区,可以参考docker官方的相应文档,修改grub启动参数。
在容器中,依次使用下面的stress命令,即可对容器的内存进行压力测试,确认内存。

stress Cvm 1 Cvm-bytes 256M Cvm-hang 0 &
stress Cvm 1 Cvm-bytes 250M Cvm-hang 0 &
docker容器资源配额控制详解
可以发现,使用256MB进行压力测试时,由于超过了内存上限(128MB内存+128MB swap),进程被OOM杀死。使用250MB进行压力测试时,进程可以正常运行,并且通过docker stats可以查看到容器的内存已经满负载了。
docker容器资源配额控制详解

磁盘IO配额控制

相对于CPU和内存的配额控制,docker对磁盘IO的控制相对不成熟,大多数都必须在有宿主机设备的情况下使用。主要包括以下参数:

Cdevice-read-bps:限制此设备上的读速度(bytes per second),单位可以是kb、mb或者gb。
Cdevice-read-iops:通过每秒读IO次数来限制指定设备的读速度。
Cdevice-write-bps :限制此设备上的写速度(bytes per second),单位可以是kb、mb或者gb。
Cdevice-write-iops:通过每秒写IO次数来限制指定设备的写速度。
Cblkio-weight:容器默认磁盘IO的加权值,有效值范围为10-100。
Cblkio-weight-device: 针对特定设备的IO加权控制。其格式为DEVICE_NAME:WEIGHT

磁盘IO配额控制示例

blkio-weight
要使Cblkio-weight生效,需要保证IO的调度算法为CFQ。可以使用下面的方式查看:

[email protected]:~# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq
使用下面的命令创建两个Cblkio-weight值不同的容器:

docker run -ti Crm Cblkio-weight 100 ubuntu:stress
docker run -ti Crm Cblkio-weight 1000 ubuntu:stress
在容器中同时执行下面的dd命令,进行测试:

time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct
最终输出如下图所示:
docker容器资源配额控制详解
在我的测试环境上没有达到理想的测试效果,通过docker官方的blkio-weight doesn't take effect in docker Docker version 1.8.1 #16173,可以发现这个问题在一些环境上存在,但docker官方也没有给出解决办法。
device-write-bps
使用下面的命令创建容器,并执行命令验证写速度的限制。

docker run -tid Cname disk1 Cdevice-write-bps /dev/sda:1mb ubuntu:stress
通过dd来验证写速度,输出如下图示:
docker容器资源配额控制详解
可以看到容器的写磁盘速度被成功地限制到了1MB/s。device-read-bps等其他磁盘IO限制参数可以使用类似的方式进行验证。

容器空间大小限制

在docker使用devicemapper作为存储驱动时,默认每个容器和镜像的最大大小为10G。如果需要调整,可以在daemon启动参数中,使用dm.basesize来指定,但需要注意的是,修改这个值,不仅仅需要重启docker daemon服务,还会导致宿主机上的所有本地镜像和容器都被清理掉。
使用aufs或者overlay等其他存储驱动时,没有这个限制。
~~~以上所有截图测试环境,宿主机为Ubuntu 14.04.4,docker版本为1.10.3~~~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

本文运维安全相关术语:linux服务器代维 linux服务器搭建 运维管理 运维工程师 企业安全文章 企业安全管理 cf安全系统检测到游戏数据异常

分页:12
转载请注明
本文标题:docker容器资源配额控制详解
本站链接:http://www.codesec.net/view/568285.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 运维安全 | 评论(0) | 阅读(17)