NingG +

Docker 系列:核心原理和实现(2)

概要

特别说明:这篇是在公司内部组织的 Docker 技术专题中,讨论分享的文章。作者是我带的一个 94 年的小青年(LanDi),非常有潜力。

容器技术,主要关注下面几点:

Docker 作为容器的一种典型实现,也围绕上述几个方面在进行。具体实现上,使用了 Unix 内核提供的一些技术,四个方面:

下文将进行详细介绍。

命名空间(Namespace)

Linux Namespace 是内核级别资源隔离方法,不同 namespace 之间,各自拥有的资源,相互没有影响,调整任何一个 namespace,都不会影响其他 namespace.

目前 Linux 内核实现了七种不同的 namespaces:

命名空间 系统调用参数(标记位) Linux 内核版本
Mount namespaces CLONE_NEWNS Linux 2.4.19
UTS namespaces CLONE_NEWUTS Linux 2.6.19
IPC namespaces CLONE_NEWIPC Linux 2.6.19
PID namespaces CLONE_NEWPID Linux 2.6.24
Network namespaces CLONE_NEWNET Linux 2.6.29
User namespaces CLONE_NEWUSER Linux 3.8
Cgroup CLONE_NEWCGROUP Linux 4.6

补充说明:

在 Linux 系统,执行下述命令,查看当前进程所属的命名空间:

# 查看当前进程所属的命名空间:
# $$ 在 shell 场景下,会自动识别为当前进程的 pid
ls -l /proc/$$/ns

加入 or 退出一个命名空间,可以直接使用 Linux 提供的系统调用。

Linux 提供了三个 API 用来创建进程,并使其加入或脱离某个 Namespace:

#clone() :创建进程并把它放入一个 namespace,当前进程保持不变
#-flags:传入 namespace 对应参数
int clone(int (*child_func)(void *), void *child_stack
            , int flags, void *arg);

#setns(): 将当前进程加入某 namespace 中
#-fd:指向/proc/[pid]/ns/目录里相应namespace对应的文件,表示要加入哪个namespace
#-nstype:指定namespace的类型
int setns(int fd, int nstype);

#unshare():使当前进程退出当前的 namespace,加入新指定的 namespace 中
#-flags:指定一个或者多个上面的CLONE_NEW*
int unshare(int flags);

Mount namespaces

作用:用来隔离文件系统的挂载点,不同的 Mount namespace 拥有独立的挂载点信息,不相互影响,有利于构建容器或者用户自己的文件目录。

当前进程所在mount namespace里的所有挂载信息,可以在 /proc/[pid]/mounts、/proc/[pid]/mountinfo/proc/[pid]/mountstats里面找到。

ls -l /proc/$$/ns

补充说明:

基于 mount namespace 的隔离性,为了实现一定程度的共享,在 Linux 2.6.15 中,引入 shared subtree 技术:

更多细节,参考 mount namespace和shared subtrees

UTS namespaces

作用:用来隔离系统的「主机名 hostname」以及「NIS 域名」。

补充: NIS 域名,集中控制「用户登录账号」相关信息的服务,细节参考 Linux Namespace : UTS

具体:

UTS namespace 不存在嵌套关系,即不存在一个namespace是另一个namespace的父namespace。

内核中的实现:

  1. 在老版本的 Linux 中,UTS 的相关信息保存在一个全局变量中,所有进程都共享这个全局变量。
  2. 在新版本中,在每个进程对应的结构体 task_struct 中,增加了一个 nsproxy 字段,保存相关信息。不同 UTS namespace 中的进程,指针指向的结构体不同,从而达到了隔离 UTS 信息的目的。

IPC namespaces

参考资料

Top