【云原生•容器】容器技术的秘密武器:90%的人不了解的Namespace魔法,必收藏!•下...

【云原生•容器】容器技术的秘密武器:90%的人不了解的Namespace魔法,必收藏!

容器案例实战

Mount Namespace

mount namespace 是用来隔离各个进程看到的挂载点视图,这样进程就只能看到自己的 mount namespace 中的文件系统挂载点。在 mount namespace 中调用mount() 和 unmount() 仅仅只会影响当前 namespace 内的文件系统,而对全局的文件系统是没有影响的。

mount namespace 是 Linux 内核实现的第一个 namespace,从内核的 2.4.19 版本开始加入,因此,它的系统调用参数是NEWNS(New Namespace的缩写),当时开发者还没有意识到,以后还会有很多类型的 namespace 加入进来。

下面继续上篇博文实战案例:

Linux系统下/tmp目录一般就是采用tmpfs文件系统挂载,它是一种基于内存的虚拟文件系统,即它的存储空间都在内存里,它最大的特点就是读写速度快,但是重启系统该目录下文件就会丢失。所以,它一般比较适合做如web缓存、上传下载临时文件等,一般容器里/tmp要重新挂载tmpfs文件系统,避免容器里/tmp可以看到宿主机上各种缓存文件信息

下面我们就来操作如何将容器中/tmp重新挂载tmpfs文件系统,并且由于mount namespace可以隔离进程的挂载点视图,这样就和宿主机之间隔离的更加彻底。

1、切换到上面1号shell,在容器环境里查看/tmp目录,可以看到容器里/tmp目录下可以看到宿主机上应用产生的各种缓存文件:

2faae9bdedafaacce2584a036d254d51.png

2、将容器里/tmp重新挂载tmpfs文件系统:

[root@docker02 ~]# mount -t tmpfs -o size=200m tmpfs /tmp

3、再次查看容器环境下/tmp目录,已和宿主机上/tmp隔离开来:

26307d6f5572c0ddaf5c728afc7035ee.png

4、在宿主机上查看挂载点,在容器里挂载操作并未影响宿主机,说明mount namespace生效:

e0770f67429df38da6edef62bc98f9ed.png


容器根文件系统切换(pivot_root)

目前为止,一个完整的容器基本成型,mount namespace将容器和宿主机的文件系统挂载点资源隔离开来,切换到上面1号shell,查看容器里挂载点:

3e6c9c28d01295fc75dabe56593861da.png


发现这时容器的挂载点和宿主机基本一样,这是为什么呢?

mount namespace虽然隔离进程挂载点资源,但是默认容器会复制父进程的挂载点,只有后续容器里挂载点调整对宿主机来说是不可见的,而且现在容器还有个比较大的问题:根文件系统和宿主机一致,即在容器里看到的文件系统和宿主机上基本一致

这就需要用到 pivot_root 系统调用,它能够将一个 mount namespace 中的所有进程的根目录切换到一个指定路径。常见的使用场景:创建容器时,在创建新的 mount namespace 之后,通过 pivot_root() 将容器内进程的根目录切换到镜像文件对应的 rootfs 所在目录。比如 CentOS 宿主机上拉取一个 Ubuntu 镜像,通过调用 pivot_root 把容器根目录指向这个 Ubuntu 文件系统,那么这个新启动的进程就会傻乎乎的认为自己真的是在 Ubuntu 环境下;同理,如果提供的是一个简单的 busybox 的文件系统,新进程感知的就是一个简单 busybox 环境。

在继续pivot_root之前,我们先来了解下什么是rootfs?

典型的Linux文件系统由bootfs与rootfs两部分组成,bootfs(boot file system)可以看出系统内核部分,用户不会修改这个文件系统,对于同样内核版本的不同的 Linux 发行版的 bootfs 都是一致的。

rootfs(Root Filesystem)是分层文件树的顶端,系统启动时会将 rootfs 挂载到 / 目录,之后再挂载其他的文件系统到其子目录中。rootfs 可以看成一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。rootfs 包含典型的目录结构如 /dev, /proc, /bin, /etc, /lib, /usr, and /tmp 等,这个文件系统在不同的Linux 发行版中是不同的,而且用户可以对这个文件进行修改

pivot_root的语法:

pivot_root new_root put_old

pivot_root将当前进程的根文件系统移动到put_old目录,并使new_root成为新的根文件系统,然后umount掉put_old。这里有几点需要注意:

  • new_root 目录必须是一个挂载点 ,并且new_root目录下有完整rootfs的各种文件

  • new_root 目录挂载点应该是一个与宿主机不同 mount namespace,不然会导致整个宿主机受到影响

  • put_old 文件夹位于 new_root 目录下

下面我们就来通过一个案例:在 CentOS 宿主机上构建一个 Ubuntu 系统环境的容器,实操下如何给容器指定根文件系统:

1、使用docker容器制作rootfs

先在宿主机上某一个命令上准备一个精简的ubuntu精简文件系统,作为后续容器运行时挂载使用的rootfs,随便打开一个宿主机shell执行下面操作即可:

1、拉取镜像:
[root@docker02 ~]# docker pull ubuntu

2、基于centos镜像创建容器:
[root@docker01 ~]# docker run -d ubuntu top -b
3db8a24af88bfee75e0ed9b214118e33712978744fb5d9490748da39f714d048

3、容器文件系统导出tar文件:
[root@docker02 ~]# docker export -o ubuntu.tar 3db

4、将容器导出的tar文件解压到/docker/ubuntu目录下
[root@docker02 ~]# mkdir -p /docker/ubuntu
[root@docker02 ~]# tar -xvf ubuntu.tar -C /docker/ubuntu

2、创建容器:

[root@docker02 ~]# unshare --uts --net --pid --ipc --fork --map-root-user --mount-proc /bin/bash

3、pivot_root切换根文件系统

上面容器环境shell下,在容器环境里切换当前容器根文件系统:

1、创建容器根文件系统路径:
[root@docker02 ~]# mkdir -p /containers/ubuntu-root

2、new-root必须是一个独立的挂载点:
[root@docker02 ~]# mount --bind /containers/ubuntu-root /containers/ubuntu-root

3、创建old-root:
[root@docker02 ~]# mkdir /containers/ubuntu-root/old-root

4、将上述制作的rootfs拷贝到new-root目录下,这样new-root文件夹里面有完整rootfs的各种文件:
[root@docker02 ~]# cp -r /docker/ubuntu/* /containers/ubuntu-root/

5、切换容器根文件系统:
[root@docker02 ~]# pivot_root /containers/ubuntu-root/ /containers/ubuntu-root/old-root

6、重新挂载proc文件系统:
[root@docker01 ~]# mount -t proc proc /proc

注意:

1、执行pivot_root切换根文件系统导致/proc挂载丢失,需要容器里对proc文件系统重新挂载,不然执行mount、ps等指令都会报错。

2、由于mount namespace隔离特性,/containers/ubuntu-root挂载点只在容器里有效,所以宿主机上/containers/ubuntu-root目录为空。

4、验证下容器环境根文件系统是否切换成功

继续在容器下shell执行mount查看容器环境挂载点:

b999c779151e1b3e70e7fef44890b4b9.png


发现容器里挂载点依然和宿主机差不多,但是发现有个变化,很多挂载点位置发生了变化,移入到了/old-root目录下,这正是pivot-root操作将原有挂载点移入到old-root指定的路径下,这些挂载点已没啥作用,使用umount指令卸载挂载点即可,卸载后再次查看容器挂载点已经和宿主机完全不一致:

0946386a719b3932d9dcb843aebac6c4.png

查看下容器环境下系统信息,发现已经变成ubuntu系统,而当前宿主机系统是centos:

9e141891fc11ebb34aa776c7acd3dbcb.png

而且容器里根文件系统(/)和宿主机上隔离开来,容器里根文件系统切换到上面指定的/containers/ubuntu-root目录下:

fa9e84bb883864d6dbd06008f2c2a453.png

总结

通过 mount namespace 和 pivot_root 技术,我们可以将容器根文件系统切换到任意指定路径,从而实现文件系统的彻底隔离,后续分析镜像原理时会发现,这些根文件系统主要基于 unionFS 联合文件系统技术叠加镜像层构建而来。

镜像的底层通常情况是一个操作系统,如Ubuntu、Debian 和CentOS等,用户的镜像通常构建在这些基础镜像层之上,因此,一个镜像就可以看成是一个rootfs,镜像打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用以及它运行所需要的所有依赖,都被封装在了一起。

有了容器镜像"打包操作系统"的能力,这个最基础的依赖环境也终于变成了应用沙盒的一部分。这就赋予了容器所谓的一致性:无论在本地、云端,还是在一台任何地方的机器上,用户只需要解压打包好的容器镜像,那么这个应用运行所需要的完整的执行环境就被重现出来了。这种深入到操作系统级别的运行环境一致性,打通了应用在本地开发和远端执行环境之间难以逾越的鸿沟。

ba75f9d127d84c69abd787a3824337fc.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云原生AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值