彻底搞懂容器技术的基石: namespace (上)
大家好,我是张晋涛。
目前我们所提到的容器技术、虚拟化技术(不论何种抽象层次下的虚拟化技术)都能做到资源层面上的隔离和限制。
对于容器技术而言,它实现资源层面上的限制和隔离,依赖于 Linux 内核所提供的 cgroup 和 namespace 技术。
我们先对这两项技术的作用做个概括:
-
cgroup 的主要作用:管理资源的分配、限制; -
namespace 的主要作用:封装抽象,限制,隔离,使命名空间内的进程看起来拥有他们自己的全局资源;
在上一篇文章中,我们重点聊了 cgroup 。本篇,我们重点来聊 namespace 。
Namespace 是什么?
我们引用 wiki 上对 namespace 的定义:
“Namespaces are a feature of the Linux kernel that partitions kernel resources such that one set of processes sees one set of resources while another set of processes sees a different set of resources. The feature works by having the same namespace for a set of resources and processes, but those namespaces refer to distinct resources.
”
namespace 是 Linux 内核的一项特性,它可以对内核资源进行分区,使得一组进程可以看到一组资源;而另一组进程可以看到另一组不同的资源。该功能的原理是为一组资源和进程使用相同的 namespace,但是这些 namespace 实际上引用的是不同的资源。
这样的说法未免太绕了些,简单来说 namespace 是由 Linux 内核提供的,用于进程间资源隔离的一种技术。将全局的系统资源包装在一个抽象里,让进程(看起来)拥有独立的全局资源实例。同时 Linux 也默认提供了多种 namespace,用于对多种不同资源进行隔离。
在之前,我们单独使用 namespace 的场景比较有限,但 namespace 却是容器化技术的基石。
我们先来看看它的发展历程。
Namespace 的发展历程
图 1 ,namespace 的历史过程
最早期 - Plan 9
namespace 的早期提出及使用要追溯到 Plan 9 from Bell Labs ,贝尔实验室的 Plan 9。这是一个分布式操作系统,由贝尔实验室的计算科学研究中心在八几年至02年开发的(02年发布了稳定的第四版,距离92年发布的第一个公开版本已10年打磨),现在仍然被操作系统的研究者和爱好者开发使用。在 Plan 9 的设计与实现中,我们着重提以下3点内容:
-
文件系统:所有系统资源都列在文件系统中,以 Node 标识。所有的接口也作为文件系统的一部分呈现。
-
Namespace:能更好的应用及展示文件系统的层次结构,它实现了所谓的 “分离”和“独立”。
-
标准通信协议:9P协议(Styx/9P2000)。
图 2 ,Plan 9 from Bell Labs 图标
开始加入 Linux Kernel
Namespace 开始进入 Linux Kernel 的版本是在 2.4.X,最初始于 2.4.19 版本。但是,自 2.4.2 版本才开始实现每个进程的 namespace。
图 3 ,Linux Kernel Note
图 4 ,Linux Kernel 对应的各操作系统版本
Linux 3.8 基本实现
Linux 3.8 中终于完全实现了 User
Namespace 的相关功能集成到内核。这样 Docker 及其他容器技术所用到的 namespace 相关的能力就基本都实现了。
图 5 ,Linux Kernel 从 2001 到2013 逐步演变,完成了 namespace 的实现
Namespace 类型
namespace名称 | 使用的标识 - Flag | 控制内容 |
---|---|---|
Cgroup | CLONE_NEWCGROUP | Cgroup root directory cgroup 根目录 |
IPC | CLONE_NEWIPC | System V IPC, POSIX message queues信号量,消息队列 |
Network | CLONE_NEWNET | Network devices, stacks, ports, etc.网络设备,协议栈,端口等等 |
Mount | CLONE_NEWNS | Mount points挂载点 |
PID | CLONE_NEWPID | Process IDs进程号 |
Time | CLONE_NEWTIME | 时钟 |
User | CLONE_NEWUSER | 用户和组 ID |
UTS | CLONE_NEWUTS | 系统主机名和 NIS(Network Information Service) 主机名(有时称为域名) |
Cgroup namespaces
Cgroup namespace 是进程的 cgroups 的虚拟化视图,通过 /proc/[pid]/cgroup
和 /proc/[pid]/mountinfo
展示。
使用 cgroup namespace 需要内核开启 CONFIG_CGROUPS
选项。可通过以下方式验证:
(MoeLove) ➜ grep CONFIG_CGROUPS /boot/config-$(uname -r)
CONFIG_CGROUPS=y
cgroup namespace 提供的了一系列的隔离支持:
-
防止信息泄漏(容器不应该看到容器外的任何信息)。
-
简化了容器迁移。
-
限制容器进程资源,因为它会把 cgroup 文件系统进行挂载,使得容器进程无法获取上层的访问权限。
每个 cgroup namespace 都有自己的一组 cgroup 根目录。这些 cgroup 的根目录是在 /proc/[pid]/cgroup
文件中对应记录的相对位置的基点。当一个进程用 CLONE_NEWCGROUP
(clone(2) 或者 unshare(2)) 创建一个新的 cgroup namespace时,它当前的 cgroups 的目录就变成了新 namespace 的 cgroup 根目录。
(MoeLove) ➜ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-2.scope
当一个目标进程从 /proc/[pid]/cgroup
中读取 cgroup 关系时,每个记录的路径名会在第三字段中展示,会关联到正在读取的进程的相关 cgroup 分层结构的根目录。如果目标进程的 cgroup 目录位于正在读取的进程的 cgroup namespace 根目录之外时,那么,路径名称将会对每个 cgroup 层次中的上层节点显示 ../
。
我们来看看下面的示例(这里以 cgroup v1 为例,如果你想看 v2 版本的示例,请在留言中告诉我):
-
在初始的 cgroup namespace 中,我们使用 root (或者有 root 权限的用户),在 freezer 层下创建一个子 cgroup 名为 moelove-sub
,同时,将进程放入该 cgroup 进行限制。
(MoeLove) ➜ mkdir -p /sys/fs/cgroup/freezer/moelove-sub
(MoeLove) ➜ sleep 6666666 &
[1] 1489125
(MoeLove) ➜ echo 1489125 > /sys/fs/cgroup/freezer/moelove-sub/cgroup.procs
-
我们在 freezer 层下创建另外一个子 cgroup,名为 moelove-sub2
, 并且再放入执行进程号。可以看到当前的进程已经纳入到moelove-sub2
的 cgroup 下管理了。
(MoeLove) ➜ mkdir -p /sys/fs/cgroup/freezer/moelove-sub2
(MoeLove) ➜ echo $$
1488899
(MoeLove) ➜ echo 1488899 > /sys/fs/cgroup/freezer/moelove-sub2/cgroup.procs
(MoeLove) ➜ cat /proc/self/cgroup |grep freezer
7:freezer:/moelove-sub2
-
我们使用 unshare(1)
创建一个进程,这里使用了-C
参数表示是新的 cgroup namespace, 使用了-m
参数表示是新的 mount namespace。
(MoeLove) ➜ unshare -Cm bash
root@moelove:~#
-
从用 unshare(1) 启动的新 shell 中,我们可以在 /proc/[pid]/cgroup
文件中看到,新 shell 和以上示例中的进程:
root@moelove:~# cat /proc/self/cgroup | grep freezer
7:freezer:/
root@moelove:~# cat /proc/1/cgroup | grep freezer
7:freezer:/..
# 第一个示例进程
root@moelove:~# cat /proc/1489125/cgroup | grep freezer
7:freezer:/../moelove-sub
-
从上面的示例中,我们可以看到新 shell 的 freezer cgroup 关系中,当新的 cgroup namespace 创建时,freezer cgroup 的根目录与它的关系也就建立了。
root@moelove:~# cat /proc/self/mountinfo | grep freezer
1238 1230 0:37 /.. /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
-
第四个字段 ( /..
) 显示了在 cgroup 文件系统中的挂载目录。从 cgroup namespaces 的定义中,我们可以知道,进程当前的 freezer cgroup 目录变成了它的根目录,所以这个字段显示/..
。我们可以重新挂载来处理它。
root@moelove:~# mount --make-rslave /
root@moelove:~# umount /sys/fs/cgroup/freezer
root@moelove:~# mount -t cgroup -o freezer freezer /sys/fs/cgroup/freezer
root@moelove:~# cat /proc/self/mountinfo | grep freezer
1238 1230 0:37 / /sys/fs/cgroup/freezer rw,relatime - cgroup freezer rw,freezer
root@moelove:~# mount |grep freezer
freezer on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer)
IPC namespaces
IPC namespaces 隔离了 IPC 资源,如 System V IPC objects、 POSIX message queues。每个 IPC namespace 都有着自己的一组 System V IPC 标识符,以及 POSIX 消息队列系统。在一个 IPC namespace 中创建的对象,对所有该 namespace 下的成员均可见(对其他 namespace 下的成员均不可见)。
使用 IPC namespace 需要内核支持 CONFIG_IPC_NS 选项。如下:
(MoeLove) ➜ grep CONFIG_IPC_NS /boot/config-$(uname -r)
CONFIG_IPC_NS=y
可以在 IPC namespace 中设置以下 /proc
接口:
-
/proc/sys/fs/mqueue
- POSIX 消息队列接口 -
/proc/sys/kernel
- System V IPC 接口 (msgmax, msgmnb, msgmni, sem, shmall, shmmax, shmmni, shm_rmid_forced) -
/proc/sysvipc
- System V IPC 接口
当 IPC namespace 被销毁时(空间里的最后一个进程都被停止删除时),在 IPC namespace 中创建的 object 也会被销毁。
Network namepaces
Network namespaces 隔离了与网络相关的系统资源(这里罗列一些):
-
network devices - 网络设备
-
IPv4 and IPv6 protocol stacks - IPv4、IPv6 的协议栈
-
IP routing tables - IP 路由表
-
firewall rules - 防火墙规则
-
/proc/net (即 /proc/PID/net)
-
/sys/class/net
-
/proc/sys/net 目录下的文件
-
端口、socket
-
UNIX domain abstract socket namespace
使用 Network namespaces 需要内核支持 CONFIG_NET_NS 选项。如下:
(MoeLove) ➜ grep CONFIG_NET_NS /boot/config-$(uname -r)
CONFIG_NET_NS=y
一个物理网络设备只能存在于一个 Network namespace 中。当一个 Network namespace 被释放时(空间里的最后一个进程都被停止删除时),物理网络设备将被移动到初始的 Network namespace 而不是上层的 Network namespace。
一个虚拟的网络设备(veth(4)) ,在 Network namespace 间通过一个类似管道的方式进行连接。这使得它能存在于多个 Network namespace,但是,当 Network namespace 被摧毁时,该空间下包含的 veth(4) 设备可能被破坏。
Mount namespaces
Mount namespaces 最早出现在 Linux 2.4.19 版本。Mount namespaces 隔离了各空间中挂载的进程实例。每个 mount namespace 的实例下的进程会看到不同的目录层次结构。
每个进程在 mount namespace 中的描述可以在下面的文件视图中看到:
-
/proc/[pid]/mounts
-
/proc/[pid]/mountinfo
-
/proc/[pid]/mountstats
一个新的 Mount namespace 的创建标识是 CLONE_NEWNS ,使用了 clone(2) 或者 unshare(2) 。
-
如果 Mount namespace 用 clone(2) 创建,子 namespace 的挂载列表是从父进程的 mount namespace 拷贝的。 -
如果 Mount namespace 用 unshare(2) 创建,新 namespace 的挂载列表是从调用者之前的 moun namespace 拷贝的。
如果 mount namespace 发生了修改,会引起什么样的连锁反应?下面,我们就在 共享子树中谈谈。
每个 mount 都被可以有如下标记 :
-
MS_SHARED - 与组内每个成员分享 events 。也就是说相同的 mount 或者 unmount 将自动发生在组内其他的 mounts 中。反之,mount 或者 unmount 事件 也会影响这次的 event 动作。
-
MS_PRIVATE - 这个 mount 是私有的。mount 或者 unmount events 都不会影响这次的 event 动作。
-
MS_SLAVE - mount 或者 unmount events 会从 master 节点传入影响该节点。但是这个节点下的 mount 或者 unmount events 不会影响组内的其他节点。
-
MS_UNBINDABLE - 这也是个私有的 mount 。任何尝试绑定的 mount 在这个设置下都将失败。
在文件 /proc/[pid]/mountinfo
中可以看到 propagation
类型的字段。每个对等组都会由内核生成唯一的 ID ,同一对等组的 mount 都是这个 ID(即,下文中的 X )。
(MoeLove) ➜ cat /proc/self/mountinfo |grep root
65 1 0:33 /root / rw,relatime shared:1 - btrfs /dev/nvme0n1p6 rw,seclabel,compress=zstd:1,ssd,space_cache,subvolid=256,subvol=/root
1210 65 0:33 /root/var/lib/docker/btrfs /var/lib/docker/btrfs rw,relatime shared:1 - btrfs /dev/nvme0n1p6 rw,seclabel,compress=zstd:1,ssd,space_cache,subvolid=256,subvol=/root
-
shared:X - 在组 X 中共享。
-
master:X - 对于组 X 而言是 slave,即,从属于 ID 为 X 的主。
-
propagate_from:X - 接收从组 X 发出的共享 mount。这个标签总是个 master:X 一同出现。
-
unbindable - 表示不能被绑定,即,不与其他关联从属。
新 mount namespace 的传播类型取决于它的父节点。如果父节点的传播类型是 MS_SHARED ,那么新 mount namespace 的传播类型是 MS_SHARED ,不然会默认为 MS_PRIVATE。
关于 mount namespaces 我们还需要注意以下几点:
(1)每个 mount namespace 都有一个 owner user namespace。如果新的 mount namespace 和拷贝的 mount namespace 分属于不同的 user namespace ,那么,新的 mount namespace 优先级低。
(2)当创建的 mount namespace 优先级低时,那么,slave 的 mount events 会优先于 shared 的 mount events。
(3)高优先级和低优先级的 mount namespace 有关联被锁定在一起时,他们都不能被单独卸载。
(4)mount(2) 标识和 atime 标识会被锁定,即,不能被传播影响而修改。
小结
以上就是关于 Linux 内核中 namespace 的一些介绍了,篇幅原因,剩余部分以及 namespace 在容器中的应用我们放在下一篇中介绍,敬请期待!
欢迎订阅我的文章公众号【MoeLove】
本文分享自微信公众号 - MoeLove(TheMoeLove)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Linus 全身每一个细胞都在拒绝 GPLv3
作者:lola “GPLv3 的作者是一群伪君子。” “那些将 GPLv3 视为有史以来最好的东西的人,有多少是真正做成事情了的?我问了 25—30 位最顶级的内核开发人员,没有一个人喜欢 GPLv3。” “做了错误的技术决策,才会选择 GPLv3。杰出工程师的标志之一是做出正确技术选择,GPLv2 可以接受,GPLv3 不能。” “GPLv3 只会让自由软件更不自由。” ...... GPLv3 作为现在最主流的 copyleft 开源许可证之一,却在诞生之初被 Linux 之父 Linus Torvalds 疯狂吐槽。 2006 年 1 月,自由软件基金会(Free Software Foudation,简称 FSF)公布了 GPLv3 第一个草案,并开启了预计为期一年的讨论和修订,Linus 这时就坚定 say no。这一年里,Linus 一边批评一边拒不参与,说了不少狠话。 “我当然希望最终的 GPLv3 不会有什么明显问题,但我对这些草稿非常不满意。”不仅是 Linus,当时在 Linux 的核心开发小组, 29 个高级架构师有 28 个反对这个协议。 2007 年,GPLv...
- 下一篇
工信部频繁下架侵害用户权益的APP,对我们的影响有多大?
WOT全球技术创新大会2022,门票6折抢购中!购票立减2320元! 近日,包括豆瓣在内的106款因损害用户权益被下架引发关注。这已经不是工信部第一次出手整治违规App,近几年知名App因违规被处罚频频登上热搜。数据监管已经成为我国立法的重点关照对象,相关法律法规陆陆续续出台。 其中包括了《个人信息保护法》,规定APP在使用过程中,不得收集超出必要范围的个人信息,并出台《常见类型移动互联网应用程序必要个人信息范围规定》,对不同类型App的功能、可以采集的信息进行了规定。 很快,各大知名App响应了政策,纷纷对功能进行调整。以微博为例,在微不可查的隐私栏最下面增加了三个文件。 再比如抖音,用户已经可以选择关闭“个性化推荐。” 在强监管下,许多App开始规范自己收集和使用个人信息的行为。但是,在持续推进App侵害用户权益专项整治行动中,仍然有不少App在多次警告后,仍然存在超范围收集用户信息等行为,可见还是有不少企业抱着侥幸心理。 可能有人对加强数据收集的监管会感到不解。 似乎App超范围违规收集个人信息,对我们的生活没有产生什么影响? 正如很早之前李彦宏所说的一样:“中国人愿意用隐私换取...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果