[docker] 容器镜像

容器镜像

[TOC]

联合文件系统UnionFS

作用是将不同文件系统下的文件mount到一个指定的挂载点上。

UnionFS 有多种实现方法,我这里以aufs(Advance Union File System)为例说明原理,实际中docker client可能会使用别的实现,比如overlay2等。你可以通过docker info查看。

在进行联合的过程中,可以对各个分支规定不同的读写权限。

比如我们这样做:
创建两个目录 fruits 和 vegetables,在里面放上文件,并且创建两个空目录 mergeDir 和 workingDir,形成如下结构:

$ tree
.
├── fruits
│   ├── apple
│   └── tomato
├── mergeDir
├── vegetables
│   ├── carrots
│   └── tomato
└── workingDir

4 directories, 4 files

接下来我们将fruits, vegetable, workingDir 三个目录联合挂载到 mergeDir 目录下,同时规定 workingDir 为可读写目录, fruits 和 vegetables 为只读目录。

sudo mount -t aufs -o br=./workingDir=rw:./fruits=ro:./vegetables=ro none ./mergeDir

查看联合挂载之后的情况:

$ tree .
.
├── fruits
│   ├── apple
│   └── tomato
├── mergeDir
│   ├── apple
│   ├── carrots
│   └── tomato
├── vegetables
│   ├── carrots
│   └── tomato
└── workingDir

4 directories, 7 files

也就是说,我们在mergeDir中可以看到所有分支中的内容。而在联合挂载时fruits目录在vegetables之前,所以这里的tomato将是fruits目录中的tomato。

尝试进行文件修改:

hzq0@ceph0:~/docker-learn/new$ cd mergeDir/
hzq0@ceph0:~/docker-learn/new/mergeDir$ ls
apple  carrots  tomato
hzq0@ceph0:~/docker-learn/new/mergeDir$ echo "mergeDir: tomato" > tomato
hzq0@ceph0:~/docker-learn/new/mergeDir$ cat tomato 
mergeDir: tomato
hzq0@ceph0:~/docker-learn/new/mergeDir$ cat ../fruits/tomato 
hzq0@ceph0:~/docker-learn/new/mergeDir$ cat ../vegetables/tomato 
hzq0@ceph0:~/docker-learn/new/mergeDir$ cat ../workingDir/tomato 
mergeDir: tomato

可以看到由于fruits 和 vegetables 目录是作为只读分支被联合挂载的,所以在mergeDir中对文件的修改只能体现在workingDir中。

现在尝试删除操作:

hzq0@ceph0:~/docker-learn/new$ cd mergeDir/
hzq0@ceph0:~/docker-learn/new/mergeDir$ ls
apple  carrots  tomato
hzq0@ceph0:~/docker-learn/new/mergeDir$ rm carrots
hzq0@ceph0:~/docker-learn/new/mergeDir$ cd ..
hzq0@ceph0:~/docker-learn/new$ tree .
.
├── fruits
│   ├── apple
│   └── tomato
├── mergeDir
│   ├── apple
│   └── tomato
├── vegetables
│   ├── carrots
│   └── tomato
└── workingDir
    └── tomato

4 directories, 7 files

我们在 mergeDir 中删除了文件 carrots,但是由于 carrots 本身的目录 vegetables 是作为只读分支挂载的,所以我们无法在 mergeDir 中真正删除 vegetables 中的 carrots,只能在 mergeDir 的“视图”中删除 carrtos。那么是怎么做到这一点的呢?

hzq0@ceph0:~/docker-learn/new$ cd workingDir/
hzq0@ceph0:~/docker-learn/new/workingDir$ ls
tomato
hzq0@ceph0:~/docker-learn/new/workingDir$ ls -a
.  ..  tomato  .wh.carrots  .wh..wh.aufs  .wh..wh.orph  .wh..wh.plnk

答案在 workingDir 中。workingDir 中新产生了一个隐藏文件.wh.carrots。该文件的作用是白障whiteout掉 carrtos 文件,使得我们并没有在分支中真正删除该文件,但却在挂载点中看不到该文件,产生一种该文件被删除的错觉。

联合文件系统UnionFS还有其他使用方式,我上面展示的这种用法正是docker产生它的容器镜像时的使用方法。

docker 如何利用UnionFS

当执行docker run将一个容器运行起来之后,该容器的容器镜像作为一个只读的rootfs和一个可读可写的workingDir被联合挂载到/var/lib/docker/aufs/mnt下面,而该目录将会作为容器运行之后的根目录。

mount -t aufs -o br=<upperDir>=rw:<lowerDir>=ro+wh none <mnt>

在联合挂载时,容器镜像作为lowerDirworkingDir作为upperDir

在没有对lowerDir进行写操作之前,upperDir里面是空的,而当对只读的lowerDir中的文件进行修改之后,就会触发UnionFScopy-on-write机制,进行一个copyup的操作,将lowerDir中被修改后的文件保存在upperDir中。

而当需要删除只读的lowerDir中的名为foo的文件时,UnionFS则是在可读写的upperDir之中创建一个叫做.wh.foo的文件,该文件的作用是“遮蔽”lowerDir中的foo文件,这样在mntDir之中就看不到被删除的文件了(实际上该文件依然存在)。以上便是docker中增量rootfs的体现。

docker 镜像的分层

只读层

就是容器的运行镜像(rootfs)。逻辑上可以将rootfs理解为一个完整的操作系统的根文件系统。但是实际上该容器镜像本身也是分层的,多个层叠加在一起形成一个完整的rootfs

比如前面例子中的 fruits 和 vegetables 目录。

可读写层

该层在容器运行时被创建,作用是保存增量(增量包括删除、修改和增加)。如前面例子中的 workingDir

特殊的init层

init层是 docker 使用的一个内部层,用来存放/etc/hosts/etc/resolv.conf等内容。需要这样一层的原因是,这些文件本来属于只读的 Ubuntu 镜像的一部分,但是用户往往需要在启动容器时写入一些指定的值比如hostname,所以就需要将这些文件放入可读写层。但是这些属性往往只想规定对当次容器运行的参数,不希望在执行docker commit之后将这些设置写入新创建的容器镜像,所以就将这些文件放入一个可读写的init层,在docker commmit时只会提交可读写层。

posted @ 2019-09-27 21:02  HZQTS  阅读(414)  评论(0编辑  收藏  举报