Docker02:Docker核心技术探索 分层联合文件系统

Posted coe2coe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker02:Docker核心技术探索 分层联合文件系统相关的知识,希望对你有一定的参考价值。

众多周知,Docker容器的文件系统可以由Linux的各种分层联合文件系统提供底层支撑,这种分层联合文件系统通常在一些处于低层的只读文件系统之上叠加一个处于高层的可读写文件系统。下面结合Docker的镜像和容器的相关概念,对Docker使用的分层文件系统所一个详细的介绍。

1.基本概念。
(1)镜像(Image)
(a)Docker的一个镜像包含一个或多个层(Layer),每个层都有ID和size。
(b)Docker的镜像是只读的,不能写数据。

(2)容器(Container)
(a)当在镜像上添加一个可读写层(容器层)之后,就形成了一个容器。
(b)在容器中可以只读访问镜像中的内容,同时可以读或写容器层的内容。

(3)AUFS
(a)AUFS是 Another Union File System的缩写,又称advanced multi-layered unification filesystem,是一种文件系统。
(b)AUFS的一个典型用途:将一个可读写文件系统附加到一个只读文件系统上形成一个新的文件系统,从而可以对这个只读文件系统进行修改,而修改的内容实际上保存到了可读写文件系统上。

(4)Overlay2
(a)Overlay2是一种类似于AUFS的分层文件系统,其工作方式与AUFS比较类似,但是性能比AUFS一般情况下更好一些。
(b)本人机器上安装的Docker使用的是Overlay2的文件系统,本博客后续将重点介绍Overlay2的工作方式。
(c)Docker官网对于Overlay2的介绍:
https://docs.docker.com/storage/storagedriver/overlayfs-driver/#performance-best-practices


2.Docker镜像和容器相关命令:

(1)docker create
从一个镜像开始,添加一个可读写层,从而创建处一个容器。

(2)docker commit
将容器的可读写层转换为一个只读层,目的是将容器转换为镜像。

(3)docker build
根据Dockerfile文件的FROM指令,反复的执行run,修改和commit,从而逐步建立一个新的镜像,这个新的镜像可能会包含很多的只读层。

(4)docker save
操作镜像,创建一个镜像的压缩包文件,每个层的元数据信息都被保留了,即仍然是分层的。

(5)docker export
操作容器,创建一个容器的压缩包文件,不包含原始层的元素据信息,最后得到的镜像只包含一个扁平的文件系统,即不再是分层的。

(6)docker history
操作镜像。查看镜像的历史镜像信息。


(7)docker load
从一个tar压缩包加载镜像。

(8)docker import
从docker export导出的tar压缩包导入一个镜像,导入后通过docker images可以看到这个镜像。

(9)docker images
查看本机所有的镜像。


(10)docker pull
从Docker Registry下载一个镜像。

(11)docker push
向Docker Registry上传一个镜像。

(12)docker ps
docker ps 查看本机正在运行的docker容器。
docker ps -a 查看本级所有的docker容器,包括正在运行的和已经停止的。


3.分层叠加文件系统(以Overlay为例)
这种分层联合文件系统具有以下特征:

(1)读取文件时先尝试读取高层的文件,在高层找不到时再依次尝试读取低层或更低层的文件。

(2)在各层都不存在某个文件,需要创建新文件时,直接在可读写层创建新文件。

(3)当需要修改某个可读写层已经存在的文件时,直接诶在刻度写层修改该文件。

(4)当需要修改可读写层不存在,而某个只读层存在的文件时,首次修改时先复制一份该文件的副本到可读写层,然后在可读写层修改,这就是所谓的COW(Copy on write,写时复制)。从第二次修改开始,直接在可读写层完成修改。这就是说,修改成功之后,并不会造成原始的只读层的文件的任何变化。 这种修改造成的变化,在最终的分层联合文件系统中是可见的,比如在使用了这种分层联合文件系统的容器中可以看到这种变化。

这种COW的思想对于AUFS和Overlay来说基本上是类似的,COW在操作系统中的其它地方也有体现,比如fork()时的内存管理。

(5)当需要删除可读写层的文件时,直接删除该文件。

(6)当需要删除值存在于只读层的文件时,将会在可读写层的相应位置建立一个特殊的文件作为删除标记,该文件具有c属性。就是说删除操作成功完成之后,并不会造成只读层的文件的任何变化。同样,在最终的分层联合文件系统中可以看到这种删除动作造成的结果,即使用ls再也找不到这个文件。

这种独特的删除文件的方法,在有些时候,称之为WHITEOUT机制。

 

4.实际安装一个基于Overlay的分层联合文件系统。

(1)准备只读层(低层)。

1 [[email protected]11 images]$ pwd
2 /home/u/test/docker/mydocker/data/images
3 [[email protected]11 images]$ ls 
4 00mini  01shell

这里准备了两个目录作为只读层,00mini,是一个空目录。 01shell,包含有若干Linux必备的程序和动态库以及配置文件,是在我们前面的博客中介绍的虚拟系统的基础上添加了若干新的程序和库的结果。

 

  1 [[email protected]11 images]$ tree -L 3 .
  2 .
  3 ├── 00mini
  4 └── 01shell
  5     ├── bin
  6     │   ├── alias
  7     │   ├── bash
  8     │   ├── cat
  9     │   ├── chattr
 10     │   ├── clear
 11     │   ├── cp
 12     │   ├── file
 13     │   ├── grep
 14     │   ├── hostname
 15     │   ├── id
 16     │   ├── ldd
 17     │   ├── login
 18     │   ├── ls
 19     │   ├── mkdir
 20     │   ├── mount
 21     │   ├── nano
 22     │   ├── nslookup
 23     │   ├── passwd
 24     │   ├── ping
 25     │   ├── ps
 26     │   ├── pwd
 27     │   ├── rm
 28     │   ├── rmdir
 29     │   ├── sh
 30     │   ├── strace
 31     │   ├── su
 32     │   ├── umount
 33     │   ├── uname
 34     │   ├── which
 35     │   ├── whoami
 36     │   └── yum
 37     ├── dev
 38     │   ├── null
 39     │   └── pts
 40     ├── etc
 41     │   ├── fstab
 42     │   ├── group
 43     │   ├── host.conf
 44     │   ├── hosts
 45     │   ├── hosts.allow
 46     │   ├── hosts.deny
 47     │   ├── issue
 48     │   ├── issue.net
 49     │   ├── login.defs
 50     │   ├── lsb-release
 51     │   ├── magic
 52     │   ├── mtab
 53     │   ├── nsswitch.conf
 54     │   ├── os-release
 55     │   ├── pam.d
 56     │   ├── passwd
 57     │   ├── passwd-
 58     │   ├── popt.d
 59     │   ├── profile
 60     │   ├── resolv.conf
 61     │   ├── security
 62     │   ├── shadow
 63     │   ├── sudo.conf
 64     │   ├── sudoers
 65     │   ├── sudoers.d
 66     │   ├── sudo-ldap.conf
 67     │   ├── yum
 68     │   ├── yum.conf
 69     │   └── yum.repos.d
 70     ├── home
 71     │   └── coe2coe
 72     ├── lib
 73     │   └── gcc
 74     ├── lib64
 75     │   ├── ld-linux-x86-64.so.2
 76     │   ├── libacl.so.1
 77     │   ├── libattr.so.1
 78     │   ├── libaudit.so.1
 79     │   ├── libbfd-2.25.1-31.base.el7.so
 80     │   ├── libbind9.so.90
 81     │   ├── libbind9.so.90.0.8
 82     │   ├── libblkid.so.1
 83     │   ├── libbz2.so.1
 84     │   ├── libbz2.so.1.0.6
 85     │   ├── libcap-ng.so.0
 86     │   ├── libcap.so.2
 87     │   ├── libcom_err.so
 88     │   ├── libcom_err.so.2
 89     │   ├── libcom_err.so.2.1
 90     │   ├── libcrack.so.2
 91     │   ├── libcrypt-2.17.so
 92     │   ├── libcrypto.so
 93     │   ├── libcrypto.so.10
 94     │   ├── libcrypto.so.1.0.2k
 95     │   ├── libcryptsetup.so.4
 96     │   ├── libcryptsetup.so.4.7.0
 97     │   ├── libcrypt.so
 98     │   ├── libcrypt.so.1
 99     │   ├── libc.so.6
100     │   ├── libcurl.so
101     │   ├── libcurl.so.4
102     │   ├── libcurl.so.4.3.0
103     │   ├── libdb-5.3.so
104     │   ├── libdb-5.so
105     │   ├── libdb.so
106     │   ├── libdbus-1.so.3
107     │   ├── libdbus-1.so.3.7.4
108     │   ├── libdbus-glib-1.so.2
109     │   ├── libdbus-glib-1.so.2.2.2
110     │   ├── libdl.so.2
111     │   ├── libdns.so.100 -> libdns.so.100.1.1
112     │   ├── libdns.so.100.1.1
113     │   ├── libdw.so.1
114     │   ├── libelf.so.1
115     │   ├── libffi.so.6
116     │   ├── libfreebl3.so
117     │   ├── libfreeblpriv3.so
118     │   ├── libgcc_s.so.1
119     │   ├── libgcrypt.so.11
120     │   ├── libGeoIP.so.1
121     │   ├── libGeoIP.so.1.5.0
122     │   ├── libGeoIPUpdate.so.0
123     │   ├── libGeoIPUpdate.so.0.0.0
124     │   ├── libglib-2.0.so.0
125     │   ├── libglib-2.0.so.0.5000.3
126     │   ├── libgmodule-2.0.so.0
127     │   ├── libgmp.so.10 -> libgmp.so.10.2.0
128     │   ├── libgmp.so.10.2.0
129     │   ├── libgmpxx.so.4 -> libgmpxx.so.4.4.0
130     │   ├── libgmpxx.so.4.4.0
131     │   ├── libgobject-2.0.so.0
132     │   ├── libgpg-error.so.0
133     │   ├── libgpm.so.2 -> libgpm.so.2.1.0
134     │   ├── libgpm.so.2.1.0
135     │   ├── libgssapi_krb5.so
136     │   ├── libgssapi_krb5.so.2
137     │   ├── libgssapi_krb5.so.2.2
138     │   ├── libidn.so.11
139     │   ├── libidn.so.11.6.11
140     │   ├── libip4tc.so.0
141     │   ├── libip4tc.so.0.1.0
142     │   ├── libip6tc.so.0
143     │   ├── libip6tc.so.0.1.0
144     │   ├── libisccc.so.90
145     │   ├── libisccc.so.90.0.4
146     │   ├── libisccfg.so.90
147     │   ├── libisccfg.so.90.0.7
148     │   ├── libisc.so.95
149     │   ├── libisc.so.95.2.1
150     │   ├── libk5crypto.so
151     │   ├── libk5crypto.so.3
152     │   ├── libk5crypto.so.3.1
153     │   ├── libkeyutils.so
154     │   ├── libkeyutils.so.1
155     │   ├── libkeyutils.so.1.5
156     │   ├── libkrb5.so
157     │   ├── libkrb5.so.3
158     │   ├── libkrb5.so.3.3
159     │   ├── libkrb5support.so
160     │   ├── libkrb5support.so.0
161     │   ├── libkrb5support.so.0.1
162     │   ├── liblua-5.1.so
163     │   ├── liblwres.so.90
164     │   ├── liblwres.so.90.0.5
165     │   ├── liblzma.so.5
166     │   ├── liblzma.so.5.2.2
167     │   ├── libmount.so.1
168     │   ├── libmpc.so.3
169     │   ├── libmpc.so.3.0.0
170     │   ├── libmpfr.so.4
171     │   ├── libmpfr.so.4.1.1
172     │   ├── libm.so.6
173     │   ├── libncurses.so.5
174     │   ├── libncursesw.so.5
175     │   ├── libnsl.so.1
176     │   ├── libnspr4.so
177     │   ├── libnss3.so
178     │   ├── libnss_dns-2.17.so
179     │   ├── libnss_dns.so
180     │   ├── libnss_dns.so.2
181     │   ├── libnss_files.so.2
182     │   ├── libnssutil3.so
183     │   ├── libopcodes-2.25.1-31.base.el7.so
184     │   ├── libpam_misc.so.0
185     │   ├── libpam.so.0
186     │   ├── libpcre.so.1
187     │   ├── libplc4.so
188     │   ├── libplds4.so
189     │   ├── libpopt.so.0
190     │   ├── libpopt.so.0.0.0
191     │   ├── libprocps.so.4
192     │   ├── libpthread.so.0
193     │   ├── libpwquality.so.1
194     │   ├── libpython2.7.so
195     │   ├── libpython2.7.so.1.0
196     │   ├── libresolv-2.17.so
197     │   ├── libresolv.so
198     │   ├── libresolv.so.2
199     │   ├── librpmbuild.so.3
200     │   ├── librpmbuild.so.3.2.2
201     │   ├── librpmio.so.3
202     │   ├── librpmio.so.3.2.2
203     │   ├── librpmsign.so.1
204     │   ├── librpmsign.so.1.2.2
205     │   ├── librpm.so.3
206     │   ├── librpm.so.3.2.2
207     │   ├── librt-2.17.so
208     │   ├── librt.so
209     │   ├── librt.so.1
210     │   ├── libselinux.so
211     │   ├── libselinux.so.1
212     │   ├── libsemanage.so.1
213     │   ├── libsepol.so.1
214     │   ├── libsoftokn3.chk
215     │   ├── libsoftokn3.so
216     │   ├── libsqlite3.so.0
217     │   ├── libsqlite3.so.0.8.6
218     │   ├── libssl3.so
219     │   ├── libssl.so
220     │   ├── libssl.so.10
221     │   ├── libssl.so.1.0.2k
222     │   ├── libsystemd.so.0
223     │   ├── libtinfo.so.5
224     │   ├── libuser.so.1
225     │   ├── libustr-1.0.so.1
226     │   ├── libutil.so
227     │   ├── libutil.so.1
228     │   ├── libuuid.so.1
229     │   ├── libxml2.so.2
230     │   ├── libxml2.so.2.9.1
231     │   ├── libxtables.so.10
232     │   ├── libxtables.so.10.0.0
233     │   ├── libz.so.1
234     │   └── python2.7
235     ├── opt
236     │   ├── install
237     │   └── test
238     ├── proc
239     ├── root
240     ├── sbin
241     │   ├── groupadd
242     │   ├── ifconfig
243     │   ├── ip
244     │   ├── pivot_root
245     │   ├── route
246     │   └── useradd
247     ├── tmp
248     ├── usr
249     │   ├── bin
250     │   ├── include
251     │   ├── lib
252     │   ├── lib64
253     │   ├── libexec
254     │   ├── local
255     │   ├── sbin
256     │   └── share
257     └── var
258         ├── cache
259         ├── lib
260         ├── log
261         └── run
262 
263 39 directories, 220 files

上面的列表中,为了节省篇幅,只列举了3层目录结构。

 

(2)准备其它必备的目录。

这里给出一个所需目录结构的模板。

rwlayer目录作为可读写层,初始状态为一个空目录;

tmp目录下的子目录将会作为Overlay分层文件系统的中间层的mount安装目录,;

workdir将会是Overlay分层文件系统安装所需要的workdir目录,设置这个目录是因为需要将00mini和01shell作为一个联合的只读层,安装在layer1目录下, 再将layer1作为新的只读层,将rwlayer作为可读写层,安装在merged目录下,这样需要mount两次overlay文件系统。在同一个进程中,overlay文件系统默认支持2级递归安装,再多一级将会报错,可通过修改overlay源代码的宏定义后重新编译来支持更多层级。

最后merged目录将会是最终的联合文件系统所在目录,也是最终的一个mount安装目录,为各个层的文件提供一个统一的视角。

 

 1  tree .
 2 .
 3 ├── merged
 4 ├── rwlayer
 5 ├── tmp
 6 │   ├── layer1
 7 │   └── layer2
 8 └── workdir
 9     └── work [error opening dir]
10 
11 7 directories, 0 files

 

(3)执行安装工作。

直接通过mount命令完成。以下命令来自我们创建的一个shell脚本文件filesystem.sh, 完整的Shell脚本将在稍后给出。

1 mount -t overlay overlay -o lowerdir=${IMAGE_DIR}01shell:${IMAGE_DIR}00mini  ${CONTAINER_DIR}tmp/layer1
2 mount -t overlay overlay -o lowerdir=${CONTAINER_DIR}tmp/layer1,upperdir=${CONTAINER_DIR}rwlayer,workdir=${CONTAINER_DIR}workdir  ${CONTAINER_DIR}merged

 

我们前面已经知道在安装overlay文件系统之前,merged目录下什么也没有。现在我们来看以下merged目录下有什么。

  1 [[email protected]11 firstcontainer]$ tree -L 3 .
  2 .
  3 ├── merged
  4 │   ├── bin
  5 │   │   ├── alias
  6 │   │   ├── bash
  7 │   │   ├── cat
  8 │   │   ├── chattr
  9 │   │   ├── clear
 10 │   │   ├── cp
 11 │   │   ├── file
 12 │   │   ├── grep
 13 │   │   ├── hostname
 14 │   │   ├── id
 15 │   │   ├── ldd
 16 │   │   ├── login
 17 │   │   ├── ls
 18 │   │   ├── mkdir
 19 │   │   ├── mount
 20 │   │   ├── nano
 21 │   │   ├── nslookup
 22 │   │   ├── passwd
 23 │   │   ├── ping
 24 │   │   ├── ps
 25 │   │   ├── pwd
 26 │   │   ├── rm
 27 │   │   ├── rmdir
 28 │   │   ├── sh
 29 │   │   ├── strace
 30 │   │   ├── su
 31 │   │   ├── umount
 32 │   │   ├── uname
 33 │   │   ├── which
 34 │   │   ├── whoami
 35 │   │   └── yum
 36 │   ├── dev
 37 │   │   ├── null
 38 │   │   └── pts
 39 │   ├── etc
 40 │   │   ├── fstab
 41 │   │   ├── group
 42 │   │   ├── host.conf
 43 │   │   ├── hosts
 44 │   │   ├── hosts.allow
 45 │   │   ├── hosts.deny
 46 │   │   ├── issue
 47 │   │   ├── issue.net
 48 │   │   ├── login.defs
 49 │   │   ├── lsb-release
 50 │   │   ├── magic
 51 │   │   ├── mtab
 52 │   │   ├── nsswitch.conf
 53 │   │   ├── os-release
 54 │   │   ├── pam.d
 55 │   │   ├── passwd
 56 │   │   ├── passwd-
 57 │   │   ├── popt.d
 58 │   │   ├── profile
 59 │   │   ├── resolv.conf
 60 │   │   ├── security
 61 │   │   ├── shadow
 62 │   │   ├── sudo.conf
 63 │   │   ├── sudoers
 64 │   │   ├── sudoers.d
 65 │   │   ├── sudo-ldap.conf
 66 │   │   ├── yum
 67 │   │   ├── yum.conf
 68 │   │   └── yum.repos.d
 69 │   ├── home
 70 │   │   └── coe2coe
 71 │   ├── lib
 72 │   │   └── gcc
 73 │   ├── lib64
 74 │   │   ├── ld-linux-x86-64.so.2
 75 │   │   ├── libacl.so.1
 76 │   │   ├── libattr.so.1
 77 │   │   ├── libaudit.so.1
 78 │   │   ├── libbfd-2.25.1-31.base.el7.so
 79 │   │   ├── libbind9.so.90
 80 │   │   ├── libbind9.so.90.0.8
 81 │   │   ├── libblkid.so.1
 82 │   │   ├── libbz2.so.1
 83 │   │   ├── libbz2.so.1.0.6
 84 │   │   ├── libcap-ng.so.0
 85 │   │   ├── libcap.so.2
 86 │   │   ├── libcom_err.so
 87 │   │   ├── libcom_err.so.2
 88 │   │   ├── libcom_err.so.2.1
 89 │   │   ├── libcrack.so.2
 90 │   │   ├── libcrypt-2.17.so
 91 │   │   ├── libcrypto.so
 92 │   │   ├── libcrypto.so.10
 93 │   │   ├── libcrypto.so.1.0.2k
 94 │   │   ├── libcryptsetup.so.4
 95 │   │   ├── libcryptsetup.so.4.7.0
 96 │   │   ├── libcrypt.so
 97 │   │   ├── libcrypt.so.1
 98 │   │   ├── libc.so.6
 99 │   │   ├── libcurl.so
100 │   │   ├── libcurl.so.4
101 │   │   ├── libcurl.so.4.3.0
102 │   │   ├── libdb-5.3.so
103 │   │   ├── libdb-5.so
104 │   │   ├── libdb.so
105 │   │   ├── libdbus-1.so.3
106 │   │   ├── libdbus-1.so.3.7.4
107 │   │   ├── libdbus-glib-1.so.2
108 │   │   ├── libdbus-glib-1.so.2.2.2
109 │   │   ├── libdl.so.2
110 │   │   ├── libdns.so.100 -> libdns.so.100.1.1
111 │   │   ├── libdns.so.100.1.1
112 │   │   ├── libdw.so.1
113 │   │   ├── libelf.so.1
114 │   │   ├── libffi.so.6
115 │   │   ├── libfreebl3.so
116 │   │   ├── libfreeblpriv3.so
117 │   │   ├── libgcc_s.so.1
118 │   │   ├── libgcrypt.so.11
119 │   │   ├── libGeoIP.so.1
120 │   │   ├── libGeoIP.so.1.5.0
121 │   │   ├── libGeoIPUpdate.so.0
122 │   │   ├── libGeoIPUpdate.so.0.0.0
123 │   │   ├── libglib-2.0.so.0
124 │   │   ├── libglib-2.0.so.0.5000.3
125 │   │   ├── libgmodule-2.0.so.0
126 │   │   ├── libgmp.so.10 -> libgmp.so.10.2.0
127 │   │   ├── libgmp.so.10.2.0
128 │   │   ├── libgmpxx.so.4 -> libgmpxx.so.4.4.0
129 │   │   ├── libgmpxx.so.4.4.0
130 │   │   ├── libgobject-2.0.so.0
131 │   │   ├── libgpg-error.so.0
132 │   │   ├── libgpm.so.2 -> libgpm.so.2.1.0
133 │   │   ├── libgpm.so.2.1.0
134 │   │   ├── libgssapi_krb5.so
135 │   │   ├── libgssapi_krb5.so.2
136 │   │   ├── libgssapi_krb5.so.2.2
137 │   │   ├── libidn.so.11
138 │   │   ├── libidn.so.11.6.11
139 │   │   ├── libip4tc.so.0
140 │   │   ├── libip4tc.so.0.1.0
141 │   │   ├── libip6tc.so.0
142 │   │   ├── libip6tc.so.0.1.0
143 │   │   ├── libisccc.so.90
144 │   │   ├── libisccc.so.90.0.4
145 │   │   ├── libisccfg.so.90
146 │   │   ├── libisccfg.so.90.0.7
147 │   │   ├── libisc.so.95
148 │   │   ├── libisc.so.95.2.1
149 │   │   ├── libk5crypto.so
150 │   │   ├── libk5crypto.so.3
151 │   │   ├── libk5crypto.so.3.1
152 │   │   ├── libkeyutils.so
153 │   │   ├── libkeyutils.so.1
154 │   │   ├── libkeyutils.so.1.5
155 │   │   ├── libkrb5.so
156 │   │   ├── libkrb5.so.3
157 │   │   ├── libkrb5.so.3.3
158 │   │   ├── libkrb5support.so
159 │   │   ├── libkrb5support.so.0
160 │   │   ├── libkrb5support.so.0.1
161 │   │   ├── liblua-5.1.so
162 │   │   ├── liblwres.so.90
163 │   │   ├── liblwres.so.90.0.5
164 │   │   ├── liblzma.so.5
165 │   │   ├── liblzma.so.5.2.2
166 │   │   ├── libmount.so.1
167 │   │   ├── libmpc.so.3
168 │   │   ├── libmpc.so.3.0.0
169 │   │   ├── libmpfr.so.4
170 │   │   ├── libmpfr.so.4.1.1
171 │   │   ├── libm.so.6
172 │   │   ├── libncurses.so.5
173 │   │   ├── libncursesw.so.5
174 │   │   ├── libnsl.so.1
175 │   │   ├── libnspr4.so
176 │   │   ├── libnss3.so
177 │   │   ├── libnss_dns-2.17.so
178 │   │   ├── libnss_dns.so
179 │   │   ├── libnss_dns.so.2
180 │   │   ├── libnss_files.so.2
181 │   │   ├── libnssutil3.so
182 │   │   ├── libopcodes-2.25.1-31.base.el7.so
183 │   │   ├── libpam_misc.so.0
184 │   │   ├── libpam.so.0
185 │   │   ├── libpcre.so.1
186 │   │   ├── libplc4.so
187 │   │   ├── libplds4.so
188 │   │   ├── libpopt.so.0
189 │   │   ├── libpopt.so.0.0.0
190 │   │   ├── libprocps.so.4
191 │   │   ├── libpthread.so.0
192 │   │   ├── libpwquality.so.1
193 │   │   ├── libpython2.7.so
194 │   │   ├── libpython2.7.so.1.0
195 │   │   ├── libresolv-2.17.so
196 │   │   ├── libresolv.so
197 │   │   ├── libresolv.so.2
198 │   │   ├── librpmbuild.so.3
199 │   │   ├── librpmbuild.so.3.2.2
200 │   │   ├── librpmio.so.3
201 │   │   ├── librpmio.so.3.2.2
202 │   │   ├── librpmsign.so.1
203 │   │   ├── librpmsign.so.1.2.2
204 │   │   ├── librpm.so.3
205 │   │   ├── librpm.so.3.2.2
206 │   │   ├── librt-2.17.so
207 │   │   ├── librt.so
208 │   │   ├── librt.so.1
209 │   │   ├── libselinux.so
210 │   │   ├── libselinux.so.1
211 │   │   ├── libsemanage.so.1
212 │   │   ├── libsepol.so.1
213 │   │   ├── libsoftokn3.chk
214 │   │   ├── libsoftokn3.so
215 │   │   ├── libsqlite3.so.0
216 │   │   ├── libsqlite3.so.0.8.6
217 │   │   ├── libssl3.so
218 │   │   ├── libssl.so
219 │   │   ├── libssl.so.10
220 │   │   ├── libssl.so.1.0.2k
221 │   │   ├── libsystemd.so.0
222 │   │   ├── libtinfo.so.5
223 │   │   ├── libuser.so.1
224 │   │   ├── libustr-1.0.so.1
225 │   │   ├── libutil.so
226 │   │   ├── libutil.so.1
227 │   │   ├── libuuid.so.1
228 │   │   ├── libxml2.so.2
229 │   │   ├── libxml2.so.2.9.1
230 │   │   ├── libxtables.so.10
231 │   │   ├── libxtables.so.10.0.0
232 │   │   ├── libz.so.1
233 │   │   └── python2.7
234 │   ├── opt
235 │   │   ├── install
236 │   │   └── test
237 │   ├── proc
238 │   │   ├── 1
239 │   │   ├── acpi
240 │   │   ├── buddyinfo
241 │   │   ├── bus
242 │   │   ├── cgroups
243 │   │   ├── cmdline
244 │   │   ├── consoles
245 │   │   ├── cpuinfo
246 │   │   ├── crypto
247 │   │   ├── devices
248 │   │   ├── diskstats
249 │   │   ├── dma
250 │   │   ├── driver
251 │   │   ├── execdomains
252 │   │   ├── fb
253 │   │   ├── filesystems
254 │   │   ├── fs
255 │   │   ├── interrupts
256 │   │   ├── iomem
257 │   │   ├── ioports
258 │   │   ├── irq
259 │   │   ├── kallsyms
260 │   │   ├── kcore
261 │   │   ├── keys
262 │   │   ├── key-users
263 │   │   ├── kmsg
264 │   │   ├── kpagecount
265 │   │   ├── kpageflags
266 │   │   ├── loadavg
267 │   │   ├── locks
268 │   │   ├── mdstat
269 │   │   ├── meminfo
270 │   │   ├── misc
271 │   │   ├── modules
272 │   │   ├── mounts -> self/mounts
273 │   │   ├── mpt
274 │   │   ├── mtrr
275 │   │   ├── net -> self/net
276 │   │   ├── pagetypeinfo
277 │   │   ├── partitions
278 │   │   ├── sched_debug
279 │   │   ├── schedstat
280 │   │   ├── scsi
281 │   │   ├── self -> [Error\\ reading\\ symbolic\\ link\\ information]
282 │   │   ├── slabinfo
283 │   │   ├── softirqs
284 │   │   ├── stat
285 │   │   ├── swaps
286 │   │   ├── sys
287 │   │   ├── sysrq-trigger
288 │   │   ├── sysvipc
289 │   │   ├── timer_list
290 │   │   ├── timer_stats
291 │   │   ├── tty
292 │   │   ├── uptime
293 │   │   ├── version
294 │   │   ├── vmallocinfo
295 │   │   ├── vmstat
296 │   │   └── zoneinfo
297 │   ├── root
298 │   ├── sbin
299 │   │   ├── groupadd
300 │   │   ├── ifconfig
301 │   │   ├── ip
302 │   │   ├── pivot_root
303 │   │   ├── route
304 │   │   └── useradd
305 │   ├── tmp
306 │   ├── usr
307 │   │   ├── bin
308 │   │   ├── include
309 │   │   ├── lib
310 │   │   ├── lib64
311 │   │   ├── libexec
312 │   │   ├── local
313 │   │   ├── sbin
314 │   │   └── share
315 │   └── var
316 │       ├── cache
317 │       ├── lib
318 │       ├── log
319 │       └── run
320 ├── rwlayer
321 ├── tmp
322 │   ├── layer1
323 │   │   ├── bin
324 │   │   ├── dev
325 │   │   ├── etc
326 │   │   ├── home
327 │   │   ├── lib
328 │   │   ├── lib64
329 │   │   ├── opt
330 │   │   ├── proc
331 │   │   ├── root
332 │   │   ├── sbin
333 │   │   ├── tmp
334 │   │   ├── usr
335 │   │   └── var
336 │   └── layer2
337 └── workdir
338     └── work [error opening dir]
339 
340 68 directories, 268 files

 

可以看到,由于只读层中的00mini和可读写层中的rwlayer目录均没有任何内容,在安装之后,merged目录下的内容和只读层中的01shell目录中的内容完全一致。到目前为止的所有操作都是在宿主系统中完成的。

对于安装之后的分层文件系统的各种文件的增删改查操作在我们的模拟docker容器环境mydocker下进行。

 

5.在overlay分层联合文件系统下的文件的增删改查操作。

本部分描述的内容中,shell提示符包含类似11:58:37这种时间格式的部分,表示在mydocker的容器的虚拟系统中进行的,即在overlay分层联合文件系统的最终统一视图下进行操作,其它表示在宿主系统中进行。

(1)新建立文件。

新创建一个各层都不存在的文件test.txt。

1 11:58:37 [email protected] /#echo  http://www.cnblogs.com/coe2coe/ > test.txt
2 11:59:18 [email protected] /#cat test.txt
3 http://www.cnblogs.com/coe2coe/

我们可以在可读写层rwlayer目录中找到这个文件。

1 [[email protected]11 rwlayer]$ ls
2 test.txt
3 [[email protected]11 rwlayer]$ cat test.txt
4 http://www.cnblogs.com/coe2coe/

(2)修改可读写层的文件。

修改位于可读写层的文件test.txt的内容。

1 11:59:21 [email protected] /#echo hello mydocker! >> test.txt
2 12:05:24 [email protected] /#cat test.txt
3 http://www.cnblogs.com/coe2coe/
4 hello mydocker!

在可读写层目录中查看这个文件:

1 [[email protected]11 rwlayer]$ cat test.txt
2 http://www.cnblogs.com/coe2coe/
3 hello mydocker!

在我们的只读层中00mini和01shell中是找不到这个文件的。

1 [[email protected]11 images]$ ls
2 00mini  01shell
3 [[email protected]11 images]$ ls 00mini
4 [[email protected]11 images]$ ls 01shell
5 bin  dev  etc  home  lib  lib64  opt  proc  root  sbin  tmp  usr  var

 

(3)修改只读层的文件。

我们在容器中修改一下只读层01shell中的etc/profile文件。

 1 12:09:51 [email protected] /#cat /etc/profile
 2 ##################################################################
 3 #   FileName    :profile
 4 #   Author      : [email protected]
 5 #   Created     :2018-04-16
 6 #   Description :http://www.cnblogs.com/coe2coe/
 7 #################################################################
 8 
 9 
10 #!/bin/bash
11 
12 echo -e "Starting my containner ....."
13 
14 export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
15 export TERM=linux
16 
17 
18 export HOSTNAME=$(hostname)
19 export USER=$(whoami)
20 
21 
22 export PS1=\\t \\[email protected]\\h \\w\\$
23 
24 
25 
26 echo -e "welcome to http://www.cnblogs.com/coe2coe/"
27 echo -e "Welcome to my containner!"

只是增加了第26行的内容。

现在来查看以下Overlay有没有使用COW机制复制一份只读层的这个文件到可读写层中来。

先看可读写层:

 1 [[email protected]11 rwlayer]$ ls
 2 etc  test.txt
 3 [[email protected]11 rwlayer]$ cat etc/profile
 4 ##################################################################
 5 #   FileName    :profile
 6 #   Author      : [email protected]
 7 #   Created     :2018-04-16
 8 #   Description :http://www.cnblogs.com/coe2coe/
 9 #################################################################
10 
11 
12 #!/bin/bash
13 
14 echo -e "Starting my containner ....."
15 
16 export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
17 export TERM=linux
18 
19 
20 export HOSTNAME=$(hostname)
21 export USER=$(whoami)
22 
23 
24 export PS1=\\t \\[email protected]\\h \\w\\$
25 
26 
27 
28 echo -e "welcome to http://www.cnblogs.com/coe2coe/"
29 echo -e "Welcome to my containner!"

发现在可读写层rwlayer中,profiel已经复制过来了,内容是修改后的内容。

再看只读层01shell,仍然是修改前的原始内容:

 1 [[email protected]11 images]$ cat 01shell/etc/profile
 2 ##################################################################
 3 #   FileName    :profile
 4 #   Author      : [email protected]
 5 #   Created     :2018-04-16
 6 #   Description :http://www.cnblogs.com/coe2coe/
 7 #################################################################
 8 
 9 
10 #!/bin/bash
11 
12 echo -e "Starting my containner ....."
13 
14 export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
15 export TERM=linux
16 
17 
18 export HOSTNAME=$(hostname)
19 export USER=$(whoami)
20 
21 
22 export PS1=\\t \\[email protected]\\h \\w\\$
23 
24 
25 
26 
27 echo -e "Welcome to my containner!"

 

(3)高层隐藏低层的同名文件。

可读写层位于高层,只读层位于低层,高层文件隐藏低层的同名文件。同名是在联合后的文件系统中,绝对路径完全相同的文件。‘

前面在容器中修改了01shell只读层etc/profile文件之后,在可读写层rwlayer中使用COW机制产生了同名文件etc/profile,这两个文件在联合后的文件系统,即容器内可见的根文件系统中,绝对路径都是/etc/profile,因此是同名的。这样就不难理解修改后,在容器中通过cat看到的/etc/profile的内容,和在宿主系统中看到的可读写层的内容是一致的。

 

(4)删除只读层的文件。

在容器中删除只读层的文件,将会在可读写层生成一个特殊的标记文件,带有c属性。这样在容器内该文件将不可见。我们在容器中删除/root目录下的.bash_profile文件。

 1 12:20:11 [email protected] ~#ls -la
 2 total 20
 3 drwxr-xr-x. 2 root root    47 Apr 18 18:46 .
 4 drwxr-xr-x. 1 root root    33 Apr 19 11:59 ..
 5 -rw-------. 1 root root 12774 Apr 19 00:40 .bash_history
 6 -rw-r--r--. 1 root root   332 Apr 19 00:40 .bash_logout
 7 12:20:12 [email protected] ~#rm .bash_history
 8 12:20:27 [email protected] ~#ls -la
 9 total 4
10 drwxr-xr-x. 1 root root  27 Apr 19 12:20 .
11 drwxr-xr-x. 1 root root  45 Apr 19 11:59 ..
12 -rw-r--r--. 1 root root 332 Apr 19 00:40 .bash_logout

在宿主系统(容器外部)查看只读层,该文件依然存在。

1 [[email protected]11 images]$ ls 01shell/root/ -la
2 总用量 20
3 drwxr-xr-x.  2 root root    47 4月  19 02:46 .
4 drwxr-xr-x. 15 root root   155 4月  19 08:40 ..
5 -rw-------.  1 root root 12774 4月  19 08:40 .bash_history
6 -rw-r--r--.  1 root root   332 4月  19 08:40 .bash_logout

再查看可读写层,”创建“了一个新文件,带有c属性。

1 [[email protected]11 rwlayer]$ ls -la root/
2 总用量 0
3 drwxr-xr-x. 2 root root   27 4月  19 20:20 .
4 drwxr-xr-x. 4 root root   45 4月  19 19:59 ..
5 c---------. 1 root root 0, 0 4月  19 20:20 .bash_history

这种情况下,在可读写层存在该文件,在只读层也存在该文件,但是在统一的联合文件系统的视图下,就是看不到该文件,从而实现了删除只读层文件的功能。

 

 

 

 

 

总结:

(1)使用overlay分层联合文件系统,有效的实现了将宿主系统下的多个目录联合组成容器中的根文件系统目录。

(2)overlay使用的时候发现一个问题,在已经mount成功之后,有时候在宿主系统中的只读层的目录中增加新的文件,在容器中”勉强“可以看到这个新文件,但是文件属性不正确,甚至无法正常读取该文件内容;有时候又可以正常看到文件属性和读取到正确的文件内容。

 

截止今天,我们已经探索了docker所使用的Linux命名空间隔离机制(主要是进程隔离,文件系统隔离,网络环境隔离、UTS主机名隔离),以及overlay分层联合文件系统;而docker的背后,当然还有更多的技术秘密需要我们做进一步的探索。

 

以上是关于Docker02:Docker核心技术探索 分层联合文件系统的主要内容,如果未能解决你的问题,请参考以下文章

Docker02:Docker核心技术探索使用cgroup限制资源的使用

Docker容器技术的核心原理

docker-04-镜像

docker核心技术导图分享

为什么 Google 和 Facebook 不用 Docker

虚拟化容器Docker核心技术实战视频课程