$ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
......
容器的本质是进程
比如我们用c语言写了一个不断接受标准输入然后打印到标准输出的程序。 当这个程序不执行的时候,躺在硬盘就是一个文件,当我们把它运行起来的时候,他就是操作系统里面中的一个进程。
所以对于进程来说,它的静态的表现形式就是硬盘中的一个文件,当它运行起来的时候,就是计算机里面状态和数据的集合,也就说进程的动态表现。
容器也是如此,容器本身也是一个tar包,当我们不运行它的时候,它也是一个文件,当我们把它运行起来的时候,他才是宿主机里面的一个进程。只不错Linux通过一些技术,让这个“进程“有了边界。
也就是我们经常会听到的:Namespace技术改变进程视图,CGroups对进程的资源进行限制
Namespace
一个Docker容器就是宿主机中的一个进程
Docker使用linux内核的namespace技术来实现容器的隔离。
Linux中的Namespace有七个(namespace list)对应的功能如下表:
Docker默认开启的Namespace有:
还有一个User Namespace默认不是开启的。
以PID Namespace举例。
当我们理解了,Docker是怎么做到隔离,然后我们来看看官网上展示的图是不是有些问题呢。
CGroups
我们知道了Docker是怎么做隔离的,既然一个Docker容器在宿主机就是一个进程(可以通过
docker inspect --format '{{ .State.Pid }}' containerId
查看容器在宿主机的进程),那么我们就不能让这个进程吃光了我们宿主机的资源,所以就要对使用资源进行限制。Linux Cgroups 的全称是 Linux Control Group。它最主要的作用就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
Cgroups暴露给用户的操作是文件系统,是通过文件或者目录的方式存在于宿主机操作系统的
/sys/fs/cgroup
目录下,我们可以通过ll /sys/fs/cgroup
将他们展示出来,也可以通过mount命令:我们可以看到很多能被限制的资源,比如说cup和memery,他们都是以目录的方式存在的,目录下面的文件是对该资源限制的描述,我们可以看一下cpu下面的文件:
这里比较重要的是
cpu.cfs_period_us
和cpu.cfs_quota_us
,表示在cfs_period
这段时间内,该进程最多占用cfs_quota/cfs_period
的CPU(-1表示没有限制)。比如我们设置cfs_period=100
和cfs_quota=50
,那么这个进程最多占用宿主机50%的CPU资源。当我们在
/sys/fs/cgroup/cpu
这个目录下面新建一个目录container,container这个目录就被称为一个控制组,操作系统会给这个子系统生成默认的资源控制文件。我们可以先看一下现在操作系统对container的cpu限制:
结论是:没有限制
⚠️:
cfs_period
和cfs_quota
的单位是us,所以上述cfs_period表示的是100000us/100ms。然后我们执行一个死循环:
可以看到机器上的一个cpu已经被打满了。
但是我们的预期是这个死循环进程最多占用一个CPU的百分之二十,那我们应该怎么操作呢?
cfs_quota/cfs_period
,所以我们需要修改cfs_quota
echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
containr/tasks
里面:echo 7512 > /sys/fs/cgroup/cpu/container/tasks
我们就能看到现在这个进程的CPU资源利用率的上线就是百分之二十了。
除了CPU子系统之外,Cgroup对每一个子系统都有其独有的资源限制能力:
我们可以举个在限制docker容器的cpu只能占用20%的例子:
查看容器中的:
容器的隔离并完美
众所周知的是容器之间还是共享的宿主机的内核。
/proc
中存储的是当前内核运行状态的一系列的特殊文件,用户可以通过访问这些文件,查看系统以及当前正在运行的进程信息。比如CPU的使用情况,内存的占用率等,这些文件是查看系统的信息指令(比如top)的主要信息来源。造成这个问题的原因就是:/proc文件系统并不知道用户通过Cgroup对这个容器做了什么样子的限制
怎么修复呢? lxfs