<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=850&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / Gentoo 之 Namespaces support]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=850</link>
		<description><![CDATA[Gentoo 之 Namespaces support 最近发表的帖子。]]></description>
		<lastBuildDate>Sun, 31 Mar 2024 10:45:51 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[Gentoo 之 Namespaces support]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=969#p969</link>
			<description><![CDATA[<p>目前我们所提到的容器技术、虚拟化技术（不论何种抽象层次下的虚拟化技术）都能做到资源层面上的隔离和限制。</p><p>对于容器技术而言，它实现资源层面上的限制和隔离，依赖于 Linux 内核所提供的 cgroup 和 namespace 技术。</p><p>我们先对这两项技术的作用做个概括：</p><p>cgroup 的主要作用：管理资源的分配、限制；<br />namespace 的主要作用：封装抽象，限制，隔离，使命名空间内的进程看起来拥有他们自己的全局资源；</p><p>本篇，我们重点来聊 namespace 。</p><p>Namespace 是什么？</p><p>我们引用 wiki 上对 namespace 的定义：</p><p>“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&#160; resources and processes, but those namespaces refer to distinct resources.”</p><p>namespace 是 Linux 内核的一项特性，它可以对内核资源进行分区，使得一组进程可以看到一组资源；而另一组进程可以看到另一组不同的资源。该功能的原理是为一组资源和进程使用相同的 namespace，但是这些 namespace 实际上引用的是不同的资源。</p><p>这样的说法未免太绕了些，简单来说 namespace 是由 Linux 内核提供的，用于进程间资源隔离的一种技术。将全局的系统资源包装在一个抽象里，让进程（看起来）拥有独立的全局资源实例。同时 Linux 也默认提供了多种 namespace，用于对多种不同资源进行隔离。</p><p>在之前，我们单独使用 namespace 的场景比较有限，但 namespace 却是容器化技术的基石。</p><p>我们先来看看它的发展历程。</p><p>Namespace 的发展历程<br /><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/754701409.jpeg" alt="FluxBB bbcode 测试" /></span> </p><p>图 1 ，namespace 的历史过程<br />最早期 - Plan 9</p><p>namespace 的早期提出及使用要追溯到 Plan 9 from Bell Labs ，贝尔实验室的 Plan 9。这是一个分布式操作系统，由贝尔实验室的计算科学研究中心在八几年至02年开发的（02年发布了稳定的第四版，距离92年发布的第一个公开版本已10年打磨），现在仍然被操作系统的研究者和爱好者开发使用。在 Plan 9 的设计与实现中，我们着重提以下3点内容：</p><p>文件系统：所有系统资源都列在文件系统中，以 Node 标识。所有的接口也作为文件系统的一部分呈现。<br />Namespace：能更好的应用及展示文件系统的层次结构，它实现了所谓的 “分离”和“独立”。<br />标准通信协议：9P协议（Styx/9P2000）。</p><p><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/3214247513.jpeg" alt="FluxBB bbcode 测试" /></span> </p><p>开始加入 Linux Kernel</p><p>Namespace 开始进入 Linux Kernel 的版本是在 2.4.X，最初始于 2.4.19 版本。但是，自 2.4.2 版本才开始实现每个进程的 namespace。</p><p><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/126919412.jpeg" alt="FluxBB bbcode 测试" /></span> </p><p>图 3 ，Linux Kernel Note</p><p><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/1881369241.png" alt="FluxBB bbcode 测试" /></span> </p><p>图 4 ，Linux Kernel 对应的各操作系统版本<br />Linux 3.8 基本实现</p><p>Linux 3.8 中终于完全实现了 User Namespace 的相关功能集成到内核。这样 Docker 及其他容器技术所用到的 namespace 相关的能力就基本都实现了。</p><p><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/791322016.png" alt="FluxBB bbcode 测试" /></span> </p><p>图 5 ，Linux Kernel 从 2001 到2013 逐步演变，完成了 namespace 的实现</p><p>Namespace 类型</p><p><span class="postimg"><img src="https://www.batsom.net/usr/uploads/2024/03/1441092924.png" alt="FluxBB bbcode 测试" /></span> </p><p>系统主机名和 NIS(Network Information Service) 主机名（有时称为域名）</p><p>Cgroup namespaces</p><p>Cgroup namespace 是进程的 cgroups 的虚拟化视图，通过 /proc/[pid]/cgroup 和 /proc/[pid]/mountinfo 展示。</p><p>使用 cgroup namespace 需要内核开启 CONFIG_CGROUPS 选项。可通过以下方式验证：</p><div class="codebox"><pre><code>(MoeLove) ➜ grep CONFIG_CGROUPS /boot/config-$(uname -r)
CONFIG_CGROUPS=y</code></pre></div><p>cgroup namespace 提供的了一系列的隔离支持：</p><p>防止信息泄漏（容器不应该看到容器外的任何信息）。<br />简化了容器迁移。<br />限制容器进程资源，因为它会把 cgroup 文件系统进行挂载，使得容器进程无法获取上层的访问权限。</p><p>每个 cgroup namespace 都有自己的一组 cgroup 根目录。这些 cgroup 的根目录是在 /proc/[pid]/cgroup 文件中对应记录的相对位置的基点。当一个进程用 CLONE_NEWCGROUP（clone(2) 或者 unshare(2)） 创建一个新的 cgroup namespace时，它当前的 cgroups 的目录就变成了新 namespace 的 cgroup 根目录。</p><div class="codebox"><pre><code>(MoeLove) ➜ cat /proc/self/cgroup 
0::/user.slice/user-1000.slice/session-2.scope</code></pre></div><p>当一个目标进程从 /proc/[pid]/cgroup 中读取 cgroup 关系时，每个记录的路径名会在第三字段中展示，会关联到正在读取的进程的相关 cgroup 分层结构的根目录。如果目标进程的 cgroup 目录位于正在读取的进程的 cgroup namespace 根目录之外时，那么，路径名称将会对每个 cgroup 层次中的上层节点显示 ../ 。</p><p>我们来看看下面的示例（这里以 cgroup v1 为例，如果你想看 v2 版本的示例，请在留言中告诉我）：</p><p>在初始的 cgroup namespace 中，我们使用 root （或者有 root 权限的用户），在 freezer 层下创建一个子 cgroup 名为 moelove-sub，同时，将进程放入该 cgroup 进行限制。</p><div class="codebox"><pre><code>**(MoeLove) ➜ mkdir -p /sys/fs/cgroup/freezer/moelove-sub
(MoeLove) ➜ sleep 6666666 &amp; 
[1] 1489125                  
(MoeLove) ➜ echo 1489125 &gt; /sys/fs/cgroup/freezer/moelove-sub/cgroup.procs
**</code></pre></div><p>我们在 freezer 层下创建另外一个子 cgroup，名为 moelove-sub2， 并且再放入执行进程号。可以看到当前的进程已经纳入到&#160; moelove-sub2的 cgroup 下管理了。</p><div class="codebox"><pre><code>(MoeLove) ➜ mkdir -p /sys/fs/cgroup/freezer/moelove-sub2
(MoeLove) ➜ echo $$
1488899
(MoeLove) ➜ echo 1488899 &gt; /sys/fs/cgroup/freezer/moelove-sub2/cgroup.procs 
(MoeLove) ➜ cat /proc/self/cgroup |grep freezer
7:freezer:/moelove-sub2</code></pre></div><p>我们使用 unshare(1) 创建一个进程，这里使用了 -C参数表示是新的 cgroup namespace， 使用了 -m参数表示是新的 mount namespace。</p><div class="codebox"><pre><code>(MoeLove) ➜ unshare -Cm bash
root@moelove:~#</code></pre></div><p>从用 unshare(1) 启动的新 shell 中，我们可以在 /proc/[pid]/cgroup 文件中看到，新 shell 和以上示例中的进程：</p><div class="codebox"><pre><code>root@moelove:~# cat /proc/self/cgroup | grep freezer
7:freezer:/
root@moelove:~# cat /proc/1/cgroup | grep freezer
7:freezer:/..</code></pre></div><p># 第一个示例进程</p><div class="codebox"><pre><code>root@moelove:~# cat /proc/1489125/cgroup | grep freezer
7:freezer:/../moelove-sub</code></pre></div><p>从上面的示例中，我们可以看到新 shell 的 freezer cgroup 关系中，当新的 cgroup namespace 创建时，freezer cgroup 的根目录与它的关系也就建立了。</p><div class="codebox"><pre><code>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</code></pre></div><p>第四个字段 ( /..) 显示了在 cgroup 文件系统中的挂载目录。从 cgroup namespaces 的定义中，我们可以知道，进程当前的 freezer cgroup 目录变成了它的根目录，所以这个字段显示 /.. 。我们可以重新挂载来处理它。</p><div class="codebox"><pre><code>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)</code></pre></div><p>IPC namespaces</p><p>IPC namespaces 隔离了 IPC 资源，如 System V IPC objects、&#160; POSIX message queues。每个 IPC namespace 都有着自己的一组 System V IPC 标识符，以及 POSIX 消息队列系统。在一个 IPC namespace 中创建的对象，对所有该 namespace 下的成员均可见（对其他 namespace 下的成员均不可见）。</p><p>使用 IPC namespace 需要内核支持 CONFIG_IPC_NS 选项。如下：</p><div class="codebox"><pre><code>(MoeLove) ➜ grep CONFIG_IPC_NS /boot/config-$(uname -r)
CONFIG_IPC_NS=y</code></pre></div><p>可以在 IPC namespace 中设置以下 /proc 接口：</p><div class="codebox"><pre><code>    /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 接口</code></pre></div><p>当 IPC namespace 被销毁时（空间里的最后一个进程都被停止删除时），在 IPC namespace 中创建的 object 也会被销毁。<br />Network namepaces</p><p>Network namespaces 隔离了与网络相关的系统资源（这里罗列一些）：</p><div class="codebox"><pre><code>    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</code></pre></div><p>使用 Network namespaces 需要内核支持 CONFIG_NET_NS 选项。如下：</p><div class="codebox"><pre><code>(MoeLove) ➜ grep CONFIG_NET_NS /boot/config-$(uname -r)
CONFIG_NET_NS=y</code></pre></div><p>一个物理网络设备只能存在于一个 Network namespace 中。当一个 Network namespace 被释放时（空间里的最后一个进程都被停止删除时），物理网络设备将被移动到初始的 Network namespace 而不是上层的 Network namespace。</p><p>一个虚拟的网络设备(veth(4)) ，在 Network namespace 间通过一个类似管道的方式进行连接。这使得它能存在于多个 Network namespace，但是，当 Network namespace 被摧毁时，该空间下包含的 veth(4) 设备可能被破坏。<br />Mount namespaces</p><p>Mount namespaces 最早出现在 Linux 2.4.19 版本。Mount namespaces 隔离了各空间中挂载的进程实例。每个 mount namespace 的实例下的进程会看到不同的目录层次结构。</p><p>每个进程在 mount namespace 中的描述可以在下面的文件视图中看到：</p><p>&#160; /proc/[pid]/mounts<br />&#160; /proc/[pid]/mountinfo<br />&#160; /proc/[pid]/mountstats</p><p>一个新的 Mount namespace 的创建标识是 CLONE_NEWNS ，使用了 clone(2) 或者 unshare(2) 。</p><p>如果 Mount namespace 用 clone(2) 创建，子 namespace 的挂载列表是从父进程的 mount namespace 拷贝的。<br />如果 Mount namespace 用 unshare(2) 创建，新 namespace 的挂载列表是从调用者之前的 moun namespace 拷贝的。</p><p>如果 mount namespace 发生了修改，会引起什么样的连锁反应？下面，我们就在 共享子树中谈谈。</p><p>每个 mount 都被可以有如下标记 ：</p><p>MS_SHARED - 与组内每个成员分享 events 。也就是说相同的 mount 或者 unmount 将自动发生在组内其他的 mounts&#160; 中。反之，mount 或者 unmount 事件 也会影响这次的 event 动作。<br />MS_PRIVATE - 这个 mount 是私有的。mount 或者 unmount events 都不会影响这次的 event 动作。<br />MS_SLAVE - mount 或者 unmount events 会从 master 节点传入影响该节点。但是这个节点下的 mount 或者 unmount events 不会影响组内的其他节点。<br />MS_UNBINDABLE - 这也是个私有的 mount 。任何尝试绑定的 mount 在这个设置下都将失败。</p><p>在文件 /proc/[pid]/mountinfo 中可以看到 propagation 类型的字段。每个对等组都会由内核生成唯一的 ID ，同一对等组的 mount 都是这个 ID（即，下文中的 X ）。</p><div class="codebox"><pre><code>(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</code></pre></div><p>&#160; shared:X - 在组 X 中共享。<br />&#160; master:X - 对于组 X 而言是 slave，即，从属于 ID 为 X 的主。<br />&#160; propagate_from:X - 接收从组 X 发出的共享 mount。这个标签总是个 master:X 一同出现。<br />&#160; unbindable -&#160; 表示不能被绑定，即，不与其他关联从属。</p><p>新 mount namespace 的传播类型取决于它的父节点。如果父节点的传播类型是 MS_SHARED ，那么新 mount namespace 的传播类型是 MS_SHARED ，不然会默认为 MS_PRIVATE。</p><p>关于 mount namespaces 我们还需要注意以下几点：</p><p>（1）每个&#160; mount namespace 都有一个 owner user namespace。如果新的 mount namespace 和拷贝的 mount namespace 分属于不同的 user namespace ，那么，新的 mount namespace 优先级低。</p><p>（2）当创建的 mount namespace 优先级低时，那么，slave 的 mount events 会优先于 shared 的 mount events。</p><p>（3）高优先级和低优先级的 mount namespace 有关联被锁定在一起时，他们都不能被单独卸载。</p><p>（4）mount(2) 标识和 atime 标识会被锁定，即，不能被传播影响而修改。<br />小结</p><p>以上就是关于 Linux 内核中 namespace 的一些介绍了，篇幅原因，剩余部分以及 namespace 在容器中的应用我们放在下一篇中介绍，敬请期待</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Sun, 31 Mar 2024 10:45:51 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=969#p969</guid>
		</item>
	</channel>
</rss>
