Union File System
UnionFS
unionfs是一种为Linux,FreeBSD和NetBSD操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务。它使用branch把不同文件系统的文件和目录“透明地”覆盖,形成一个单一一致的文件系统。这些branches或者是read-only或者是read-write的,所以当对这个虚拟后的联合文件系统进行写操作的时候,系统是真正写到了一个新的文件中。看起来这个虚拟后的联合文件系统是可以对任何文件进行操作的,但是其实它并没有改变原来的文件,这是因为unionfs用到了一个重要的资管管理技术叫写时复制。
写时复制(copy-on-write,下文简称CoW),也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。它的思想是,如果一个资源是重复的,但没有任何修改,这时候并不需要立即创建一个新的资源;这个资源可以被新旧实例共享。创建新资源发生在第一次写操作,也就是对资源进行修改的时候。通过这种资源共享的方式,可以显著地减少未修改资源复制带来的消耗,但是也会在进行资源修改的时候增减小部分的开销。
用一个经典的例子来解释一下,Knoppix,一个用于Linux演示、光盘教学和商业产品演示的Linux发行版,就是把一个CD-ROM或者DVD和一个存在在可读写设备(eg,U盘)上的叫knoppix.img的文件系统联合起来。这样任何对CD/DVD上文件的改动都会在被应用在U盘上,不改变原来的CD/DVD上的内容。
AUFS
AUFS,英文全称是Advanced multi-layered unification filesystem, 曾经也叫 Acronym multi-layered unification filesystem,Another multi-layered unification filesystem。AUFS完全重写了早期的UnionFS 1.x,其主要目的是为了可靠性和性能,并且引入了一些新的功能,比如可写分支的负载均衡。AUFS的一些实现已经被纳入UnionFS 2.x版本。
Docker是如何使用AUFS的
AUFS是Docker选用的第一种存储驱动。AUFS具有快速启动容器,高效利用存储和内存的优点,直到现在AUFS仍然是Docker支持的一种存储驱动类型。接下来我们要介绍Docker是如何利用AUFS存储images和containers的。
image layer和AUFS
每一个Docker image都是由一系列的read-only layers组成。image layers的内容都存储在Docker hosts filesystem的/var/lib/docker/aufs/diff目录下。而/var/lib/docker/aufs/layers目录则存储着image layer如何堆栈这些layer的metadata。
准备一台安装了Docker 1.11.2的ECS。在没有拉取任何镜像,启动任何容器的情况下,执行ls /var/lib/docker/aufs/diff命令,发现目录没有存储任何内容。拉取Ubuntu:15.04镜像,然后再次执行ls /var/lib/docker/aufs/diff命令。我们可以看到在docker pull的结果显示ubuntu:15.04镜像一共有4个layers,在执行ls /var/lib/docker/aufs/diff命令的结果中也有四个对应的存储文件目录。这里有一点需要说明的是,自从Docker 1.10之后,diff目录下的存储镜像layer文件夹不再与镜像ID相同。最后cat /var/lib/docker/aufs/layers/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea命令列出来的是堆栈里位于6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea layer下方的layers。
$ docker pull ubuntu:15.0415.04: Pulling from library/ubuntu9502adfba7f1: Pull complete4332ffb06e4b: Pull complete2f937cc07b5f: Pull completea3ed95caeb02: Pull completeDigest: sha256:2fb27e433b3ecccea2a14e794875b086711f5d49953ef173d8a03e8707f1510fStatus: Downloaded newer image for ubuntu:15.04$ ls /var/lib/docker/aufs/diff208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c175736bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77$ ls /var/lib/docker/aufs/mnt208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c175736bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77$ cat /var/lib/docker/aufs/layers/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
接下来我们要以ubuntu:15.04镜像为基础镜像,创建一个名为changed-ubuntu的镜像。这个镜像只是在镜像的/tmp文件夹中添加一个写了“Hello world”的文件。你可以使用下面的Dockerfile来实现:
FROM ubuntu:15.04 RUN echo "Hello world" > /tmp/newfile
- 1
- 2
- 3
在terminal中cd到上面Dockerfile所在位置,执行docker build -t changed-ubuntu .命令来build镜像。
$docker build -t changed-ubuntu .Sending build context to Docker daemon 10.75 kBStep 1 : FROM ubuntu:15.04 ---> d1b55fd07600Step 2 : RUN echo "Hello world" > /tmp/newfile ---> Running in c72100f81dd1 ---> 9d8602c9aee1Removing intermediate container c72100f81dd1Successfully built 9d8602c9aee1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后执行docker images查看现在的镜像,可以看到新生成的changed-ubuntu。
$docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEchanged-ubuntu latest