Docker核心技术Namespace浅析

简介

Linux Namespace提供了一种内核级别隔离系统资源的方法,经过将系统的全局资源放在不一样的Namespace中,来实现资源隔离的目的。不一样Namespace的程序,能够享有一份独立的系统资源。目前Linux中提供了六类系统资源的隔离机制,分别是:node

  • Mount: 隔离文件系统挂载点
  • UTS: 隔离主机名和域名信息
  • IPC: 隔离进程间通讯
  • PID: 隔离进程的ID
  • Network: 隔离网络资源
  • User: 隔离用户和用户组的ID

下面简单的介绍一下这些Namespace的使用和功能。markdown

使用

涉及到Namespace的操做接口包括clone()setns()unshare()以及还有/proc下的部分文件。为了使用特定的Namespace,在使用这些接口的时候须要指定如下一个或多个参数:网络

  • CLONE_NEWNS: 用于指定Mount Namespace
  • CLONE_NEWUTS: 用于指定UTS Namespace
  • CLONE_NEWIPC: 用于指定IPC Namespace
  • CLONE_NEWPID: 用于指定PID Namespace
  • CLONE_NEWNET: 用于指定Network Namespace
  • CLONE_NEWUSER: 用于指定User Namespace

下面简单概述一下这几个接口的用法。dom

clone系统调用

能够经过clone系统调用来建立一个独立Namespace的进程,它的函数描述以下:ide

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

它经过flags参数来控制建立进程时的特性,好比新建立的进程是否与父进程共享虚拟内存等。好比能够传入CLONE_NEWNS标志使得新建立的进程拥有独立的Mount Namespace,也能够传入多个flags使得新建立的进程拥有多种特性,好比:函数

flags = CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC;

传入这个flags那么新建立的进程将同时拥有独立的Mount NamespaceUTS NamespaceIPC Namespacespa

经过/proc文件查看已存在的Namespace

在3.8内核开始,用户能够在/proc/$pid/ns文件下看到本进程所属的Namespace的文件信息。例如PID为2704进程的状况以下图所示:Docker核心技术Namespace浅析其中4026531839是Namespace的ID,若是两个进程的Namespace ID相同代表进程同处于一个命名空间中。这里须要注意的是:只/proc/$pid/ns/对应的Namespace文件被打开,而且该文件描述符存在,即便该PID所属的进程被销毁,这个Namespace会依然存在。能够经过挂载的方式打开文件描述符:code

touch ~/mnt
mount --bind /proc/2704/mnt ~/mnt

这样就能够保留住PID为2704的进程的Mount Namespace了,即便2704进程被销毁或者退出,ID为4026531840的Mount Namespace依然会存在。对象

setns加入已存在的Namepspace

setns()函数能够把进程加入到指定的Namespace中,它的函数描述以下:blog

int setns(int fd, int nstype);

它的参数描述以下:

  • fd参数:表示文件描述符,前面提到能够经过打开/proc/$pid/ns/的方式将指定的Namespace保留下来,也就是说能够经过文件描述符的方式来索引到某个Namespace
  • nstype参数:用来检查fd关联Namespace是否与nstype代表的Namespace一致,若是填0的话表示不进行该项检查。

经过在程序中调用setns来将进程加入到指定的Namespace中。

unshare脱离到新的Namespace

unshare()系统调用用于将当前进程和所在的Namespace分离,并加入到一个新的Namespace中,相对于setns()系统调用来讲,unshare()不用关联以前存在的Namespace,只须要指定须要分离的Namespace就行,该调用会自动建立一个新的Namespaceunshare()的函数描述以下:

int unshare(int flags);

其中flags用于指明要分离的资源类别,它支持的flagsclone系统调用支持的flags相似,这里简要的叙述一下几种标志:

  • CLONE_FILES: 子进程通常会共享父进程的文件描述符,若是子进程不想共享父进程的文件描述符了,能够经过这个flag来取消共享。
  • CLONE_FS: 使当前进程再也不与其余进程共享文件系统信息。
  • CLONE_SYSVSEM: 取消与其余进程共享SYS V信号量。
  • CLONE_NEWIPC: 建立新的IPC Namespace,并将该进程加入进来。

注意事项

这里须要注意的是unshare()setns()系统调用对PID Namespace的处理不太相同,当unshare PID namespace时,调用进程会为它的子进程分配一个新的PID Namespace,可是调用进程自己不会被移到新的Namespace中。并且调用进程第一个建立的子进程在新Namespace中的PID为1,并成为新Namespace中的init进程。setns()系统调用也是相似的,调用者进程并不会进入新的PID Namespace,而是随后建立的子进程会进入。为何建立其余的Namespace时unshare()setns()会直接进入新的Namespace,而惟独PID Namespace不是如此呢?由于调用getpid()函数获得的PID是根据调用者所在的PID Namespace而决定返回哪一个PID,进入新的PID namespace会致使PID产生变化。而对用户态的程序和库函数来讲,他们都认为进程的PID是一个常量,PID的变化会引发这些进程奔溃。换句话说,一旦程序进程建立之后,那么它的PID namespace的关系就肯定下来了,进程不会变动他们对应的PID namespace。

小结

经过上面简单的概述,对于Namespace的操做有如下方式:

  • 一、能够在进程刚建立的时候经过clone系统调用为新进程分配一个或多个新的Namespace
  • 二、经过setns()将进程加入到已有的Namespace中。
  • 三、经过unshare()为已存在的进程建立一个或多个新的Namespace

接下来详细的介绍一下各个Namespace的功能和特性。

Mount Namespace

Mount Namespace用来隔离文件系统的挂载点,不一样Mount Namespace的进程拥有不一样的挂载点,同时也拥有了不一样的文件系统视图。Mount Namespace是历史上第一个支持的Namespace,它经过CLONE_NEWNS来标识的。

挂载的概念

挂载的过程是经过mount系统调用完成的,它有两个参数:一个是已存在的普通文件名,一个是能够直接访问的特殊文件,一个是特殊文件的名字。这个特殊文件通常用来关联一些存储卷,这个存储卷能够包含本身的目录层级和文件系统结构。mount所达到的效果是:像访问一个普通的文件同样访问位于其余设备上文件系统的根目录,也就是将该设备上目录的根节点挂到了另一个文件系统的页节点上,达到给这个文件系统扩充容量的目的。能够经过/proc文件系统查看一个进程的挂载信息,具体作法以下:

cat /proc/$pid/mountinfo

其输出结果以下:Docker核心技术Namespace浅析其中输出的格式以下:Docker核心技术Namespace浅析

挂载传播

进程在建立Mount Namespace时,会把当前的文件结构复制给新的Namespace,新的Namespace中的全部mount操做仅影响自身的文件系统。但随着引入挂载传播的特性,Mount Namespace变得并非彻底意义上的资源隔离,这种传播特性使得多Mount Namespace之间的挂载事件能够相互影响。挂载传播定义了挂载对象之间的关系,系统利用这些关系来决定挂载对象中的挂载事件对其余挂载对象的影响。其中挂载对象之间的关系描述以下:

  • 共享关系(MS_SHARED):一个挂载对象的挂载事件会跨Namespace共享到其余挂载对象。
  • 从属关系(MS_SLAVE): 传播的方向是单向的,即只能从Master传播到Slave方向。
  • 私有关系(MS_PRIVATE): 不一样Namespace的挂载事件是互不影响的(默认选项)。
  • 不可绑定关系(MS_UNBINDABLE): 一个不可绑定的私有挂载,与私有挂载相似,可是不能执行挂载操做。

其中给挂载点设置挂载关系的例子以下:

mount --make-shared /mntS      # 将挂载点设置为共享关系属性
 mount --make-private /mntP     # 将挂载点设置为私有关系属性
 mount --make-slave /mntY       # 将挂载点设置为从属关系属性
 mount --make-unbindable /mntU  # 将挂载点设置为不可绑定属性

注意在设置私有关系属性时,在本命名空间下的这个挂载点是Slave,而父命名空间下这个挂载点是Master,挂载传播的方向只能由Master传给Slave。

绑定挂载

绑定挂载的引入使得mount的其中一个参数不必定要是一个特殊文件,也能够是该文件系统上的一个普通文件目录。Linux中绑定挂载的用法以下:

mount --bind /home/work /home/alpha
mount -o bind /home/work /home/alpha

其中/home/work是磁盘上的存在的一个目录,而不是一个文件设备(好比磁盘分区)。若是须要将Linux中两个文件目录连接起来,能够经过绑定挂载的方式,挂载后的效果相似于在两个文件目录上创建了硬连接。在绑定挂载中同时会涉及到挂载的传播特性,挂载传播的特性参考:Linux绑定挂载

UTS Namespace

UTS Namespace提供了主机名和域名的隔离,也就是struct utsname里的nodenamedomainname两个字段。不一样Namespace中能够拥有独立的主机名和域名。那么为何须要对主机名和域名进行隔离呢?由于主机名和域名能够用来代替IP地址,若是没有这一层隔离,同一主机上不一样的容器的网络访问就可能出问题。

IPC Namespace

IPC Namespace是对进程间通讯的隔离,进程间通讯常见的方法有信号量、消息队列和共享内存。IPC Namespace主要针对的是SystemV IPC和Posix消息队列,这些IPC机制都会用到标识符,好比用标识符来区分不一样的消息队列,IPC Namespace要达到的目标是相同的标识符在不一样的Namepspace中表明不一样的通讯介质(好比信号量、消息队列和共享内存)。原文

更多深度文章,关注:二进制社区

相关文章
相关标签/搜索