关于k8s的隔离namespace与cgroup
容器技术中一个非常重要的概念,容器是一个单进程模型。 用户的应用进程就是容器里面PID=1的进程,其他后续创建的进程都是这个进程的子进程,意味着你没法运行两个不同的应用,除非找到一个公共的PID=1的程序来充当两个不同程序的父进程。
关于两者关系
namesapce主要是隔离作用,cgroups主要是资源限制,联合文件主要用于镜像分层存储和管理,runC是运行时,遵循了oci接口,一般来说基于libcontainer。网络主要是docker单机网络和多主机通信模式。
namespace 是用来做资源隔离, cgroup 是用来做资源限制
关于namespace
什么是namespace
Namespace是将内核的全局资源做封装,使得每个Namespace都有一份独立的资源,因此不同的进程在各自的Namespace内对同一种资源的使用不会互相干扰。实际上,Linux内核实现namespace的主要目的就是为了实现轻量级虚拟化(容器)服务。在同一个namespace下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,仿佛自己置身于一个独立的系统环境中,以此达到独立和隔离的目的。
这样的解释可能不清楚,举个例子,执行sethostname这个系统调用时,可以改变系统的主机名,这个主机名就是一个内核的全局资源。内核通过实现UTS Namespace,可以将不同的进程分隔在不同的UTS Namespace中,在某个Namespace修改主机名时,另一个Namespace的主机名还是保持不变。
目前Linux内核总共实现了6种Namespace:
Mount:隔离文件系统挂载点。每个容器能看到不同的文件系统层次结构。
UTS:隔离主机名和域名。
PID:隔离进程ID。
Network:隔离网络资源。
User:隔离用户ID和组ID。
IPC:隔离System V IPC和POSIX消息队列。
各个namespace介绍
第一个是 mout namespace。mout namespace 就是保证容器看到的文件系统的视图,是容器镜像提供的一个文件系统,也就是说它看不见宿主机上的其他文件,除了通过 -v 参数 bound 的那种模式,是可以把宿主机上面的一些目录和文件,让它在容器里面可见的;
第二个是 uts namespace,这个 namespace 主要是隔离了 hostname 和 domain;
第三个是 pid namespace,这个 namespace 是保证了容器的 init 进程是以 1 号进程来启动的;
第四个是网络 namespace,除了容器用 host 网络这种模式之外,其他所有的网络模式都有一个自己的 network namespace 的文件;
第五个是 user namespace,这个 namespace 是控制用户 UID 和 GID 在容器内部和宿主机上的一个映射,不过这个 namespace 用的比较少;
第六个是 IPC namespace,这个 namespace 是控制了进程兼通信的一些东西,比方说信号量;
第七个是 cgroup namespace,上图右边有两张示意图,分别是表示开启和关闭 cgroup namespace。用 cgroup namespace 带来的一个好处是容器中看到的 cgroup 视图是以根的形式来呈现的,这样的话就和宿主机上面进程看到的 cgroup namespace 的一个视图方式是相同的;另外一个好处是让容器内部使用 cgroup 会变得更安全。
为什么要用到cgroup
虽然 Namespace 提供的隔离机制有很多,但实际上我们操作的一些资源仍然是全局的,并且基本上是没有什么限制的,如:内存,CPU,硬盘等。一些在已经「隔离」了的进程中做的操作还是会影响到其他进程的。
所以,Linux 在内核中以文件系统的形式为我们实现了一种资源隔离的机制:Linux CGroup,位于 /sys/fs/cgroup 目录 。它用来限制,控制一个进程群组的资源。工作方式类似于:先对计算机的某个资源设置了一些限制规则,如只能使用 CPU 的20%。然后,如果我们想一些进程去遵守这个使用 CPU 资源的限制的话,就将它加入到这个规则所绑定的进程组中,之后,相应的限制就会对其生效。
关于cgroup
cgroup 主要是做资源限制的
Cgroups的设计比较简单粗暴,就是一个子系统目录加上一组资源限制文件的组合。Docker等容器项目来说就是在各个子系统下,为每个容器创建一个控制组(就是新建个目录),然后启动容器进程后,把容器进程的PID写入对应控制组的tasks文件中就可以了。
控制组 (Control Group)
一个控制组包含多个进程,而资源的限制也是定义在控制组上的。若一个进程加入到某一个控制组,则自动会受到定义在这个控制组上面的限制规则的影响。
层级
一个子系统下面的控制组,可以进行嵌套,最终形成一个树形的结构。子节点控制组会继承父节点控制组上对于资源的限制规则。若在子节点的控制组重定义了和父节点中相同资源的规则,则会发生覆盖(子覆盖父)
在/sys/fs/cgroup/cpu目录下创建container文件夹,系统自动创建了一堆资源限制文件。这个目录就成为一个“控制组”。sys/fs/cgroup 目录下的项目就是目前操作系统提供的全部子系统。
[[email protected] docker]# docker run -it --cpu-period=100000 --cpu-quota=20000 centos:7 /bin/bash
[[email protected] /]#
[[email protected] ~]# cd /sys/fs/cgroup/cpu/docker/90c9fd20daf8f4f9ad384d349d37ca1542490d78daf4e3fe6b79f3ad6d7179bd/
[[email protected] 90c9fd20daf8f4f9ad384d349d37ca1542490d78daf4e3fe6b79f3ad6d7179bd]# cat cpu.cfs_quota_us
20000
[[email protected] 90c9fd20daf8f4f9ad384d349d37ca1542490d78daf4e3fe6b79f3ad6d7179bd]# cat cpu.cfs_period_us
100000
[[email protected] 90c9fd20daf8f4f9ad384d349d37ca1542490d78daf4e3fe6b79f3ad6d7179bd]#
常用到的cgroup
第一个是 CPU,CPU 一般会去设置 cpu share 和 cupset,控制 CPU 的使用率;
第二个是 memory,是控制进程内存的使用量;
第三个 device ,device 控制了你可以在容器中看到的 device 设备;
第四个 freezer。它和第三个 cgroup(device)都是为了安全的。当你停止容器的时候,freezer 会把当前的进程全部都写入 cgroup,然后把所有的进程都冻结掉,这样做的目的是:防止你在停止的时候,有进程会去做 fork。这样的话就相当于防止进程逃逸到宿主机上面去,是为安全考虑;
第五个是 blkio,blkio 主要是限制容器用到的磁盘的一些 IOPS 还有 bps 的速率限制。因为 cgroup 不唯一的话,blkio 只能限制同步 io,docker io 是没办法限制的;
第六个是 pid cgroup,pid cgroup 限制的是容器里面可以用到的最大进程数量。
cgroup的缺点
Cgroups也有缺陷,提起最多的就是/proc文件系统的问题。/proc存储的是内核运行状态的一系列特殊文件,用户可以访问这些文件查看当前运行的进程的状态,比如CPU使用情况,内存使用情况,这些是top的主要数据来源。但是/proc不了解Cgroups的存在,就是容器里面读取的CPU核数,内存状态其实是宿主机的
关于k8s
kubernetes对于容器级别的隔离其实是交由底层的runtime来负责的,例如docker, 当我们指定运行容器所需要资源的request和limit时,docker会为容器设置进程所运行cgroup的cpu.share, cpu.quota, cpu.period, mem.limit等指标来