namespace
容器技术出现较早,最早FreeBSD的操作系统虚拟化,其目的就是为了让进程不受其他干扰。就算这个经常出现了某些故障,bug,以及异常行为也不会影响到容器边界之外的其他进程,破坏的边界也是此jail而已。这种隔离可使应用安全运行。
这种技术被用到linux后,叫VServer,VServer也能实现一定的jail的效果,vserver实现的功能chroot,chroot将正在运行的进程限制在主机文件系统中的子目录中。在该过程中,它将目录视为其文件系统的根(/)。它只能访问chroot环境中的文件。如果是root用户,当然可以为所欲为。
但是FreeBSD的jails比chroot更安全,因为它们还对某些管理操作提供了一些限制,例如挂载和卸载文件系统,无法访问路由表或原始套接字,没有修改内核参数和sysctl等。
在一个单独的用户空间中,主要的目的是实现隔离环境,任何一个用户运行在当中就会以为自己是唯一一个运行在当前内核之上用户空间的进程,而且自己能够看到的其他进程也都是这个系统之上的所有进程。而一个用户空间也只能看到一些组件。
- 1,主机名域名(uts)
内核在起初设计的时候只支持单个用户空间引擎,为了jail,vserver,开始在内核级别只要可以切分为隔离的环境,就可以称为名称空间。在内核当中uts可以根据名称空间进行隔离。在同一个内核上创建出名称空间,让uts资源让每一个名称空间和另外一个名称空间进行隔离,每一个名称空间都有一个独立的名称。主机名是内核级别的,一个主机有多个用户空间,就都可以有自己的主机名,就需要在内核级别隔离开。
- 2,根文件系统(mount)
挂载文件系统也可以切分为多个,每一个名称空间有一个,各自互不干扰。内核级实现。
- 3,IPC(进程间通讯专用通道)
同一个空间内,底层内核本身管理时候,是期望各进程之间,任何进程之间可直接使用ipc通讯的,通过内存实现。
但是现在,必须分开。名称空间内的进程是在同一个内存空间中运行的,必须要确保IPC也是独立的。同一个空间是可以通讯的,跨空间是不能通讯。
- 4,Pid
每一个用户空间中,每一个进程都是由父进程创建,而没有父进程的进程也就是这个用户空间的init。一个系统运行分为进程树和文件系统树。如果当前空间内认为当前系统是唯一的,要给一个假象。要么是init,要么是从属某个init。每一个系统只有一个init,就需要给每个用户空间做一个假象的init。要么就运行一个进程。如果是有多个进程就需要init。由此,进程树使得每一个进程所看到的id号要么是1,要么自己从属某个iD号是1的进程。pid是需要互相隔离的。
1号id必然是init的。在一个内核之上,真正init也就是id是1的只能是一个。但是现在不得不去为每个用户空间进程去伪装一个,这样一来,就不得不将他们隔离开。
- 5,user
运行一个进程,应该是以某一个用户运行,第一个用户和第二个用户有可能是id号一样的,可能名称不一样。因为每一个id号都是有一些规范的,0为root , 1-999 系统用户,1000+登陆用户。每一个用户空间都需要一个root,然而在一个内核里面只能有一个root。那就意味着必须给每个空间伪装一个root。在系统上这个root是一个用户,而在名称空间内伪装为id为0,只是只能在这个空间内有root权限,看起来就是root。而在宿主机上这个用户仍然是一个普通用户。
- 6,Net
每一个空间都以为自己是唯一的一个,就像是一个虚拟机一样,那么就可以有自己的ip地址,有自己的接口,有自己的端口平面,运行不同的服务。
这6种容器机制的实现,在内核级别已经通过名称空间的机制,原生支持。这种功能通过系统调用向外输出。
如: chone(). setns().
要想使用容器,需要内核级别的内核资源的名称空间隔离机制来实现。 要想完美使用,内核必须在3.8以上,那也就意味着必须是centos7
资源限制
我们试想一种场景,我们现在不在使用主机虚拟化技术,抽取掉主机虚拟化虚拟的内核,所有的空间不在分属于一个独立的内核,而从属于同一个内核,这种技术称为容器级虚拟化技术。
cpu
在主机虚拟化技术创建虚拟机的时候,是可以进行指定CPU使用多少个核心,多大内存,在创建的时候就可以进行设定好这些资源限制。在一些场景中,CPU和内存如果直接被使用是会出问题的。CPU属于可压缩资源,而内存就容易出现OOM。所以内核必须实现一种功能来限制每一个用户空间进程所有可用的资源总量。这种分配的方式可以是比例分配,也可以单一用户空间做核心绑定。
- 比例分配:如果是4核心,而三个名称空间分别的是1/4,2/4,1/4,1/4,分别是1,2,1,1核心如果其中不用的资源就会别其他说使用,如果其他也用,就不会超出分配的配额。
- 核心绑定: 绑定多少核心,同时使用不能超过绑定的数量。
内存
而内存也是一样的逻辑,可以限制最多使用多少 ,比如使用4G,也就说空间内所有的进程使用的内存总数不能超过4G,如果超过4G,就会OOM掉最耗费内存的进程。因为内存是不可压缩资源。
这些功能必须要在内核依靠cgroups(Control Groups)实现,对于Cgroup来讲,是将系统资源分成多组,将每一个组内的资源量进行指派,分配到特定的用户空间上去。
加入将一个用户空间当作一个组,而后向这个组指派不同的资源就可以限制资源。而后将资源给名称空间后,名称空间内部的进程就自动拥有了使用得到分配所有资源的能力。
容器的隔离能力和主机虚拟化相比差了很多,在于同一个内核设置了边界而已。而主机虚拟化本身就不属于同一个内核。为了加强这种安全性,可以通过selinux等机制加强边界。