Linux Namespace系列(04):mount namespaces (CLONE_NEWNS)

Mount namespace用来隔离文件系统的挂载点, 使得不同的mount namespace拥有自己独立的挂载点信息,不同的namespace之间不会相互影响,这对于构建用户或者容器自己的文件系统目录非常有用。

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

Mount namespaces是第一个被加入Linux的namespace,由于当时没想到还会引入其它的namespace,所以取名为CLONE_NEWNS,而没有叫CLONE_NEWMOUNT。

每个mount namespace都拥有一份自己的挂载点列表,当用clone或者unshare函数创建新的mount namespace时,新创建的namespace将拷贝一份老namespace里的挂载点列表,但从这之后,他们就没有关系了,通过mount和umount增加和删除各自namespace里面的挂载点都不会相互影响。

本篇所有例子都在ubuntu-server-x86_64 16.04下执行通过

演示

#--------------------------第一个shell窗口----------------------
#先准备两个iso文件,用于后面的mount测试
dev@ubuntu:~$ mkdir iso
dev@ubuntu:~$ cd iso/
dev@ubuntu:~/iso$ mkdir -p iso01/subdir01
dev@ubuntu:~/iso$ mkdir -p iso02/subdir02
dev@ubuntu:~/iso$ mkisofs -o ./001.iso ./iso01
dev@ubuntu:~/iso$ mkisofs -o ./002.iso ./iso02
dev@ubuntu:~/iso$ ls
001.iso  002.iso  iso01  iso02
#准备目录用于mount
dev@ubuntu:~/iso$ sudo mkdir /mnt/iso1 /mnt/iso2

#查看当前所在的mount namespace
dev@ubuntu:~/iso$ readlink /proc/$$/ns/mnt
mnt:[4026531840]

#mount 001.iso 到 /mnt/iso1/
dev@ubuntu:~/iso$ sudo mount ./001.iso /mnt/iso1/
mount: /dev/loop1 is write-protected, mounting read-only

#mount成功
dev@ubuntu:~/iso$ mount |grep /001.iso
/home/dev/iso/001.iso on /mnt/iso1 type iso9660 (ro,relatime)

#创建并进入新的mount和uts namespace
dev@ubuntu:~/iso$ sudo unshare --mount --uts /bin/bash
#更改hostname并重新加载bash
root@ubuntu:~/iso# hostname container001
root@ubuntu:~/iso# exec bash
root@container001:~/iso#

#查看新的mount namespace
root@container001:~/iso# readlink /proc/$$/ns/mnt
mnt:[4026532455]

#老namespace里的挂载点的信息已经拷贝到新的namespace里面来了
root@container001:~/iso# mount |grep /001.iso
/home/dev/iso/001.iso on /mnt/iso1 type iso9660 (ro,relatime)

#在新namespace中mount 002.iso
root@container001:~/iso# mount ./002.iso /mnt/iso2/
mount: /dev/loop0 is write-protected, mounting read-only
root@container001:~/iso# mount |grep iso
/home/dev/iso/001.iso on /mnt/iso1 type iso9660 (ro,relatime)
/home/dev/iso/002.iso on /mnt/iso2 type iso9660 (ro,relatime)

#umount 001.iso
root@container001:~/iso# umount /mnt/iso1
root@container001:~/iso# mount |grep iso
/home/dev/iso/002.iso on /mnt/iso2 type iso9660 (ro,relatime)

#/mnt/iso1目录变为空
root@container001:~/iso# ls /mnt/iso1
root@container001:~/iso#

#--------------------------第二个shell窗口----------------------
#打开新的shell窗口,老namespace中001.iso的挂载信息还在
#而在新namespace里面mount的002.iso这里看不到
dev@ubuntu:~$ mount |grep iso
/home/dev/iso/001.iso on /mnt/iso1 type iso9660 (ro,relatime)
#iso1目录里面也有内容
dev@ubuntu:~$ ls /mnt/iso1
subdir01
#说明两个namespace中的mount信息是隔离的

Shared subtrees

在某些情况下,比如系统添加了一个新的硬盘,这个时候如果mount namespace是完全隔离的,想要在各个namespace里面用这个硬盘,就需要在每个namespace里面手动mount这个硬盘,这个是很麻烦的,这时Shared subtrees就可以帮助我们解决这个问题。

关于Shared subtrees的详细介绍请参考Linux mount (第二部分),里面有他的详细介绍以及bind nount的例子。

演示

对Shared subtrees而言,mount namespace和bind mount的情况差不多,这里就简单演示一下shared和private两种类型

#--------------------------第一个shell窗口----------------------
#准备4个虚拟的disk,并在上面创建ext2文件系统,用于后续的mount测试
dev@ubuntu:~/iso$ cd && mkdir disks && cd disks
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk1.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk2.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk3.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk4.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk1.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk2.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk3.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk4.img
#准备两个目录用于挂载上面创建的disk
dev@ubuntu:~/disks$ mkdir disk1 disk2
dev@ubuntu:~/disks$ ls
disk1  disk1.img  disk2  disk2.img  disk3.img  disk4.img


#显式的分别以shared和private方式挂载disk1和disk2
dev@ubuntu:~/disks$ sudo mount --make-shared ./disk1.img ./disk1
dev@ubuntu:~/disks$ sudo mount --make-private ./disk2.img ./disk2
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105
173 24 7:2 / /home/dev/disks/disk2 rw,relatime

#查看mount namespace编号
dev@ubuntu:~/disks$ readlink /proc/$$/ns/mnt
mnt:[4026531840]

#--------------------------第二个shell窗口----------------------
#重新打开一个新的shell窗口
dev@ubuntu:~$ cd ./disks
#创建新的mount namespace
#默认情况下,unshare会将新namespace里面的所有挂载点的类型设置成private,
#所以这里用到了参数--propagation unchanged,
#让新namespace里的挂载点的类型和老namespace里保持一致。
#--propagation参数还支持private|shared|slave类型,
#和mount命令的那些--make-private参数一样,
#他们的背后都是通过调用mount(...)函数传入不同的参数实现的
dev@ubuntu:~/disks$ sudo unshare --mount --uts --propagation unchanged /bin/bash
root@ubuntu:~/disks# hostname container001
root@ubuntu:~/disks# exec bash
root@container001:~/disks# 

#确认已经是在新的mount namespace里面了
root@container001:~/disks# readlink /proc/$$/ns/mnt
mnt:[4026532463]

#由于前面指定了--propagation unchanged,
#所以新namespace里面的/home/dev/disks/disk1也是shared,
#且和老namespace里面的/home/dev/disks/disk1属于同一个peer group 105
#因为在不同的namespace里面,所以这里挂载点的ID和原来namespace里的不一样了
root@container001:~/disks# cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
221 177 7:1 / /home/dev/disks/disk1 rw,relatime shared:105
222 177 7:2 / /home/dev/disks/disk2 rw,relatime

#分别在disk1和disk2目录下创建disk3和disk4,然后挂载disk3,disk4到这两个目录
root@container001:~/disks# mkdir ./disk1/disk3 ./disk2/disk4
root@container001:~/disks# mount ./disk3.img ./disk1/disk3/
root@container001:~/disks# mount ./disk4.img ./disk2/disk4/
root@container001:~/disks# cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
221 177 7:1 / /home/dev/disks/disk1 rw,relatime shared:105
222 177 7:2 / /home/dev/disks/disk2 rw,relatime
223 221 7:3 / /home/dev/disks/disk1/disk3 rw,relatime shared:107
227 222 7:4 / /home/dev/disks/disk2/disk4 rw,relatime

#--------------------------第一个shell窗口----------------------
#回到第一个shell窗口

#可以看出由于/home/dev/disks/disk1是shared,且两个namespace里的这个挂载点都属于peer group 105,
#所以在新namespace里面挂载的disk3,在老的namespace里面也看的到
#但是看不到disk4的挂载信息,那是因为/home/dev/disks/disk2是private的
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105
173 24 7:2 / /home/dev/disks/disk2 rw,relatime
224 164 7:3 / /home/dev/disks/disk1/disk3 rw,relatime shared:107

#我们可以随时修改挂载点的propagation type
#这里我们通过mount命令将disk3改成了private类型
dev@ubuntu:~/disks$ sudo mount --make-private /home/dev/disks/disk1/disk3
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk3| sed 's/ - .*//'
224 164 7:3 / /home/dev/disks/disk1/disk3 rw,relatime

#--------------------------第二个shell窗口----------------------
#回到第二个shell窗口,disk3的propagation type还是shared,
#表明在老的namespace里面对propagation type的修改不会影响新namespace里面的挂载点
root@container001:~/disks# cat /proc/self/mountinfo |grep disk3| sed 's/ - .*//'
223 221 7:3 / /home/dev/disks/disk1/disk3 rw,relatime shared:107

关于mount命令和mount namespace的配合,里面有很多技巧,后面如果需要用到更复杂的用法,会再做详细的介绍。

参考


Linux程序员
专注Linux相关技术
3.7k 声望
1.6k 粉丝
0 条评论
推荐阅读
走进docker(07):docker start命令背后发生了什么?
在上一篇介绍过了docker create之后,这篇来看看docker start是怎么根据create之后的结果运行容器的。 启动容器 在这里我们先启动上一篇中创建的那个容器,然后看看docker都干了些什么。 {代码...} start的大概流...

public08218阅读 14.1k评论 5

工具篇:iTerm与Zsh
iTerm2支持许多的主题配色,可以自己定义,也可以参考网上现成的主题配色。我个人比较喜欢draculatheme配色。支持item,vim,phpstorm , 下方存在主题官网路径,按照教程安装即可。

super白4阅读 4.8k

深入剖析容器网络和 iptables
Docker 能为我们提供很强大和灵活的网络能力,很大程度上要归功于与 iptables 的结合。在使用时,你可能没有太关注到 iptables 的作用,这是因为 Docker 已经帮我们自动完成了相关的配置。

张晋涛3阅读 1.3k

封面图
麒麟操作系统 (kylinos) 从入门到精通 - 常用软件安装 - 第三篇 常用软件安装(windows下的习惯)
本篇内容大部分从应用商店进行安装,部分通过官网下载,少部分通过命令行安装。1.原生应用1.1钉钉1.2飞书1.3 蓝信1.4 腾讯文档1.5 金山文档1.6 搜狗输入法(拼音)1.7 五笔输入法1.8 libreoffice官方也带了WPS,...

码上世界3阅读 7.6k评论 17

封面图
.记一次使用gdb诊断gc问题全过程
上次解决了GC长耗时问题后,系统果然平稳了许多,这是之前的文章《GC耗时高,原因竟是服务流量小?》 然而,过了一段时间,我检查GC日志时,又发现了一个GC问题,如下: 从这个图中可以发现,我们GC有一些尖峰,...

扣钉日记2阅读 675

封面图
麒麟操作系统 (kylinos) 从入门到精通 - 办公环境 - 第十三篇 字体安装与windows字体的利用
正常情况下,系统下的wps或libreoffice用到的字体都已默认安装。但有时候一些ttf字体或者microsoft office下一些字体需要安装。我们可以在安装好office、acrobat之后,将C:\windows\Fonts文件夹中的字体(我是win...

码上世界2阅读 4.1k

封面图
麒麟操作系统 (kylinos) 从入门到精通 - 影音娱乐- 第三十篇 利用PKS观看电视台(IPTV)直播
类别:笔记本型号:中国长城 UF712硬件平台:飞腾处理器(ArmV8 指令集)系统:银河麒麟操作系统 V10 SP1(2203) 关键词:信创,麒麟系统,linux,PKS,银河麒麟,飞腾,arm64,arm,湖南卫视,中央电视台,电视直播,iptv

码上世界1阅读 3.9k

封面图
3.7k 声望
1.6k 粉丝
宣传栏