UnionFS

Docker 镜像、容器的基石——联合文件系统(UnionFS)

假设Dockerfile 内容如下

  1. FROM ubuntu:14.04
  2. ADD run.sh /
  3. VOLUME /data
  4. CMD ["./run.sh"]

联合文件系统对应的层次结构如下图所示

aufs实战 - 图1
Docker 文件系统(图片来源于网络).png

  • FROM ubuntu:14.04 :设置基础镜像,此时会使用基础镜像ubuntu:14.04的所* 有镜像层,为简单起见,图中将其作为一个整体展示。
  • ADD run.sh /:将Dockerfile所在目录的文件run.sh加至镜像的根目录,此时新一层的镜像只有一项内容,即根目录下的run.sh.
  • VOLUME /data:设定镜像的VOLUME,此VOLUME在容器内部的路径为/data。需要注意的是,此时并未在新一层的镜像中添加任何文件,但更新了镜像的json文件,以便通过此镜像启动容器时获取这方面的信息。
  • CMD [“./run.sh”]:设置镜像的默认执行入口,此命令同样不会在新建镜像中添加任何文件,仅仅在上一层镜像json文件的基础上更新新建镜像的json文件。

图中的顶上两层,是Docker为Docker容器新建的内容,而这两层属于容器范畴。 这两层分别为Docker容器的初始层(Init Layer)与可读写层(Read-Write Layer)。

  • 初始层: 大多是初始化容器环境时,与容器相关的环境信息,如容器主机名,主机host信息以及域名服务文件等。
  • 读写层: Docker容器内的进程只对可读写层拥有写权限,其他层对进程而言都是只读的(Read-Only)。 另外,关于VOLUME以及容器的hosts、hostname、resolv.conf文件等都会挂载到这里。

实验

AUFS 的英文全称为 Advanced Mult-Layered Unification Filesystem,曾经是 Another Mult-Layered Unification Filesystem,显然,现在的内涵规格更高些。说明:本文的演示环境为 ubuntu 16.04。

创建 AUFS

我们可以通过 mount 命令手动创建 AUFS,接下来我们就用这种方式来演示 AUFS 的主要特征。
创建演示用的目录和文件
先创建一个名称为 aufs 的文件夹用作实验目录:

$ mkdir aufs

然后在 aufs 中创建名称为 mnt 的目录作为文件系统的挂载点:

$ mkdir aufs/mnt

接下来在 aufs 目录下创建 container-layer 文件夹(模拟容器的读写层),并且在文件夹中创建文件 container-layer.txt,文件的内容初始化为 “I am container layer”:

$ mkdir aufs/container-layer $ echo "I am container layer" > aufs/container-layer/container-layer.txt

最后在 aufs 目录下创建三个文件夹 image-layer1、image-layer2、image-layer3(用它们来模拟容器的镜像层)。在这三个文件夹中分别创建文件,并初始化为对应的内容:

$ mkdir aufs/{image-layer1,image-layer2,image-layer3} $ echo "I am image layer 1" > aufs/image-layer1/image-layer1.txt $ echo "I am image layer 2" > aufs/image-layer2/image-layer2.txt $ echo "I am image layer 3" > aufs/image-layer3/image-layer3.txt

准备的目录和文件结构如下图所示:
aufs实战 - 图2
创建 AUFS 文件系统
mount 是一个非常强大的命令,我们可以用它来创建 AUFS 文件系统。下面的命令把 container-layer、image-layer1、image-layer2、image-layer3 以 AUFS 的方式挂载到刚才创建的 mnt 目录下:

$ cd aufs $ sudo mount -t aufs -o dirs=./container-layer:./image-layer1:./image-layer2:./image-layer3 none ./mnt

挂载完成后可以看到 mnt 目录下的文件结构为:
aufs实战 - 图3
注意,在 mount 命令中我们没有指定要挂载的 4 个文件夹的权限信息,其默认行为是:dirs 指定的左边起第一个目录是 read-write 权限,后续目录都是 read-only 权限。我们可以通过下面的方式查看详情:
aufs实战 - 图4
其中的 si_99b90cdd124201eb 目录是系统为 mnt 这个挂载点创建的,从上图中我们可以清楚的看到各个目录的挂载权限。
验证写时复制
下面让我们向 mnt/image-layer2.txt 文件中写入些数据,看看会发生什么变化?

$ echo "I changed mnt/image-layer2.txt" >> mnt/image-layer2.txt

查看 mnt/image-layer2.txt 的内容,发现已经变化了:

I am image layer 2 I changed mnt/image-layer2.txt

再去看看 image-layer2/image-layer2.txt,发现它的内容没有变化,还是:

I am image layer 2

原因在哪里呢?去 container-layer 目录下看看:
aufs实战 - 图5
这里多了一个 image-layer2.txt 文件,其内容如下:
aufs实战 - 图6
原来,当尝试向 mnt/image-layer2.txt 中写入文件时,系统首先在 mnt 目录下查找名为 image-layer2.txt 的文件,将其拷贝到 read-write 层的 container-layer 目录中,接着对 container-layer 目录中的 image-layer2.txt 的文件进行写操作。这个过程也就是 AUFS 的实际工作原理。
至此,我们完成了一个简单的 AUFS demo。可以看出 AUFS 的原理并不是很复杂,但是当与容器技术相结合后,就展示出来超强的魅力,并成为初代 docker 默认的存储驱动。虽然当前 docker 默认的存储驱动已经演进到了 overlay2,但是学习 AUFS 依然可以帮助我们深入理解 docker 中的文件系统。