云存储硬核技术内幕——(26) 面壁十年图破壁

在上期,我们提到,docker容器本身是不支持持久化存储的,对容器文件系统做的任何修改都会在容器销毁后永久丢失。

这是为什么呢?

这就需要从容器的隔离机制讲起。

在前面的实验中,我们利用docker拉起了一个busybox的容器,让我们接着做个实验:

首先,在hostos上用ps -A命令查看进程:

然后,我们在busybox容器上执行以下命令:

ping cloud.tencent.com

我们回到hostos上,再用ps -A命令查看当前进程:

我们发现,多了一个ping进程!

从第二列看出,这个进程是在pts/0终端上执行的,也就是刚才执行busybox的bash窗口。pts/0比起pts/1,还多了一个podman进程。podman在CentOS8中,可以理解为docker的替身 (感兴趣的同学可以自行了解podman的主页:https://podman.io )

原来,我们在busybox中执行的进程ping,是由docker/podman启动的。用pstree命令查看进程树,可以看到:

但是,我们也发现,容器中的命令只能在容器中有效,如busybox中运行的ping命令,其发起的IP地址是容器busybox本身的IP。这个IP地址和HostOS上的IP地址是不同的。具体请看专题《容器网络硬核技术内幕》系列。

我们整理一下看到的事实:

  1. 容器中跑的进程,在HostOS上也可以看到;
  2. 容器中可以看到一个完整的文件系统;
  3. 容器对文件系统本身做的修改无法持久化;

这是为什么呢?

让我们复习一下容器的基础概念:

容器通过三大隔离技术实现:namespace, cgroups, rootfs.

namespace做隔离,让进程只能看到自己所在的namespace中的世界,如busybox这个容器中的进程,就只能看到同一个容器的进程——因为Linux HostOS上的其他进程的namespace和它不一样;

cgroups做限制,让这个世界被无形的墙包围;

rootfs虚拟出一个虚拟化的文件系统;

正是因为rootfs这个虚拟化的文件系统存在,docker或其他容器运行时引擎中运行的容器才可以如同在“沙盒”中运行一样,对文件系统的任何修改,都不会影响到HostOS的世界。

rootfs会包含一个操作系统所需要的文件,如/bin, /etc, /proc 等目录。

我们知道,一个docker镜像的体积是很小的,往往以MB计,而linux操作系统安装完成后占用的空间却是以GB计的。

原来,docker镜像的rootfs实际上并不会把操作系统整个文件打包,而是一个分层的设计,也就是说,rootfs只包括docker镜像相对于hostos的增量。

让我们做一个实验,在CentOS中拉取并运行一个ubuntu镜像:

通过命令 docker image inspect ubuntu:latest ,可以发现这些有用的信息……

我们可以发现,这个ubuntu镜像实际上由三个层组成,也就是有3个增量rootfs。

我们再来到podman工作目录,这个地方实际上是容器存储的挂载点:

~/.local/share/containers/storage/overlay

执行ls命令:

我们发现,除了最后一个目录以外,其他目录都没有在ubuntu的rootfs层中出现过。

我们进去看看:

原来,这个目录里面有个diff目录,里边藏了容器镜像里面所有的东西。

再仔细看看:/bin/目录下面的部分文件:

我们去容器ubuntu的命令行下看看这些文件:

我们注意到,同样文件名的文件,大小是一样的。原来,这个地方就是rootfs的只读层,所有与操作系统hostos有差异的文件,实际上都在只读层里面打包集成,并挂载到hostos的挂载目录。

这里面另外几个目录则属于可读写层。我们对ubuntu容器文件系统做的修改将在这里体现,容器销毁后,它们也被销毁。

能不能通过在容器中创建一个挂载点,通过执行mount命令挂载外部持久化卷,然后实现数据的持久化存储呢?

很遗憾,由于前文所述的无形的墙的限制,实际上是做不到的。

怎么样能够突破无形的墙的限制,“面壁十年图破壁”,让容器挂载外部持久化卷呢?

请看下回分解。