Docker资源限制与Cgroups(转)

Docker资源限制与Cgroups

一、Linux control groups

简介

Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中。

主要提供了如下功能:

  • Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
  • Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
  • Accounting: 一些审计或一些统计,主要目的是为了计费。
  • Control: 挂起进程,恢复执行进程。

CGroup的术语

任务(Tasks):就是系统的一个进程。

控制组(Control Group):一组按照某种标准划分的进程,其表示了某进程组,Cgroups中的资源控制都是以控制组为单位实现,一个进程可以加入到某个控制组。而资源的限制是定义在这个组上,简单点说,cgroup的呈现就是一个目录带一系列的可配置文件。

层级(Hierarchy):控制组可以组织成hierarchical的形式,既一颗控制组的树(目录结构)。控制组树上的子节点继承父结点的属性。简单点说,hierarchy就是在一个或多个子系统上的cgroups目录树。

子系统(Subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多,也在不断增加中。以下为内核3.10+支持的子系统(可以通过 ls /sys/fs/cgroup 查看到):

  • blkio — 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。

  • cpu — 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。

  • cpuacct — 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。

  • cpuset — 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。

  • devices — 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。

  • freezer — 这个子系统挂起或者恢复 cgroup 中的任务。

  • memory — 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成内存资源使用报告。

  • net_cls — 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。

  • net_prio — 这个子系统用来设计网络流量的优先级

  • hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。

操作接口

在linux系统中一皆文件,当然对CGroup的接口操作也是通过文件来实现的,我们可以通过mount命令查看其挂载目录:

以上目录都可以是限制的对象,也就是对应的术语中的各个子系统。例如查看这里的CPU限制目录:

如果你熟悉CPU管理的话,cfs_period 和 cfs_quota两个可能不会陌生,这两个参数需要组合使用,可以用来限制进程在长度为cfs_period 的一段时间内,只能被分配到总量为cfs_quota 的 CPU 时间。以下示例将会演示如何限制。

限制CPU

限制CPU的使用需要在子系统目录(/sys/fs/cgroup/cpu)下创建目录,这里创建一个cpu_limit_demo目录:

可以看到当我们在CPU子系统下创建目录时候,其对应的限制配置文件会自动进行创建,现在在后台写个死循环跑满CPU:

此时使用top命令查看pid为10069的进程CPU使用状况:

如图可见,此时该进程使用的CPU1已经是100%,即%Cpu :100.0 us,我们先来查看我们刚才创建的cpu_limit_demo目录下这两个参数默认值是多少:

cpu.cfs_quota_us值为-1代表没有任何限制,cpu.cfs_period_us 则是默认的 100 ms,即100000us,下面将向cpu_limit_demo控制组的cpu.cfs_quota_us文件写入50ms即50000us,这表示在100ms周期内,cpu最多使用%50,同时将该进程的pid号为10069写入对应的tasks文件,表示对那个进程限制:

image

于是pid为10069的进程cpu就被限制成了%Cpu :50.0 us,此时利用top在此查看cpu使用情况(top后按1可看到每个逻辑cpu具体使用):

image

以上可以看到此时pid10069的进程使用率已经变成了50%了,说明限制生效了。同样的道理,在/sys/fs/cgroup下的子系统都可以限制不通的资源使用如Block IO、Memory等。

限制内存

首先在 /sys/fs/cgroup/memory 下新建一个名为 limit_memory_demo 的 cgroup:

mkdir /sys/fs/cgroup/memory/limit_memory_demo

限制该 cgroup 的内存使用量为 500 MB:

# 物理内存500M(下面单位Byte) MB并且不使用swap
echo 524288000 > /sys/fs/cgroup/memory/limit_memory/memory.limit_in_bytes

echo 0 > /sys/fs/cgroup/memory/limit_memory/memory.swappiness

最后将需要限制的进程号的pid写入task文件就可以了:

echo [pid] > /sys/fs/cgroup/memory/limit_memory_demo/tasks

限制磁盘IO

使用dd写到null设备:

[root@app51 blkio]# dd if=/dev/sda of=/dev/null  bs=1M

使用iotop(安装使用yum install -y iotop)查看io速率,此时读的速率为2.17G/s

image.png

创建一个blkio(块设备IO)的cgroup,并查看设备号,然后将pid和限制的设备以及速度写入文件:

image.png

然后在观察dd命令的进程:10178,如图显示该进程读的的速率已经变成了3.8M/s了

image

注:8:0 是设备号,你可以通过ls -l /dev/sda1查看。

可以简写为以下步骤:

mkdir /sys/fs/cgroup/blkio/ echo '8:0 1048576'  > /sys/fs/cgroup/blkio/limit_io/blkio.throttle.read_bps_device
echo [pid] > /sys/fs/cgroup/blkio/limit_io/tasks

二、 Docker中资源限制原理

在了解了Cgroup对资源的限制方法,再来理解Docker中的资源限制其实就变的容易了。默认情况下,Docker会在需要限制的子系统下创建一个目录为docker的控制组如下:

当容器运行后,会在这些目录生成以容器ID为目录的子目录用于限制的容器资源。例如,以cpu为例,docker run启动参数中--cpu-quota 参数可以指定默认时间内使用的cpu:

[root@app51 docker]# docker run -d --name nginx-c1 --cpu-quota 50000  nginx:latest 
e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29
[root@app51 docker]#

此时我们查看cpu对应的容器资源限制:

[root@app51 docker]# cd /sys/fs/cgroup/cpu/docker/
[root@app51 docker]# cat e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29/cpu.cfs_quota_us
50000 [root@app51 docker]# cat e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29/tasks 
10561
10598

从上面结果我们能看出,docker run的启动限制参数值,被注入到了Cgroups的cpu控制组的对应配置文件中了。同时还有看到有两个进程同时被限制了,这是因为我们启动的nginx,nginx的主进程会启动多个子进程,使用ps -ef可以查看:

[root@app51 docker]# ps -ef |grep 10561
root     10561 10544  0 16:32 ?        00:00:00 nginx: master process nginx -g daemon off; 101      10598 10561  0 16:32 ?        00:00:00 nginx: worker process
root 10614 10179  0 16:38 pts/2    00:00:00 grep --color=auto 10561 
[root@app51 docker]#

不难看到,其中主进程为10561(nginx master),子进程为10598(nginx worker)。以上则是docker的资源限制原理。Docker对资源限制主要是CPU和内存,其中还有很多参数可以使用,以下将会介绍docker中CPU和MEMERY限制参数。

三、CPU限制参数

默认情况下,容器的资源是不受限制的,宿主机提供多少资源,容器就可以使用多少资源,如果不对容器做资源限制,很有可能一个或几个容器把宿主机的资源耗尽,而导致应用或者服务不可用。所以对容器资源的限制显得非常重要,而docker主要提供两种类别的资源限制:CPU和内存,通过docker run 时指定参数实现。cpu限制资源限制有多种维度,以下将详细介绍。

限制cpu配额

参数通过--cpu-period=<value>和--cpu-quota=<value>共同起作用,即介绍上述Cgroups使用的例子。表示在cpu-period时间(默认100ms)内,可用的cpu配额。

示例:

docker run -d --cpu-period=100000 --cpu-quota=250000 --name test-c1 nginx:latest

限制cpu可用数量

参数通过--cpus=<value>指定,意思限制可用cpu个数,列如--cpus=2.5表示该容器可使用的cpu个数最多是2.5个,这相当与设置了--cpu-period=100000和 --cpu-quota=250000该指令在docker版本1.13以及以上使用。

示例:

[root@app51 ~]# docker run -d --cpus=2 --name test-c2 nginx:latest
5347269d0974e37af843b303124d8799c6f4336a14f61334d21ce9356b1535bc

使用固定的cpu

通过--cpuset-cpus参数指定,表示指定容器运行在某个或某些个固定的cpu上,多个cpu使用逗号隔开。例如四个cpu,0代表第一个cpu,--cpuset-cpus=1,3代表该容器只能运行在第二个或第四个cpu上。查看cpu可以通过cat /proc/cpuinfo查看。

示例:

[root@app51 ~]# docker run -d --cpuset-cpus=1,3 --name test-c3 nginx:latest  
 276056fce04982c2de7969ca309560ce60b0ebf960cf7197808616d65aa112d4

设置CPU比例(权重)

通过参数--cpu-shares指定,值为大于或小于1024的整数(默认1024),代表当主机cpu资源使用紧张时,每个容器所使用的cpu资源的比例(权重)。当有足够的CPU资源时,所有容器都会根据需要使用尽可能多的CPU。当第一个容器设置该参数的值为2048,第二个容器设置该参数的值为4096,那么当cpu资源不够用时候,两个容器cpu资源使用比例为1:2,

示例:

[root@app51 ~]# docker run -d --cpu-shares=2048 --name test-c4 nginx:latest 
578506d61324b38d7a01bf1d2ec87cb5d1ab50276ef6f7b28858f2d2e78b2860
[root@app51 ~]# docker run -d --cpu-shares=4096 --name test-c5 nginx:latest 
d56a90bf080b70d11d112468348874e48fe4a78d09d98813a0377b34fa382924

四、 MEMORY限制参数

内存是一种不可压缩资源,一旦某个进程或者容器中应用内存不足就会引起OOM异常(Out Of Memory Exception),这将导致应用不可用,并且在Linux主机上,如果内核检测到没有足够的内存来执行重要的系统功能,系统会按照一定优先级杀死进程来释放系统内存。docker对内存的限制分为swap限制和物理内存限制,单位可以使用b,k, m,g。

限制最大物理内存使用

通过-m或者—memory指定,是硬限制,如果设置此选项,则允许设置的最小值为4m,该参数是最常用参数。

示例:

[root@app51 ~]# docker run -d  --memory=512m --name mem-c1 nginx:latest 
67b0cb645c401bc6df3235d27d629185870716351396c71dfa3877abbbd377c8

限制swap使用

通过--memory-swap参数指定,不过此参数设置时候,情况比较较多。当容器中的物理内存使用不足时候,swap才会被允许使用,所以当--memory参数设置时候,此参数才会有意义:

  • --memory-swap 设置为正整数,那么这两个--memory和 --memory-swap 必须设置。--memory-swap 表示可以使用的memory 和 swap,并且--memory控制非交换内存(物理内存)使用的量。列如--memory=300m 和--memory-swap=1g,容器可以使用300m的内存和700m(1g - 300m)swap。
  • --memory-swap 设置为0,则忽略该设置,并将该值视为未设置。
  • --memory-swap 设置为与值相同的值--memory,并且--memory设置为正整数,则容器不能使用swap,可以通过此方法限制容器不使用swap。
  • --memory-swap 未设置并--memory 设置,则容器可以使用两倍于--memory设置的swap,主机容器需要配置有swap。例如,如果设置--memory="300m" 和--memory-swap 未设置,容器可以使用300m的内存和600m的swap。
  • --memory-swap 设置为-1,则允许容器使用无限制swap,最多可达宿主机系统上可用的数量。

示例:

[root@app51 ~]# docker run -d  --memory=512m --memory-swap=512m --name mem-c2 nginx:latest 
6b52c015a53be2c3e0e509eea918125a760c1c14df4cc977f05b5b31b83161d5

其他

  • --oom-kill-disable :默认情况下,如果发生内存不足(OOM)错误,内核会终止容器中的进程,如要更改此行为,使用该--oom-kill-disable选项,但是该选项有个前提就是-m选项必须设置。
  • --memory-swappiness:默认情况下,容器的内核可以交换出一定比例的匿名页。--memory-swappiness就是用来设置这个比例的。--memory-swappiness可以设置为从 0 到 100。0 表示关闭匿名页面交换。100 表示所有的匿名页都可以交换。默认情况下,如果不适用--memory-swappiness,则该值从父进程继承而来。
  • --memory-reservation:Memory reservation 是一种软性限制,用于限制物理内存的最大用值,它只是确保容器不会长时间占用超过--memory-reservation限制的内存大小。给--memory-reservation设置一个比-m小的值后,虽然容器最多可以使用-m使用的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到--memory-reservation设置的值大小。

五、总结

本文简单介绍了Linux Cgroups以及它对资源限制的使用方法,并提供了简单的示例。此外还介绍了Docker如何使用该方式对容器进行资源限制,本质上是docker run时将限制参数注入到Cgroups的各个资源限制的配置文件中,从而达到对容器的资源限制。希望大家对容器的资源限制有一定的理解。

参考:

《DOCKER 基础技术:LINUX CGROUP》

《Limit a container's resources》

Memo

本文转载整理来源: https://www.cnblogs.com/wdliu/p/10509045.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 初中同学突然发信息给我,说是她邻居让她帮助寻找七十年代送给别人的她的弟弟,送给的地方就是我们单位,收养者还是当...
    山灵水动阅读 778评论 0 1
  • 函数声明和函数表达式有什么区别 使用函数声明,如果函数被提前调用也是可以正常运行的;如果使用函数表达式,和变量提升...
    饥人谷_严琰阅读 227评论 0 0