通过Namespace实现隔离

mark

在上次的文章 《虚拟化的基石——Namespace》 中主要讲述了Namespace的作用,却没有详细的拿代码举证,上次仅仅证明了UTS Namespace,剩下五个命名空间全在这篇文章了,主要是IPC Namespace、PID Namespace、Mount Namespace、User Namespace、Network Namespace的概念的理解以及使用代码证明!

IPC Namespace

IPC Namespace用来隔离System V IPC和POSIX message queues。每一个 IPC Namespace,都有自己的System V IPC和POSIX message queue。所谓System V IPC即系统IPC资源,其实主要是共享内存、信号量( System V信号量 )、消息队列;POSIX message queues其实就是随文件系统的消息队列

  • 随进程持续的(Process-Persistent IPC) IPC对象一直存在,直到最后拥有他的进程被关闭为止,典型的IPC有pipes(管道)和FIFOs(先进先出对象)

  • 随内核持续的(Kernel-persistent IPC) IPC对象一直存在直到内核被重启或者对象被显式关闭为止,在Unix中这种对象有System v 消息队列,信号量,共享内存。(注意Posix消息队列,信号量和共享内存被要求为至少是内核持续的,但是也有可能是文件持续的,这样看系统的具体实现)。

  • 随文件系统持续的(FileSystem-persistent IPC) 除非IPC对象被显式删除,否则IPC对象会一直保持(即使内核才重启了也是会留着的)。如果Posix消息队列,信号量,和共享内存都是用内存映射文件的方法,那么这些IPC都有着这样的属性。

还是昨天的UTS Namespace代码稍做修改

mark

现在go run IPNamespace.go

mark

通过以上实验,可以发现在新创建的Namespace里,看不到宿主机上已经创建的message queue,说明IPC Namespace创建成功,IPC 已经被隔离。

PID Namespace

PID Namespace是用来隔离进程ID的。同样一个进程在不同的PID Namespace里可以拥有不同的PID。这样就可以理解,在docker container里面,使用ps -ef经常会发现,在容器内,前台运行的那个进程PID是1,但是在容器外,使用ps -ef会发现同样的进程却有不同的PID,这就是PID Namespace做的事情。

mark

mark

Mount Namespace

Mount Namespace用来隔离各个进程看到的挂载点视图。在不同Namespace的进程中到的文件系统层次是不一样的。在Mount Namespace中调用mount()和umount()仅仅只会影当前Namespace内的文件系统,而对全局的文件系统是没有影响的。

看到这里,也许就会想到chroot()。它也是将某一个子目录变成根节点。但是,MountNamespace不仅能实现这个功能,而且能以更加灵活和安全的方式实现。MountNamespace是Linux第一个实现的Namespace类型,因此,它的系统调用参是NEWNS (New Namespace的缩写)。当时人们貌似没有意识到,以后还会有很多类型Namespace加入Linux大家庭。代码做了一点改动,增加了NEWNS标识,如下。

mark

运行代码,然后查看一下/proc 的文件内容。proc 是一个文件系统,提供额外的机制,可以通过内核和内核模块将信息发送给进程。这里的/proc 还是宿主机的, 所以看到里面会比较乱,下面将/proc mount 到我们自己的Namespace 下面来。

mark

Mount Namespace 中的mount 和外部空间是隔离的, mount 操作并没有影响到外部。Docker volume 也是利用了这个特性。

User Namespace

User Namespace主要是隔离用户的用户组ID。也就是说,一个进程的UserID和GroupID在User Namespace内外可以是不同的。比较常用的是,在宿主机上以一个非root用户运行创建一个User Namespace,然后在User Namespace里面却映射成root用户。

这意味着,这个进程在User Namespace里面有root权限,但是在User Namespace外面却没有root的权限。从LinuxKernel3.8开始,非root进程也可以创建UserNamespace,并且此用户在Namespace里面可以被映射成root,且在Namespace内有root权限。

mark

mark

Network Namespace

Network Namespace是用来隔离网络设备、IP 地址端口等网络栈的Namespace。Network Namespace可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个Namespace内的端口都不会互相冲突。在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口。

mark

mark

在Namespace 里面什么网络设备都没有。这样就能断定Network Namespace 与宿主机之间的网络是处于隔离状态了,是不是离Docker的实现更进一步了呢?