本文演示 CentOS 7.6:Linux testerfans 3.10.0-1160.45.1.el7.x86_64
前言
Mount namespace在Linux 2.4.19加入内核中,是第一个被加入到Linux的namespace。有一点需要注意,由于当时没有计划加入其它namespace,所以取名为CLONE_NEWNS,而没有叫CLONE_NEWMOUNT,这个是和其他的namespace命名上有些区别,本章我们将具体了解一下mount namespace。
mount namespace概述
mount namespace使不同的挂载命名空间之间拥有自己的挂载点信息,为进程提供独立的文件系统视图,实现不同的namespace之间文件系统的挂载点隔离,不会相互影响。
当Linux系统启动后会创建一个初始的mnt namespace,在使用clone()或unshare()系统调用通过CLONE_NEWNS标志来创建新的mnt namespace时,这个新的mnt namespace将会拷贝一份clone()或unshare()调用方的挂载点列表。之后对A和A’命名空间下的挂载点进行更改彼此之间不会产生影响(后边介绍的subtrees实际会有影响),进程A’-1和A’-2对所在的A’命名空间下的mount信息可见。
内核将每个进程的挂载点信息保存在/proc/<pid>/{mountinfo,mounts,mountstats}三个文件中:
[root@testerfans proc]# ls -1 /proc/$$/mount*
/proc/12054/mountinfo
/proc/12054/mounts
/proc/12054/mountstats
mount namespace演示
接下来我们将在CentOS7.0上通弄过iso文件的挂载来演示mount namespace对文件系统的隔离。
安装mkisofs命令
在系统内通过mkisofs --version来检查是否有mkisofs命令,如果没有请先进行安装并确认安装成功。
[root@testerfans /]# yum install mkisofs
[root@testerfans /]# mkisofs --version
mkisofs 2.01 is not what you see here. This line is only a fake for too clever
GUIs and other frontend applications. In fact, this program is:
genisoimage 1.1.11 (Linux)
创建演示目录和文件
[root@testerfans ~]# sudo mkdir /demo && sudo chmod 777 /demo && cd $_
[root@testerfans demo]# mkdir -p iso1/subdir1
[root@testerfans demo]# mkdir -p iso2/subdir2
[root@testerfans demo]# mkisofs -o 1.iso ./iso1
[root@testerfans demo]# mkisofs -o 2.iso ./iso2
[root@testerfans demo]# ls
1.iso 2.iso iso1 iso2
创建挂载点
[root@testerfans /]# mkdir /mnt/iso1 /mnt/iso2
[root@testerfans /]# ls /mnt/
iso1 iso2
演示
为了方便对比效果,我们将终端拆分成两个窗口,左侧为shell1,右侧为shell2。
- 在shell1中将1.iso挂载到/mnt/iso1目录下。
#-------------------------------shell1-------------------------------
[root@testerfans demo]# mount 1.iso /mnt/iso1/
mount: /dev/loop0 is write-protected, mounting read-only
- 在shell2中执行 sudo unshare -m:
#-------------------------------shell2-------------------------------
[root@testerfans ~]# unshare -m
- 对比两个挂载命名空间在两个 shell 中分别执行 readlink /proc/$$/ns/mnt 命令,对比查到的inode number。
如图显示,两个shell的mnt命名空间不同。
- 在两个 shell 中再分别执行mount | grep 1.iso,对比两个命名空间的挂载点信息是否是拷贝的。
如图显示,两个shell完成了挂载点信息的拷贝。
- 在 shell2 中卸载1.iso并将2.iso挂载到/mnt/iso2。
#-------------------------------shell2-------------------------------
[root@testerfans demo]# mount 2.iso /mnt/iso2
[root@testerfans demo]# umount /mnt/iso1
[root@testerfans demo]# mount | grep iso
如图显示,在shell2内执行umount /mnt/iso1和mount 2.iso /mnt/iso2并未影响shell1内的挂载点信息,说明两个shell的mnt namespace是隔离的。
shared subtrees介绍
Linux的每个挂载点都具有一个决定该挂载点是否共享子挂载点的属性,称为shared subtrees。该属性以决定某挂载点之下新增或移除子挂载点时,是否同步影响它【副本】挂载点,允许在命名空间之间自动、可控地传播挂载和卸载事件。共享子树特性是在 2006 年初,即大约挂载命名空间实现了三年后加入到Linux 2.6.15中的。
共享子树的作用
假设基于root namespace创建了一个mnt namespace(ns1),那么ns1将具有当前root namespace的挂载点信息拷贝。如果此时新插入了一块磁盘并对其分区格式化,然后在root namespace中对其进行挂载,默认情况下,在ns1中将看不到新挂载的文件系统。这种默认行为可以通过修改shared subtrees属性来改变。
其实,用户创建namespace,其目的一般是希望创建完全隔离的运行环境,所以默认情况下,拷贝挂载点信息时不会拷贝shared subtrees属性,而是将mount namespace中的所有挂载点的shared subtrees属性设置为private。在mnt namespace的示例中,我们演示了两个mnt namespace的隔离可以说明这一点:
- 在shell1所在的ns中新增或者移除挂载点不会影响shell2所在的ns。
- 在shell2所在的ns中新增或者移除挂载点同样不会影响shell1所在的ns。
unshare --propagation参数
上述这种默认的行为是可以进行改变的,使用unshare方法有 --propagation private|shared|slave|unchanged选项可以控制创建mnt namespace挂载点的共享方式,具体可以通过unshanre(1)查看具体介绍。
- private:表示新创建的mnt namespace中的挂载点的shared subtrees属性都设置为private,即ns1和ns2的挂载点互不影响。
- shared:表示新创建的mnt namespace中的挂载点的shared subtrees属性都设置为shared,即ns1或ns2中新增或移除子挂载点都会同步到另一方。
- slave:表示新创建的mnt namespace中的挂载点的shared subtrees属性都设置为slave,即ns1中新增或移除子挂载点会影响ns2,但ns2不会影响ns1。
- unchanged:表示拷贝挂载点信息时也拷贝挂载点的shared subtrees属性,也就是说挂载点A原来是shared,在mnt namespace中也将是shared。
- 不指定–progapation选项时,创建的mount namespace中的挂载点的shared subtrees默认值是private。
使用mnt namespace的数据进行命令演示。
- 准备验证数据。
#-------------------------------shell1-------------------------------
[root@testerfans demo] dd if=/dev/zero bs=1M count=32 of=./disk1.img
[root@testerfans demo] dd if=/dev/zero bs=1M count=32 of=./disk2.img
[root@testerfans demo] mkfs.ext2 ./disk1.img
[root@testerfans demo] mkfs.ext2 ./disk2.img
- 创建挂载点。
#-------------------------------shell1-------------------------------
[root@testerfans demo] mkfdir /mnt/disk1
[root@testerfans demo] mkfdir /mnt/disk2
- 将disk1.img和disk2.img分别以shared和private挂载到/mnt/disk1和/mnt/disk2
挂载时使用参数具体可以参考mount(8)介绍。
#-------------------------------shell1-------------------------------
[root@testerfans demo]# sudo mount --make-shared disk1.img /mnt/disk1
[root@testerfans demo]# sudo mount --make-private disk2.img /mnt/disk2
- 重新打开一个shell2窗口执行unshare -m --propagation shared。
#-------------------------------shell2-------------------------------
[root@testerfans ~]# unshare -m --propagation shared
- 查看两个shell的mntspace信息情况。
$ readlink /proc/$$/ns/mnt
$ cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
如图显示,我们可以看到subtrees属性为private的挂载点/mnt/disk2在使用unshare -m --propagation shared命令后,在新的命名空间内变成了shared类型。所以 --propagation private|shared|slave|unchanged 选项是在调用unshare()方法创建mnt ns的时候改变调用方挂载信息拷贝的subtrees属性为我们指定的类型,就像上面例子演示的一样。
删除mnt namespace演示数据
移除挂载点并删除demo目录及演示文件
关闭shell2
shell1中执行
#-------------------------------shell1-------------------------------
$ umount /mnt/iso1 /mnt/disk1 /mnt/disk2
$ rm -rf /mnt/iso* /mnt/disk*
$ cd / && rm -rf /demo
创建演示目录和文件
$ sudo mkdir /demo && sudo chmod 777 /demo && cd $_ && mkdir disk1 disk2
$ dd if=/dev/zero bs=1M count=32 of=./disk1.img
$ dd if=/dev/zero bs=1M count=32 of=./disk2.img
$ dd if=/dev/zero bs=1M count=32 of=./disk3.img
$ dd if=/dev/zero bs=1M count=32 of=./disk4.img
$ mkfs.ext2 ./disk1.img
$ mkfs.ext2 ./disk2.img
$ mkfs.ext2 ./disk3.img
$ mkfs.ext2 ./disk4.img
$ ls
disk1 disk1.img disk2 disk2.img disk3.img disk4.img
演示
为了方便对比效果,我们将终端拆分成两个窗口,左侧为shell1,右侧为shell2。
- 在shell1中执行挂载操作,分别以shared和private方式挂载disk1和disk2。
#-------------------------------shell1-------------------------------
[root@testerfans demo]# sudo mount --make-shared disk1.img ./disk1
[root@testerfans demo]# sudo mount --make-private disk2.img ./disk2
- 在shell2中执行unshare -m --propagation unchanged。
#-------------------------------shell2-------------------------------
[root@testerfans ~]# unshare -m --propagation unchanged
表示拷贝挂载点信息时也拷贝挂载点的shared subtrees属性,也就是说挂载点A原来是shared,在mnt namespace中也将是shared
- 对比两个挂载命名空间,在两个 shell 中分别执行 readlink /proc/$$/ns/mnt 命令,对比查到的inode number。
如图显示,两个shell的mnt命名空间不同。
- 在两个 shell 执行如下指令,确认–propagation unchanged是否生效。
[root@testerfans demo]# cat /proc/self/mountinfo |grep disk| sed 's/ - .*//'
如图显示,两个shell完成了挂载点信息和subtrees属性的拷贝。
- 在shell2中改变挂载信息,验证shared和private属性作用,分别在disk1和disk2目录下创建两个子目录,并且把disk3和disk4挂载到对应的目录。
#-------------------------------shell2-------------------------------
[root@testerfans demo]# mkdir ./disk1/disk3 ./disk2/disk4
[root@testerfans demo]# mount disk3.img ./disk1/disk3
[root@testerfans demo]# mount disk4.img ./disk2/disk4
- 通过cat /proc/self/mountinfo |grep disk| sed ‘s/ - .*//’ 查看挂载点信息。
如图显示,在shell2中变更了挂载点之后,shell1中的挂载点信息也发生了变化。
- shared类型的 disk1下新增的disk3挂载点信息同步到了shell1的ns。
- private类型的disk2下新增的disk4挂载点信息未同步到shell1的ns。
- 在shell1中改变挂载信息,验证shared和private属性作用。
[root@testerfans demo]# umount ./disk1/disk3
如图显示,在shell1中umount了shared类型disk1/disk3的挂载点之后,shell2的ns挂载信息也发生了变化。
删除mnt namespace演示数据
移除挂载点并删除demo目录及演示文件
shell2执行退出shell2命名空间
#-------------------------------shell2-------------------------------
$ exit
shell1执行
#-------------------------------shell1-------------------------------
[root@testerfans demo]# umount ./disk1
[root@testerfans demo]# umount ./disk2
[root@testerfans demo]# cd / && rm -rf demo/
总结
通过本篇对mnt ns和subtree的试验,我们简单了解了mnt ns的原理和使用方式,直观的理解了什么是Linux的mnt namespace。后续我们将继续探索其他的命名空间的原理。
本文参考:
mount(8)
unshanre(1)
Linux Namespace:Mount
Linux namespace之:mount namespace
命名空间介绍之八:挂载命名空间和共享子树
评论区