Docker容器因为还是共用的宿主机的内核,看似隔离了,其实还是隔离不彻底,只不过是被宿主机隐藏的进程,但是容器却没有虚拟机似的那么大消耗,虽然“敏捷”、“高性能”是容器较于虚拟机最大的优势,但是隔离不彻底也是它的最大缺点,共享内核那么暴露也会越多,不安全性就会越多,还有很多的资源和对象是不能够被Linux的Namespace化的,如时间,如果容器调用系统函数修改了时间,那么宿主机也会进行时间的修改,这是不科学也是不安全,所以就要为容器做一个限制。
为什么需要对容器进行限制呢?
进程虽然隔离了,但是资源还是使用的是宿主机进行共享,如CPU、内存等等,那就会出现危险的事情,如容器的进程将宿主机的资源全部用光,然后宿主机全部的内容处理不了,所以这样危险的事情,一个未来成树的容器是绝对不允许的。
那么linux 的Cgroups就是linux内核用来为进程设置资源限制的一个功能,Namespace解决隔离性问题,cgroup解决了资源限制问题。
Linux Cgroups的全称是linux controller group:它最主要的作用就是限制一个进程组能够使用的资源上线,包括CPU、内存、磁盘、网络带宽等等。除此之外,Cgroups还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作,不过本章只想讲“限制",看看他是如何限制进程使用的。
我们着重讲Cgroups的限制能力,我们可以实践操作一下来认识下Cgroups
在linux中Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作接口是文件系统,以文件和目录的方式组织在操作系统的sys/fs/cgroup路径下,作者是Ubuntu 16.04 机器里,用mount指令把它们展示出来,这条命令是:
$ mount -t cgroup
cpuset on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cpu on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)
cpuacct on /sys/fs/cgroup/cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct)
blkio on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
memory on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
...
输出的结果是一系列文件系统目录,可以看到有一系列cpuset,cpu,cpucct,blkio,memory等目录也叫子系统,这些都是这台机器当前可以被 Cgroups 进行限制的资源种类,而在子系统对应的资源下可以对该类资源具体被限制的方法,指令如下:
$ ls /sys/fs/cgroup/cpu
cgroup.clone_children cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
cgroup.procs cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
对CPU的限制,cfs_period:可以用来限制进程在长度为 cfs_period 的一段时间。
cfs_quota:被分配的总量为cfs_quota的CPU时间,这两个参数需要组合使用。
配置文件又如何使用呢?
对应的子系统下面创建一个目录,比如,我们现在进入 /sys/fs/cgroup/cpu 目录下:
root@ubuntu:/sys/fs/cgroup/cpu$ mkdir container
root@ubuntu:/sys/fs/cgroup/cpu$ ls container/
cgroup.clone_children cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
cgroup.procs cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
这个目录就称为”控制组“,操作系统会在你新创建的container下,自动生成该子系统对应的限制资源文件。
我们在后台执行这样的一个脚本:
$ while : ; do : ; done &
[1] 226
这个进程PID是226,它执行了一个死循环,所以肯定是不会释放CPU,一直进行CPU的消耗,使用TOP指令查看CPU的状态:
$ top
%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
CPU的使用率已经100了,这时我们可以去看我们在资源限制里的文件是没有任何限制,查看 container 目录下的文件,看到 container 控制组里的 CPU quota 还没有任何限制(即:-1),CPU period 则是默认的 100 ms(100000 us)
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
-1
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
100000
我们可以通过修改配置来进行资源的限制
像cfs_quota_us设置20ms(20000us),这个设置是在100MS的时间里,操作系统限制此进程只能使用20ms的CPU时间,也就是说这个进程只能使用20%的CPU带宽。
$ echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
接下来我们把被限制的进程的PID写入到container的tasks文件,上面的设置就会对该进程生效了
$ echo 226 > /sys/fs/cgroup/cpu/container/tasks
还是运行刚刚程序,用top指令再查询一下:
$ top
%Cpu0 : 20.3 us, 0.0 sy, 0.0 ni, 79.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
计算机的CPU使用被控制在了20%。
除 CPU 子系统外,Cgroups 的每一个子系统都有其独有的资源限制能力
那么Docker也是一样的操作,它只需要在子系统目录下创建一个控制组,启动容器之后把进程PID写入到task里就可以了,然后指定CPU的限制则可以在docker启动时指定参数,如:
$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash
总结:Cgroups主要是对进程资源的限制,毕竟不是虚拟机如果不加限制使用宿主机的资源可能会导致宿主机的事故,那么如何实现对进程资源的限制呢,按规定在指定好的文件目录及配置文件里配置下你对资源的限制即可。
本文章学习深入剖析 Kubernetes-张磊而整理的笔记资料。