mount挂载时 no such device_mount namespace 真正隔离了你的mount信息吗?

de323e32ef56bd16b58da2f086ff703b.png

在写所有内容内容之前,我想先说一句:知乎,垃圾


首先给出结论:mount NS的确隔离mount信息,在不同的mount NS中,系统拥有自己独立的VFS目录树及挂载点信息,但是由于shared subtree机制,使用clone系统调用和CLONE_NEWNS参数,可能会得到和你预期不同的结果


在很多docker的入门书籍中,都会讲到namespace的内容,在介绍mount ns时,往往会通过在新创建的mount ns中挂载proc在显示效果:

bea6d2b7faaebf4eb4837ce73b75d750.png

一个简单的go语言示例如上图所示

主机NS(init 进程所在的NS)中/proc的内容如图所示:

e34171b2a9864c138f5253b880c8e54a.png

可以看到有很多的进程,我们在子进程中执行mount操作:

mount -t proc proc /proc
ls /proc

7010065a0220fcb91c65f50676e1fb46.png

会发现,只有两个进程了分别是1号进程,也就是我们fork出来的子进程,以及11号进程,这是我们进行ls操作产生的进程

这是很多资料就告诉我们,啊,你看mount ns隔离起作用了。

屁!

这时我们回到主机系统,会发现很多命令都无法使用了:

7c902633dfffd193f0e0b0b2d9c28a69.png

这其实就是因为主机的proc被修改了,需要在主机重新mount一次才能恢复正常,这也就说明了不同的mount NS之间挂载proc会彼此影响。

进一步验证,我们在子进程中执行--bind操作:

mount --bind . .
mount |grep ext4

092a9e700ed9dfefed69a86dc173e85d.png

会发现mount打印的信息中出现了第二条挂载点,我们在主机系统中也会看到相同的挂载点信息,查看进程所在mount ns(需要确保/proc被正确挂载):

ls -al /proc/self/ns/mnt

可以发现主机系统和子进程的确在不同的NS中,那是因为ns mount无法隔离吗?显然不是的,我们使用unshare 执行相同的操作:

unshare -m -p -f
mount proc proc /proc
mount --bind . .

查看子进程:

664ba82cae51b4b0bc36587abab0ee3a.png

查看主机系统:

829ee0985dd7268ba45ee1431fb89388.png

会发现,果真是隔离状态的,为什么clone系统调用不行呢?是因为shared subtree机制。


shared subtree

引入该机制是为了消除mount ns带来的不便,比如系统新增一块磁盘,我希望所有的NS都感知到新挂载的这块磁盘,那么如果NS 之间是完全隔离的,就需要每个都执行一次挂载操作,这是非常不变的,shared subtree保证了不同的NS 之间可以共享挂载信息

核心机制:

  • peer group

表示了一个或多个挂载点的集合,下面两种情况属于统一group:

  1. 通过--bind操作挂载的源挂载点和目标挂载点(前提是源目录是个挂载点)
  2. 生成新mount ns时,复制过去的挂载点之间同在一个group
  • propagate type(传播属性)

这是mount点的属性,其常见值有:

  1. MS_SHARED 该挂载点的删除操作、该挂载点下子挂载点的新增和删除操作都会同步到同一group中的其他mount点,且其他同group的mount点的操作也会同步到该mount点
  2. MS_PRIVATE 与1相反,不会将自己的信息共享出去,也不接受其他点的共享,从而实现真正的隔离
  3. MS_SLAVE 单向的共享,自己的更新不会影响到他人,但是他人的操作会同步到自己

我们通过命令:

cat /proc/self/mountinfo 

可以看到,系统默认的挂载点都是shared的,clone系统调用会完全copy父进程的挂载点信息,因此子进程的挂载点也是shared,这就导致了我们前面提到的问题。

因此为了实现完全的隔离,我们可以在子进程中执行操作:

mount  --make-rprivate  /

--make-rprivate表示递归修改整个mount树的propagate type为private,这相当于给mount系统调用传递参数:MS_PRIVATE和MS_REC,如果你通过编程语言实现,那么这两个参数是必需的。

到这里我想说的已经说完了,留一个简单的思考题,以证明你看懂了,如果我不设置MS_REC参数,或者说,仅仅执行:

mount  --make-private  /

那么在子进程挂载proc会传播到到系统NS中吗?(会)

在子进程执行--bind会传播到到系统NS中吗?(不会)

如果你不知道为什么,那就去仔细的查一下propagate type参数的含义吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值