什么是Cgroup
容器的资源限制是通过Cgroup实现的,Cgroups 的全称是 Control Group,属于linux内核提供的一个特性,它最主要的作用就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
此外,Cgroups 还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,默认linux系统在启动时已经挂载到了 /sys/fs/cgroup 路径下。
1 | [root@slions_pc1 ~]# mount -t cgroup |
查看Cgroup
可以看到,在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。
1 | [root@slions_pc1 ~]# ls /sys/fs/cgroup/ |
而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。
Cgroup子系统
对实际资源的分配和管理是由各个Cgroup子系统来完成的。
cpuset子系统
cpuset可以为一组进程分配指定的CPU和内存节点。
主要的接口有:
- cpuset.cpus: 允许进程使用的CPU列表
- cpuset.mems: 允许进程使用的内存节点列表
cpu子系统
cpu子系统用于限制进程的CPU占用率。
主要的接口有:
- cpu.shares:CPU比重分配
- cpu.cfs_period_us和cpu.cfs_quota_us:CPU带宽限制(微秒)
- cpu.rt_period_us和cpu.rt.runtime_us:实时进程的CPU带宽限制
cpuacct子系统
cpuacct子系统用来统计各Cgroup的CPU使用情况。
- cpuacct.stat: 报告这个Cgroup分别在用户态和内核态消耗的CPU时间(USER_HZ)
- cpuacct.usage: 报告这个Cgroup消耗的总CPU时间
- cpuacct.usage_percpu: 报告这个Cgroup在各CPU上消耗的CPU时间
memory子系统
memory子系统用来限制Cgroup所使用的内存上限。
- memory.limit_in_bytes:内存上限
- memory.memsw.limit_in_bytes:内存+swap上限
- memory.oom_control:设置为0则当内存使用量超过上限时系统不会杀死进程,而是阻塞进程直到有内存被释放
- memory.stat: 汇报内存使用情况
blkio子系统
blkio子系统用来限制Cgroup的block I/O带宽。
blkio.weight: 设置权重
blkio.weight_device:对具体的设备设置权重
blkio.throttle.write_bps_device:对具体的设备设置每秒写磁盘的带宽上限
blkio.throttle.read_bps_device: 对具体的设备设置每秒读磁盘的带宽上限
device子系统
用来控制Cgroup的进程对那些设备有访问权限。
devices.list:只读文件,显示目前允许被访问的设备列表
devices.deny:只写文件,允许相应的设备访问权限
devices.allow:只写文件,禁止相应的设备访问权限
如何限制资源
介绍了那么多Cgroup的概念,我们下面就拿限制cpu使用率为例,来讲讲如何限制这些资源。
首先本地下载用来设置Cgroup的工具包。
1 | [root@slions_pc1 ~]# yum install libcgroup-tools.x86_64 -y |
接下来通过cgcreate命令来创建一个自己的控制组slions。
1 | [root@slions_pc1 cpu]# cgcreate -g cpu:slions |
会发现/sys/fs/cgroup/cpu目录下已经多了一个slions的目录,并且自动创建了一些文件,其中task文件就是记录进程pid的地方,换句话说,只要是在此task中的进程都会被此控制组所限制。
接着写一个死循环后台跑着,获得进程号为44577,执行top看到cpu使用率为100%
1 | [root@slions_pc1 cpu]# while :;do :;done & |
我们把此进程号写入slions/tasks中
1 | [root@slions_pc1 cpu]# echo 44577 > slions/tasks |
没有变化,这是因为默认没有任何限制。
1 | [root@slions_pc1 cpu]# cat slions/cpu.cfs_quota_us |
接下来,我们可以通过修改这些文件的内容来设置限制。
向 slions组里的 cfs_quota_us 文件写入 30 ms(30000 us),它意味着在每 100 ms 的时间里,被该控制组限制的进程只能使用 30 ms 的 CPU 时间,也就是说这个进程只能使用到 30% 的 CPU 带宽。
1 | [root@slions_pc1 cpu]# echo 30000 > slions/cpu.cfs_quota_us |
我们可以再起一个死循环进程,并且放入slions控制组。
1 | [root@slions_pc1 cpu]# while :;do :;done & |
可以看到,这两个进程各占了15%,加起来正好是我们的cfs_quota_us 限制。
总结
Linux Cgroups 的设计还是比较易用的,简单粗暴地理解呢,它就是一个子系统目录加上一组资源限制文件的组合。而对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了。