17.4 联合文件系统

联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。

联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承。例如,用户基于基础镜像(用来生成其他镜像的基础,往往没有父镜像)来制作各种不同的应用镜像。这些镜像共享同一个基础镜像层,提高了存储效率。此外,当用户改变了一个Docker镜像(比如升级程序到新的版本),则会创建一个新的层(layer)。因此,用户不用替换整个原镜像或者重新建立,只需要添加新层即可。用户分发镜像的时候,也只需要分发被改动的新层内容(增量部分)。这让Docker的镜像管理变得十分轻量级和快速。

1.Docker存储

Docker目前通过插件化方式支持多种文件系统后端。Debian/Ubuntu上成熟的AUFS(Another Union File System,或v2版本往后的Advanced Multilayered Unification File System),就是一种联合文件系统实现,如图17-3所示。AUFS支持为每一个成员目录(类似Git的分支)设定只读(readonly)、读写(readwrite)或写出(whiteout-able)权限,同时AUFS里有一个类似分层的概念,对只读权限的分支可以在逻辑上进行增量地修改(不影响只读部分的)。

17.4 联合文件系统 - 图1

图17-3 AUFS文件系统

Docker镜像自身就是由多个文件层组成,每一层有唯一的编号(层ID)。

可以通过docker history查看一个镜像由哪些层组成。例如查看ubuntu:14.04镜像由4层组成,每层执行了不同的命令:


  1. $ docker history ubuntu:14.04
  2. IMAGE CREATED CREATED BY SIZE COMMENT
  3. 2a274e3405ec 13 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
  4. df697c8b1bf4 13 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.895 kB
  5. 371166fb96e0 13 months ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB
  6. 69191ca023af 13 months ago /bin/sh -c #(nop) ADD file:c8f078961a543cdefa 188.1 MB

对于Docker镜像来说,这些层的内容都是不可修改的、只读的。而当Docker利用镜像启动一个容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将会发生在可读写层。当所操作对象位于较深的某层时,需要先复制到最上层的可读写层。当数据对象较大时,往往意味着IO性能较差。因此,一般推荐将容器修改的数据通过volume方式挂载,而不是直接修改镜像内数据。

此外,对于频繁启停Docker容器的场景下,文件系统的IO性能也将十分关键。

具体看,Docker所有的存储都在Docker目录下,以Ubuntu系统为例,默认路径是/var/lib/docker。

在这个目录下面,存储由Docker镜像和容器运行相关的文件和目录,可能包括aufs、containers、graph、image、init、linkgraph.db、network、repositories-aufs、swarm、tmp、trust、volumes等。其中,最关键的就是aufs目录,这是aufs文件系统所在,保存Docker镜像相关数据和信息。该目录该目录包括layers、diff和mnt三个子目录。1.9版本和之前的版本中,命名跟镜像层的ID是匹配的,而自1.10开始,层数据相关的文件和目录名与层ID不再匹配。

layers子目录包含层属性文件,用来保存各个镜像层的元数据:某镜像的某层下面包括哪些层。例如:某镜像由5层组成,则文件内容应该如下:


  1. # cat aufs/layers/78f4601eee00b1f770b1aecf5b6433635b99caa5c11b8858dd6c8cec03b4584f-init
  2. d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6
  3. 29460ac934423a55802fcad24856827050697b4a9f33550bd93c82762fb6db8f
  4. b670fb0c7ecd3d2c401fbfd1fa4d7a872fbada0a4b8c2516d0be18911c6b25d6
  5. 83e4dde6b9cfddf46b75a07ec8d65ad87a748b98cf27de7d5b3298c1f3455ae4
  6. # cat aufs/layers/d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6
  7. 29460ac934423a55802fcad24856827050697b4a9f33550bd93c82762fb6db8f
  8. b670fb0c7ecd3d2c401fbfd1fa4d7a872fbada0a4b8c2516d0be18911c6b25d6
  9. 83e4dde6b9cfddf46b75a07ec8d65ad87a748b98cf27de7d5b3298c1f3455ae4

diff子目录包含层内容子目录,用来保存所有镜像层的内容数据。例如:


  1. # ls aufs/diff/78f4601eee00b1f770b1aecf5b6433635b99caa5c11b8858dd6c8cec03b4584f-init/
  2. dev etc

mnt子目录下面的子目录是各个容器最终的挂载点,所有相关的AUFS层在这里挂载到一起,形成最终效果。一个运行中容器的根文件系统就挂载在这下面的子目录上。同样,1.10版本之前的Docker中,子目录名和容器ID是一致的。其中,还包括容器的元数据、配置文件和运行日志等。

2.多种文件系统比较

Docker目前支持的联合文件系统种类包括AUFS、OverlayFS、btrfs、vfs、zfs和Device Mapper等。各种文件系统目前的支持情况如下:

·AUFS:最早支持的文件系统,对Debian/Ubuntu支持好,虽然没有合并到Linux内核中,但成熟度很高;

·OverlayFS:类似于AUFS,性能更好一些,已经合并到内核,未来会取代AUFS,但成熟度有待提高;

·Device Mapper:Redhat公司和Docker团队一起开发用于支持RHEL的文件系统,内核支持,性能略慢,成熟度高;

·btrfs:参考zfs等特性设计的文件系统,由Linux社区开发,试图未来取代Device Mapper,成熟度有待提高;

·vfs:基于普通文件系统(ext、nfs等)的中间层抽象,性能差,比较占用空间,成熟度也一般。

·zfs:最初设计为Solaris 10上的写时文件系统,拥有不少好的特性,但对Linux支持还不够成熟。

总结一下,AUFS和Device Mapper的应用最为广泛,支持也相对成熟,推荐生产环境考虑。长期来看,OverlayFS将可能具有更好的特性。