众多周知,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 [u@11 images]$ pwd 2 /home/u/test/docker/mydocker/data/images 3 [u@11 images]$ ls 4 00mini 01shell
这里准备了两个目录作为只读层,00mini,是一个空目录。 01shell,包含有若干Linux必备的程序和动态库以及配置文件,是在我们前面的博客中介绍的虚拟系统的基础上添加了若干新的程序和库的结果。
1 [u@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 [u@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 root@firstcontainer /#echo http://www.cnblogs.com/coe2coe/ > test.txt 2 11:59:18 root@firstcontainer /#cat test.txt 3 http://www.cnblogs.com/coe2coe/
我们可以在可读写层rwlayer目录中找到这个文件。
1 [u@11 rwlayer]$ ls 2 test.txt 3 [u@11 rwlayer]$ cat test.txt 4 http://www.cnblogs.com/coe2coe/
(2)修改可读写层的文件。
修改位于可读写层的文件test.txt的内容。
1 11:59:21 root@firstcontainer /#echo hello mydocker! >> test.txt 2 12:05:24 root@firstcontainer /#cat test.txt 3 http://www.cnblogs.com/coe2coe/ 4 hello mydocker!
在可读写层目录中查看这个文件:
1 [u@11 rwlayer]$ cat test.txt 2 http://www.cnblogs.com/coe2coe/ 3 hello mydocker!
在我们的只读层中00mini和01shell中是找不到这个文件的。
1 [u@11 images]$ ls 2 00mini 01shell 3 [u@11 images]$ ls 00mini 4 [u@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 root@firstcontainer /#cat /etc/profile 2 ################################################################## 3 # FileName :profile 4 # Author : coe2coe@qq.com 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 \u@\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 [u@11 rwlayer]$ ls 2 etc test.txt 3 [u@11 rwlayer]$ cat etc/profile 4 ################################################################## 5 # FileName :profile 6 # Author : coe2coe@qq.com 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 \u@\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 [u@11 images]$ cat 01shell/etc/profile 2 ################################################################## 3 # FileName :profile 4 # Author : coe2coe@qq.com 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 \u@\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 root@firstcontainer ~#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 root@firstcontainer ~#rm .bash_history 8 12:20:27 root@firstcontainer ~#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 [u@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 [u@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的背后,当然还有更多的技术秘密需要我们做进一步的探索。