目录
原理介绍
组织结构和基本规则介绍
规则一:同一个Hierarchy(层级)可以附加一个或者多个subsystem(子系统)
规则二:一个子系统可以附加到多个层级,当且仅当目标层级只有唯一一个子系统时。
规则三:一个tasks不能存在于同一层级的不同cgroup中,但一个tasks可以存在于不同层级中的多个cgroup中。
规则四:tasks任务在fork/clone自身创建的子任务默认与原任务在同一个cgroup中,但是子任务允许被移动到不同的cgroup中。
测试用例
CPU限制
内存限制
cgroup不仅可以限制被namespace隔离起来的资源,还可以为我们的资源设置权重,操控进程,停止进程等操作。
我们这里重点学习cgroup怎么实现限制的,以配置为准,它整个配置是通过伪文件系统配置文件的方式来修改配置的。所以我们最终理解一下它的作用即可。
所以cgroup和我们之前的通俗的理解上用户和组的关系是类似的,把用户放到组里面,然后对组限制权限,然后组的目录又分为父目录,子目录,有继承关系,不同目录的继承关系有一个规定,就是入乡随俗,跨目录又不行。。。
我们namespace划分的是空间资源的东西,划分出来一个隔离的空间,这个空间跟我们完全解耦的虚拟化有一个最大的区别在于就是没有hypervisor那一层,这一层可以隔离我们的物理资源,那么这个时候cgroup就起到我们隔离物理资源的管控的工具。那么cgroup起到的作用就是物理资源的管控。
虚拟机和docker容器的区别
我们常见的物理资源有:CPU,memory,IO.......
我们物理硬件资源是为了给虚拟化提供一种叫做基础的运行保障,它是构成docker一系列虚拟化管理当中的基础 。
从开发的角度来看,cgroup有以下几个特点
cgroup的作用
cgroup的术语
分析:
1. 其实每一个程序里面表示进程都由task来表示的,我们随便找个进程验证一下
[root@master1 ~]# ls /proc/577/task/
577
[root@master1 ~]#
我们在进到577这个进程里面,可以看到进程里面的内容
2. 关于subsystem子系统,我们可以先安装一个工具 yum install libcgroup-tools -y
可以看到系统当中所有的subsystem内容
通过名字我们就可以发现,是对哪一项进行控制的。
3. 现在我们进入到sys这个伪文件系统的目录下(注意我们之前学习proc也是一个伪文件系统,这个是内存的)
对上面的目录分析:
这里我们只要关注一下内存和CPU即可,这个是我们经常用到的。。。
我们手动在/sys/fs/cgroup/cpu目录下创建一个目录,会看见系统会为我们默认生成一堆文件。
通过控制这些文件里面的数值,就可以对资源进行限制。比如cpu.cfs_quota_us文件,如果我们往里写入100000(十万),那么就证明使用了xjjdog的cgroup,最多能够使用1核的CPU。写入20000,证明最多使用使用1/5核的CPU,这是因为,cpu.cfs_period_us这个配置文件,默认把1核cpu分成了10万份。。。
分析:假如我们这里有一个Cgroup Hierarchy A的程序,它有自己的cgroup,我们这里给它取个名字就叫做cpu_mem_cg,同时这个cgroup它又有自己的两个cgroup,一个叫cg1,一个叫做cg2,这些组成了一个Hierarchy(层级关系),然后我们这里有一个CPU,Memory作为subsystem,假如说我们现在想要对Cgroup Hierarchy A程序里面的cgroup进行控制的话,那么CPU,Memory就可以附加到我们Cgroup Hierarchy A程序中来。这个就是规则一的简单介绍,同一个Hierarchy(层级)可以附加一个或者多个subsystem(子系统)。
扩展:其实我们cgroup里面都是有tasks的,我们cg1里面都是有tasks的。这里我们以cpu这个目录为例,然后看到里面的tasks里面全是进程编号,它的意思就是,我们tasks里面的进程都要遵循cpu这个目录里面的控制,里面的每一个选项(cpu目录里面的文件,例如:cpu.shares,cpu.stat等等)都代表一个规则。
例如:最常见的cpu.shares,我们cat一下它的值,1024,表示tasks里面的所有进程使用cpu share的有限就为1024。
[root@master1 cpu]# cat cpu.shares
1024
现在假设说我们现在还有有一 个Cgroup Hierarchy B的程序,我们的Memory没有附加在Cgroup Hierarchy A上,而是附加在Cgroup Hierarchy B上了。Cgroup Hierarchy A对CPU的subsystem进行了附加,Cgroup Hierarchy B对Memory的CPU进行了附加。如果我们要想把CPU附加到Cgroup Hierarchy B这个节点上来,这种跨平台的方式是不被允许的。这个就叫做一个已经附加到某个Hierarchy 的subsystem不能附加到别的Hierarchy 的subsystem上。
如果真的想用,那么只能附加到一个没有subsystem的Hierarchy节点上。
具体的业务场景就是,我们的一个tasks既想被我们的CPU控制,也想被我们的Memory控制,
那么tasks里面的值也要放到对应的cgroup层级中去。。。
假如我们现在有两个Hierarchy A和B,现在突然冒出一个进程,例如:httpd,进程号为1234,这个进程可以放到A程序中的cg1或者B程序中的cg1。但是如果放到了A程序的cg1中,那么就不能放到A程序中的cg2中。原因其实很简单,如果我们的cg1是限制CPU的30%,cg2是限制50%,那么最后听谁的呢。。。
例如我们还是以一个Apache的进程为例,我们httpd刚开始属于cg1,我们知道Apache每建立一个链接,就要fork一个子进程,这个子进程在初始状态的时候一定跟它的父进程在同一个cgroup里面(这里的cg1),我们对子进程进行一个特殊的设置,子进程运行一段时间之后,子进程可以移动到不同的cgroup中去。
上面的都是理论部分,下面我进行一些小实验来验证一下
这里我们先下载一个镜像progrium/stress,通过这个镜像来验证我们的试验是否生效。
下载完成之后,按理说我们需要将宿主机调整为单核CPU,这样效果更明显,但是我的VMware开启单核就起不来虚拟机了,这里我改成2核进行试验。顺便清理一下防火墙
[root@master1 ~]# iptables -F
[root@master1 ~]# iptables-save
[root@master1 ~]# systemctl restart docker
前面的准备工作完成之后,就可以开始了
根据上面的理论说明,我们默认的cpu-shares是1024,这里我们启动一个容器,修改为512看看
[root@master1 ~]# docker run -itd --name AA --cpu-shares 512 centos:7 bash
此时我们进去/sys/fs/cgroup/cpu/下面去看看
[root@master1 cpu]# pwd
/sys/fs/cgroup/cpu
[root@master1 cpu]# cat cpu.shares
1024
[root@master1 cpu]#
然而并没有改变,还是默认的1024,其实是我们找错目录了,
我们可以看到进程编号2619其实就是我们开启的容器里的进程编号。
上面的目录关系我们理解一下
当然我们也可以手动的修改这个值
[root@master1 811baf188595e976c6cbee3017299f7b6e191dcfc471529b3feb6eb07d24d76a]# echo 2048 > cpu.shares
[root@master1 811baf188595e976c6cbee3017299f7b6e191dcfc471529b3feb6eb07d24d76a]# cat cpu.shares
2048
[root@master1 811baf188595e976c6cbee3017299f7b6e191dcfc471529b3feb6eb07d24d76a]#
接下来我们使用stress这个容器具体看一下限制之后的效果
[root@master1 docker]# docker run -itd --name aa -c 512 progrium/stress --cpu 2
9310cb28f60dd569abfe20674d3e4be7f47d55317814b0e0f41ce19718f8491a
[root@master1 docker]# docker run -itd --name bb -c 1024 progrium/stress --cpu 2
c8c33ef0616b5ea42705a36057cfe8575e32a17a7e01a5e0daae7c3e46796b84
[root@master1 docker]#
-c 指定优先级(cpu.shares)
--cpu 把CPU核数压满,即要消耗多少CPU
启动之后,使用docker stats可以看到容器占用的CPU
或者使用top命令
此时使用docker top xxxx命令可以看到
此时我们杀掉其中一个进程kill -9 4798,另外一个进程的CPU就会立刻压满。
上面就是对CPU的限制,下面试验对内存的限制,在Linux系统中。内存分为物理内存和swap分区,限制上也有这个。
先检查swap分区是否开启,使用命令swapon,或free -h看看
若未开启则使用如下命令创建swap分区
[root@master1 ~]# dd if=/dev/zero of=/swap bs=1M count=1024
[root@master1 ~]# mkswap -f /swap
[root@master1 ~]# swapon /swap
然后可以试验了
[root@master1 ~]# docker run --memory 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
--memory-swap表示swap+物理内存总共限额多少,这里是限制内存+swap300M
--vm:使用多少个进程
--vm-bytes:进程占用的内存大小
这里我们限制300M,占用280M,则可以运行。若改成300M,则不会运行
[root@master1 ~]# docker run --memory 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 300M
stress: FAIL: [1] (416) <-- worker 8 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 1s
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [8] forked
[root@master1 ~]#
查看dmesg打印信息,有OOM错误
若只使用-m参数运行的话,表示容器最多使用物理内存多少,swap多少,如下:最多使用物理内存200M,最多使用swap分区200M
[root@master1 ~]# docker run -itd -m 200M centos:7 bash
参考资料:《docker 容器与容器云(第2版)》