一、根文件系统(rootfs)
在之前文章里面有说过,Namespace的作用是隔离,Cgroups的作用是限制,通过这两个的组合使用,进程就真的被装在了一个单独的容器里面,而这些容器就是PaaS项目赖以生存的应用“沙盒”。
思考一下,这个容器有了边界,与宿主机隔离开了,容器里的进程看到的文件系统是怎样的呢?
其实,这种文件系统隔离是通过Linux的命名空间(namespace)技术实现的,特别是通过Mount Namespace,让容器内的进程拥有自己独立的挂载点视图,从而实现文件系统的隔离。
Mount Namespace是Linux操作系统里的第一个Namespace,是基于对chroot的不断改良发明出来的,一般容器的根目录下会挂载一个完整的操作系统的文件系统,比如Debian的文件系统,这样在容器启动后,通过"ls /"查看根目录下的内容,就是Debian的所有目录和文件。
所以,一个最常见的rootfs,或者说容器镜像,会包括 /bin /etc 等文件目录。以下是一个实际的例子,我们查看一个运行中的Jenkins容器(基于Debian系统)的根目录结构:
[root@iZwz99mwn4n76i3rw7l4d1Z ~] docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a6ba5113895e jenkins/jenkins:2.452.2-lts "/usr/bin/tini -- /u…" 8 months ago Up 3 months 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp, 0.0.0.0:8083->8080/tcp, :::8083->8080/tcp myjenkins2
[root@iZwz99mwn4n76i3rw7l4d1Z ~] docker exec -it a6b /bin/bash
root@a6ba5113895e:/# ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
而这个挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)。
需要明确的是,rootfs(根文件系统)只包含操作系统的文件、配置和目录结构,而不包含操作系统内核。这也是为什么所有容器,无论其rootfs基于Ubuntu、Debian还是CentOS,都必须能够在宿主机的内核上运行。当宿主机启动时,加载了特定版本的内核,所有在其上运行的容器都将共享使用这个相同的内核,尽管它们的用户空间文件系统(rootfs)可能完全不同。所以说,rootfs只包括了操作系统的“躯壳”,没有包括操作系统的“灵魂”。
Docker创建容器的基本流程是:
- 获取并准备容器所需的镜像。
- 创建各种Namespace用于隔离(包括PID、Mount、Network、UTS、IPC和User等)。
- 配置Cgroups进行资源限制。
- 在新的Mount Namespace内进行文件系统的挂载(rootfs)。
- 配置网络、存储和其他容器参数。
- 执行容器的启动命令。
需要特别注意的是,启动的容器本质上就是一个进程。在Mount Namespace创建并进行文件系统挂载之前,这个进程能够看到的是宿主机的文件系统。只有通过在Mount Namespace中挂载独立的rootfs后,容器的文件系统才与宿主机完全隔离开来,实现了"让进程认为自己拥有独立的文件系统"这一容器化的核心特性。
二、联合文件系统(Union File System)
2.1 介绍
思考一下,假如我需要同时运行100个基于Debian的容器,每个容器rootfs大小约为1GB。这会占用多少存储空间?更重要的是,如果我要为每个容器安装一个新软件包或修改配置文件,我需要如何处理?
很明显,如何按照rootfs那种情况,那么我需要100GB的存储空间,且我每次更新基础镜像时需要同时更新100个副本。但是如果让所有容器共享一个rootfs,那么就使得各容器间文件无法隔离,一个容器的修改会影响其他所有容器。所以,这些难题就引入了我们的联合文件系统ufs。
Union FS能够将多个目录层(layers)联合挂载到同一个挂载点,呈现为单一的文件系统,同时实现:
- 多容器共享基础层,大幅节省存储空间
- 每个容器拥有自己的可写层,确保隔离性
- 文件修改不影响基础镜像,支持增量存储
ufs最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下。ufs有许多的种类,比如说OverlayFS、AUFS等。目前博主的服务器机器用的是OverlayFS。
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] docker info|grep -i "storage driver"
Storage Driver: overlay2
在 overlay2 文件结构中,联合挂载技术通过联合三个不同的目录来实现:lower目录、upper目录和work目录,这三个目录联合挂载后得到merged目录
- lower目录:只读层,可以有多个,处于最底层目录
- upper目录:读写层,只有一个
- work目录:工作基础目录,挂载后内容被清空,且在使用过程中其内容不可见
- merged目录:联合挂载后得到的视图,其中本身并没有实体文件,实际文件都在upper目录和lower目录中,在merged目录中对文件进行编辑,实际会修改upper目录中文件,而在upper目录与lower目录中修改文件,都会影响我们在merged目录中看到的结果
2.2 案例
比如,有两个目录,C和D,它们分别有三个文件如下:
[root@iZwz99mwn4n76i3rw7l4d1Z test1] tree
.
├── C
│ ├── c1
│ ├── c2
│ └── cd
└── D
├── cd
├── d1
└── d2
其中有一个公共的文件cd,现在我们使用联合挂载命令,将这两个目录挂载到一个公共的目录Y上,其中X是可读可写层,Y是挂载点。具体的对应关系是C和D为lower目录,X为upper目录,workdir为work目录,Y为merged目录。
[root@iZwz99mwn4n76i3rw7l4d1Z test1] sudo mount -t overlay overlay -o lowerdir=./D:./C,upperdir=./X,workdir=./workdir ./Y
[root@iZwz99mwn4n76i3rw7l4d1Z test1] tree
.
├── C
│ ├── c1
│ ├── c2
│ └── cd
├── D
│ ├── cd
│ ├── d1
│ └── d2
├── workdir
│ └── work
├── X
└── Y
├── c1
├── c2
├── cd
├── d1
└── d2
可以看到挂载目录Y有来自C、D目录下的所有文件,其中同名文件(如C和D中都有的文件)只会显示一份(显示优先级更高的文件,我们挂载的命令中D目录在C目录左边所以优先级更高)。 如果在目录Y里对c1、c2、cd、d1、d2文件做修改,这些修改不会影响C、D目录(lowerdir)中的原始文件,而是被保存在X目录(upperdir)中。OverlayFS采用写时复制(Copy-on-Write)机制,所有修改仅保存在可写层X中,C和D作为只读层始终保持不变。
下面我们来做个试验:
①首先将原始目录CD目录下文件写入内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] # 在C目录创建文件
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "原始C1内容" > C/c1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "原始C2内容" > C/c2
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "C目录中的CD文件" > C/cd
[root@iZwz99mwn4n76i3rw7l4d1Z test1]
[root@iZwz99mwn4n76i3rw7l4d1Z test1] # 在D目录创建文件
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "原始D1内容" > D/d1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "原始D2内容" > D/d2
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "D目录中的CD文件" > D/cd
②修改联合挂载目录Y下的文件c1和d1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat C/c1
原始C1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat D/d1
原始D1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] # 修改来自C层的文件
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "修改后的C1内容" > Y/c1
[root@iZwz99mwn4n76i3rw7l4d1Z test1]
[root@iZwz99mwn4n76i3rw7l4d1Z test1] # 修改来自D层的文件
[root@iZwz99mwn4n76i3rw7l4d1Z test1] echo "修改后的D1内容" > Y/d1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat C/c1
原始C1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat D/d1
原始D1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat X/c1
修改后的C1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat X/d1
修改后的D1内容
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat Y/cd
D目录中的CD文件
我们发现原始目录C、D下文件不变,X目下下会新增修改后的c1和d1文件内容。所以这能够说明C、D目录是只读的,X目录时可写的,挂载点Y目录下所有的修改只能改变可读可写层X中的内容。我们再查看Y目录下cd文件的内容,发现他是来自于D目录下,这也就证明了我们前面所说:显示优先级更高的文件,我们挂载的命令中D目录在C目录左边所以优先级更高。
③在Y目录下面,我们删除c2文件
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls X/
c1 d1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls Y/
c1 c2 cd d1 d2
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls C/
c1 c2 cd
[root@iZwz99mwn4n76i3rw7l4d1Z test1] rm -f Y/c2
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls Y/
c1 cd d1 d2
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls C/
c1 c2 cd
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls X/
c1 c2 d1
[root@iZwz99mwn4n76i3rw7l4d1Z test1] ls -l X/c2
c--------- 1 root root 0, 0 May 13 09:40 X/c2
可以发现Y目录(merged层)确实少了c2文件,但是C目录下c2文件没少,且X目录(upperdir层)多了一个c2文件,发现这个c2的文件类型为c。其实这个文件被称为whiteout文件,它的作用就是覆盖lowerdir中的c2来表示该文件被删除的,因为lowerdir中的文件只读,所以只能在upperdir中通过这种“曲线救国”的方式来达到删除只读层文件的效果。
上面的文件系统理论就是docker进行存储的前置基础,介绍了前面的理论,后面docker镜像等就很好理解了。
2.3 Docker镜像存储
2.3.1 镜像存储结构
如下是Docker容器与镜像分层结构示意图:
可以看到上面的多个只读层之上叠加一个可写层,形成容器的视图。这种只读层可写层的构造就是overlay2文件系统的实现。
- 容器层(上面):可写,每个容器独有。
- 镜像层(下面):只读,每个镜像层可被多个容器共享。
使用docker image inspect 镜像id 命令查看镜像的信息,其中关注GraphDriver.Data可以看到前面介绍的ufs文件系统的几个目录LowerDir、MergedDir、UpperDir、WorkDir,以及类型为overlay2。
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] docker image inspect jenkins/jenkins:2.452.2-lts
[
{
...
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/c4203eabe3e6c391270bd5f5aad353c1a0ba6e26e8f5ccd81f3ebe3f1fc3308c/diff:/var/lib/docker/overlay2/69f66d3e5977c215dbfa57dfa23810cac8803170a519e5b52996cd7fa111d449/diff:/var/lib/docker/overlay2/f08456060e6e2d4f5f8af09749eccea52c978e0320f2581c4c7e9415f3c63b02/diff:/var/lib/docker/overlay2/abb60277c4ffbaaff05ae7458bac8079b4ae4cc6c1b9e3b607693202e901a32d/diff:/var/lib/docker/overlay2/65eb90fd28bf3a6e5aa61980964af0ac6eeee2c3378e9809d87be0cf11d3729f/diff:/var/lib/docker/overlay2/d007af46f56265b78c741fd0cbe20b78b3067d655c6a1cb8074794efbbde8f5b/diff:/var/lib/docker/overlay2/a520cbc8d1d903d7f8ae0e0495ceb5960ee48e8914b9b4183432e49d0775b49a/diff:/var/lib/docker/overlay2/3346cbc8e1e4246af1d49a1ae9a4e0c85b9f42338a3a56ecfc2921e74654d136/diff:/var/lib/docker/overlay2/c81a79667ec02e2c222eb934eb4ee445f2b3b3fca759d8aae9c6348d86c5dcd5/diff:/var/lib/docker/overlay2/3509e40ba92490bf5eb8b50baf587f6e9a5987850f0f0921418be476f8d2b019/diff:/var/lib/docker/overlay2/f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1/diff",
"MergedDir": "/var/lib/docker/overlay2/228c5dc3a35f6638c7d31641c8fed26c296eef3c7a93fd9e1e6c4f39e6ce4686/merged",
"UpperDir": "/var/lib/docker/overlay2/228c5dc3a35f6638c7d31641c8fed26c296eef3c7a93fd9e1e6c4f39e6ce4686/diff",
"WorkDir": "/var/lib/docker/overlay2/228c5dc3a35f6638c7d31641c8fed26c296eef3c7a93fd9e1e6c4f39e6ce4686/work"
},
"Name": "overlay2"
},
....
}
]
让我们看看这些/var/lib/docker/overlay2/xxxxxx/
里面都有啥,我们就拿LowerDir举个例子:
- diff目录存放的是当前层的文件,我们发现GraphDriver.Data的LowerDir里面会存在多个路径,用冒号进行分隔,在这些路径中每一个都有一部分文件,把他们联合到一起就得到了完整的 rootfs。比如上上面一层中diff目录下有一个usr目录。
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] ls /var/lib/docker/overlay2/c4203eabe3e6c391270bd5f5aad353c1a0ba6e26e8f5ccd81f3ebe3f1fc3308c/diff
usr
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] ls /var/lib/docker/overlay2/f08456060e6e2d4f5f8af09749eccea52c978e0320f2581c4c7e9415f3c63b02/diff
opt
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] ls /var/lib/docker/overlay2/3509e40ba92490bf5eb8b50baf587f6e9a5987850f0f0921418be476f8d2b019/diff
etc run usr var
#最后一个,也就是最底层
[root@iZwz99mwn4n76i3rw7l4d1Z overlay2] ls /var/lib/docker/overlay2/f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1/diff
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@iZwz99mwn4n76i3rw7l4d1Z /] cd /var/lib/docker/overlay2/c4203eabe3e6c391270bd5f5aad353c1a0ba6e26e8f5ccd81f3ebe3f1fc3308c
[root@iZwz99mwn4n76i3rw7l4d1Z] ls -l
total 16
-rw------- 1 root root 0 Sep 9 2024 committed
drwxr-xr-x 3 root root 4096 Sep 9 2024 diff
-rw-r--r-- 1 root root 26 Sep 9 2024 link
-rw-r--r-- 1 root root 289 Sep 9 2024 lower
drwx------ 2 root root 4096 Sep 9 2024 work
[root@iZwz99mwn4n76i3rw7l4d1Z] ls diff/
usr
[root@iZwz99mwn4n76i3rw7l4d1Z] cat link
3LD5ABUCPBGOK2C4RTAY4LJSEC
[root@iZwz99mwn4n76i3rw7l4d1Z] cat lower
l/NSFDD3YXTTJ4AMHRVCWMR6TJDO:l/AVNP3BSYKWTIB6ENR7Z3376VUI:l/WGLESGCNZ25FLU2645M7DO7B5B:l/OUNX2CBXZA6OHK2ICKA2RYZB7B:l/4VVBUGDYTGAENGVVEXLQNN7JX7:l/HON2RWPHNCXP2LSQW4BUTLEPAK:l/VXUFT26CQX7Z4CXCFCPYLF5OB5:l/5QR3YGPKDWKC6RJ6YDMTUFDKNF:l/L6Z4NONTIAVH5PVEQCRDXTSBXO:l/QRM573YBRQGLMNY7L6CWTYKVHP
#进入到lowerdir的最后一个diff文件目录下,发现只有三个文件
[root@iZwz99mwn4n76i3rw7l4d1Z] cd /var/lib/docker/overlay2/f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1
[root@iZwz99mwn4n76i3rw7l4d1Z] ls
committed diff link
- link内容分别是当前层的软链接名字。如果本层是当前层是底层,则lower文件不存在。真正的软链接文件都存在
/var/lib/docker/overlay2/l
目录下 ,分别指向对应层的diff目录。
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] cd /var/lib/docker/overlay2/l
[root@iZwz99mwn4n76i3rw7l4d1Z l] ll
total 440
lrwxrwxrwx 1 root root 72 Sep 22 2024 2AQQK3NKVDLFLQKZIJU42PMQKX -> ../6e0222bc29c81082eda29610cef936309bad8c2d4f3c82b4681b7080d8e1a1aa/diff
lrwxrwxrwx 1 root root 72 Sep 14 2024 2S2CAHL4EX3K5YZHDSITOYRMAJ -> ../f95626743fd22e3bbb3116d825424debaf6de631e13d116013fb0af14f4e3f13/diff
lrwxrwxrwx 1 root root 72 Sep 21 2024 3C2HHMYSRXXF6E3TNJWJRYMUIO -> ../fb38686ef3951363b9ca0e479bcc350b96718a5395eaab5ae59a9ef331ebd575/diff
lrwxrwxrwx 1 root root 77 Jan 8 19:50 3KPAQJ7LH4G7SEHPW46YKRW6NX -> ../92360f25c8788137f43f25dca94eefcb07d7c7ba9c8311ac3ddaf419d4ff9602-init/diff
lrwxrwxrwx 1 root root 72 Sep 9 2024 3LD5ABUCPBGOK2C4RTAY4LJSEC -> ../c4203eabe3e6c391270bd5f5aad353c1a0ba6e26e8f5ccd81f3ebe3f1fc3308c/diff
lrwxrwxrwx 1 root root 77 Dec 25 23:14 3TY3KCFZKYS4ZWWD2X74IZIFB2 -> ../c215ebc8a956efa888ea9d5dec36fb843f6c2b8329bfd4eb76473906517e50e8-init/diff
.....
-
lower文件中的内容是在此层之下所有软连接的名称,最底层不存在该文件,根据此图可知docker与overlay2对比结构图知道 upper 层在 lower 层之上,而 lower 层中越靠后则越在底层。我们查看 upper 层对应目录下 lower 文件,可以发现其中有11个软链接,而docker inspect里LowerDir下也有11层,最后一层是不存在lower文件的。
#LowerDir最后一层
[root@iZwz99mwn4n76i3rw7l4d1Z f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1] cd /var/lib/docker/overlay2/f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1
[root@iZwz99mwn4n76i3rw7l4d1Z f8b61df3fcff3477b4e4c62daa739c822fa94bb33d558061e1a466ae591d13e1] ll
total 8
-rw------- 1 root root 0 Sep 9 2024 committed
drwxr-xr-x 17 root root 4096 Sep 9 2024 diff
-rw-r--r-- 1 root root 26 Sep 9 2024 link
2.3.2 镜像元数据(Metadata)
镜像元数据存储在了/var/lib/docker/image/<storage_driver>/imagedb/content/sha256
目录下,名称是以镜像ID命名的文件,镜像ID可通过docker images查看,这些文件以json的形式保存了该镜像的rootfs信息、镜像创建时间、构建历史信息、所用容器、包括启动的Entrypoint和CMD等等。
博主用的jenkins的image id为5dea1f4edf69
[root@iZwz99mwn4n76i3rw7l4d1Z test1] docker images -a
jenkins/jenkins 2.452.2-lts 5dea1f4edf69 11 months ago 470MB
#查看元数据
[root@iZwz99mwn4n76i3rw7l4d1Z test1] cat /var/lib/docker/image/overlay2/imagedb/content/sha256/5dea1f4edf69bc10aaecd2c98b5041a865f35c24a8ad89677c7b138f82a74369
{"architecture":"amd64","config":{"User":"jenkins","ExposedPorts":{"50000/tcp":{},"8080/tcp":{}},"Env":["PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","JENKINS_HOME=/var/jenkins_home","JENKINS_SLAVE_AGENT_PORT=50000","REF=/usr/share/jenkins/ref","JENKINS_VERSION=2.452.2","JENKINS_UC=https://updates.jenkins.io","JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental","JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals","COPY_REFERENCE_FILE_LOG=/var/jenkins_home/copy_reference_file.log","JAVA_HOME=/opt/java/openjdk"],"Entrypoint":["/usr/bin/tini","--","/usr/local/bin/jenkins.sh"],"Volumes":{"/var/jenkins_home":{}},"Labels":{"org.opencontainers.image.description":"The Jenkins Continuous Integration and Delivery server","org.opencontainers.image.licenses":"MIT","org.opencontainers.image.revision":"ab63f1f4763a201c6a779f0abc04d6176890b3d2","org.opencontainers.image.source":"https://github.com/jenkinsci/docker","org.opencontainers.image.title":"Official Jenkins Docker image","org.opencontainers.image.url":"https://www.jenkins.io/","org.opencontainers.image.vendor":"Jenkins project","org.opencontainers.image.version":"2.452.2"}},"created":"2024-06-12T15:15:55.323520976Z","history":[{"created":"2024-05-14T01:27:51.0519485Z","created_by":"/bin/sh -c #(nop) ADD file:b9a9fc37b874060c713002ae1ac220f097edd7c6576116c22bb15aad8229b1b3 in / "},{"created":"2024-05-14T01:27:51.549080671Z","created_by":"/bin/sh -c #(nop) CMD [\"bash\"]","empty_layer":true},{"created":"2024-06-12T15:14:02.811921843Z","created_by":"RUN /bin/sh -c apt-get update \u0026\u0026 apt-get install -y --no-install-recommends ca-certificates curl git gnupg gpg libfontconfig1 libfreetype6 procps ssh-client tini unzip tzdata \u0026\u0026 rm -rf /var/lib/apt/lists/* # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:47.938652791Z","created_by":"RUN /bin/sh -c curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh -o /tmp/script.deb.sh \u0026\u0026 bash /tmp/script.deb.sh \u0026\u0026 rm -f /tmp/script.deb.sh \u0026\u0026 apt-get install -y --no-install-recommends git-lfs \u0026\u0026 rm -rf /var/lib/apt/lists/* \u0026\u0026 git lfs install # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ENV LANG=C.UTF-8","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG TARGETARCH","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG COMMIT_SHA","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG user=jenkins","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG group=jenkins","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG uid=1000","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG gid=1000","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG http_port=8080","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG agent_port=50000","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG JENKINS_HOME=/var/jenkins_home","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ARG REF=/usr/share/jenkins/ref","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ENV JENKINS_HOME=/var/jenkins_home","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ENV JENKINS_SLAVE_AGENT_PORT=50000","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"ENV REF=/usr/share/jenkins/ref","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"RUN |10 TARGETARCH=amd64 COMMIT_SHA=ab63f1f4763a201c6a779f0abc04d6176890b3d2 user=jenkins group=jenkins uid=1000 gid=1000 http_port=8080 agent_port=50000 JENKINS_HOME=/var/jenkins_home REF=/usr/share/jenkins/ref /bin/sh -c mkdir -p $JENKINS_HOME \u0026\u0026 chown ${uid}:${gid} $JENKINS_HOME \u0026\u0026 groupadd -g ${gid} ${group} \u0026\u0026 useradd -d \"$JENKINS_HOME\" -u ${uid} -g ${gid} -l -m -s /bin/bash ${user} # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:48.59521192Z","created_by":"VOLUME [/var/jenkins_home]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:49.041200459Z","created_by":"RUN |10 TARGETARCH=amd64 COMMIT_SHA=ab63f1f4763a201c6a779f0abc04d6176890b3d2 user=jenkins group=jenkins uid=1000 gid=1000 http_port=8080 agent_port=50000 JENKINS_HOME=/var/jenkins_home REF=/usr/share/jenkins/ref /bin/sh -c mkdir -p ${REF}/init.groovy.d # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:49.041200459Z","created_by":"ARG JENKINS_VERSION","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:49.041200459Z","created_by":"ENV JENKINS_VERSION=2.452.2","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:49.041200459Z","created_by":"ARG JENKINS_SHA=b4f596923eb37b93c3f5a21a6a32fc3bedd57d04a1b63186811c0ce8b3d9f07c","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:49.041200459Z","created_by":"ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.452.2/jenkins-war-2.452.2.war","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:55.520912262Z","created_by":"RUN |13 TARGETARCH=amd64 COMMIT_SHA=ab63f1f4763a201c6a779f0abc04d6176890b3d2 user=jenkins group=jenkins uid=1000 gid=1000 http_port=8080 agent_port=50000 JENKINS_HOME=/var/jenkins_home REF=/usr/share/jenkins/ref JENKINS_VERSION=2.452.2 JENKINS_SHA=360efc8438db9a4ba20772981d4257cfe6837bf0c3fb8c8e9b2253d8ce6ba339 JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.452.2/jenkins-war-2.452.2.war /bin/sh -c curl -fsSL ${JENKINS_URL} -o /usr/share/jenkins/jenkins.war \u0026\u0026 echo \"${JENKINS_SHA} /usr/share/jenkins/jenkins.war\" \u003e/tmp/jenkins_sha \u0026\u0026 sha256sum -c --strict /tmp/jenkins_sha \u0026\u0026 rm -f /tmp/jenkins_sha # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:55.520912262Z","created_by":"ENV JENKINS_UC=https://updates.jenkins.io","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:55.520912262Z","created_by":"ENV JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:55.520912262Z","created_by":"ENV JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:55.879048102Z","created_by":"RUN |13 TARGETARCH=amd64 COMMIT_SHA=ab63f1f4763a201c6a779f0abc04d6176890b3d2 user=jenkins group=jenkins uid=1000 gid=1000 http_port=8080 agent_port=50000 JENKINS_HOME=/var/jenkins_home REF=/usr/share/jenkins/ref JENKINS_VERSION=2.452.2 JENKINS_SHA=360efc8438db9a4ba20772981d4257cfe6837bf0c3fb8c8e9b2253d8ce6ba339 JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.452.2/jenkins-war-2.452.2.war /bin/sh -c chown -R ${user} \"$JENKINS_HOME\" \"$REF\" # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:55.879048102Z","created_by":"ARG PLUGIN_CLI_VERSION=2.13.0","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:55.879048102Z","created_by":"ARG PLUGIN_CLI_URL=https://github.com/jenkinsci/plugin-installation-manager-tool/releases/download/2.13.0/jenkins-plugin-manager-2.13.0.jar","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"RUN |15 TARGETARCH=amd64 COMMIT_SHA=ab63f1f4763a201c6a779f0abc04d6176890b3d2 user=jenkins group=jenkins uid=1000 gid=1000 http_port=8080 agent_port=50000 JENKINS_HOME=/var/jenkins_home REF=/usr/share/jenkins/ref JENKINS_VERSION=2.452.2 JENKINS_SHA=360efc8438db9a4ba20772981d4257cfe6837bf0c3fb8c8e9b2253d8ce6ba339 JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.452.2/jenkins-war-2.452.2.war PLUGIN_CLI_VERSION=2.13.0 PLUGIN_CLI_URL=https://github.com/jenkinsci/plugin-installation-manager-tool/releases/download/2.13.0/jenkins-plugin-manager-2.13.0.jar /bin/sh -c curl -fsSL ${PLUGIN_CLI_URL} -o /opt/jenkins-plugin-manager.jar \u0026\u0026 echo \"$(curl -fsSL \"${PLUGIN_CLI_URL}.sha256\") /opt/jenkins-plugin-manager.jar\" \u003e/tmp/jenkins_sha \u0026\u0026 sha256sum -c --strict /tmp/jenkins_sha \u0026\u0026 rm -f /tmp/jenkins_sha # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"EXPOSE map[8080/tcp:{}]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"EXPOSE map[50000/tcp:{}]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"ENV COPY_REFERENCE_FILE_LOG=/var/jenkins_home/copy_reference_file.log","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"ENV JAVA_HOME=/opt/java/openjdk","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:14:57.837202636Z","created_by":"ENV PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:15:55.225966919Z","created_by":"COPY /javaruntime /opt/java/openjdk # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:15:55.225966919Z","created_by":"USER jenkins","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:15:55.278347679Z","created_by":"COPY jenkins-support /usr/local/bin/jenkins-support # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:15:55.301098315Z","created_by":"COPY jenkins.sh /usr/local/bin/jenkins.sh # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:15:55.323520976Z","created_by":"COPY jenkins-plugin-cli.sh /bin/jenkins-plugin-cli # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2024-06-12T15:15:55.323520976Z","created_by":"ENTRYPOINT [\"/usr/bin/tini\" \"--\" \"/usr/local/bin/jenkins.sh\"]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2024-06-12T15:15:55.323520976Z","created_by":"LABEL org.opencontainers.image.vendor=Jenkins project org.opencontainers.image.title=Official Jenkins Docker image org.opencontainers.image.description=The Jenkins Continuous Integration and Delivery server org.opencontainers.image.version=2.452.2 org.opencontainers.image.url=https://www.jenkins.io/ org.opencontainers.image.source=https://github.com/jenkinsci/docker org.opencontainers.image.revision=ab63f1f4763a201c6a779f0abc04d6176890b3d2 org.opencontainers.image.licenses=MIT","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77","sha256:5e0d98401a6b2e688b57e6cfe2504474f7c11bf67de77ea2dabc7dd0be6b7979","sha256:073021bf7776f2215d1f5805a26c5794d53b60203f734fca1109881b8375f51c","sha256:91d507b1bb6c9858df6ca165f74041be821a3f33ee547e2e7d32398b6954e8fb","sha256:c5f84ccebefc33f41a6616734c76a97176a46b3f040e7e1ac2044afa4db10181","sha256:5eb0ce0e40e4cf6b46489706246f56c784fe2e082fc87a2852fa7442b00b7abb","sha256:ee5737dda42f1dbcbc74328b755148357e06ae026051015560be253e114720f4","sha256:e988f77e842b44d71ea71e49cb04c3a994eaa3bde23f068dd70a7ec6f19644ea","sha256:114fb9cd6aee830aa686044fb93e919f90f5bbe790ec874a9f7c8694f42ee091","sha256:06cf2b7469ca7483774db40d3a6afb0662d970b65378c02f7d3c16b4675bb0bc","sha256:5fa4e56556307f5a2d45e85b37fc5b00903f61a9b9be6af7c501cd02f68c2665","sha256:5c564fae7856b17515a540f59138e068f4a67019e01c7a92c82701c1ff81d124"]}}
我们重点看一下镜像中的RootFS.Layers下的sha256都是指的什么
[root@iZwz99mwn4n76i3rw7l4d1Z test1] docker image inspect jenkins/jenkins:2.452.2-lts
[
{
...
"RootFS": {
"Type": "layers",
"Layers": [
#最底层
"sha256:bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77",
"sha256:5e0d98401a6b2e688b57e6cfe2504474f7c11bf67de77ea2dabc7dd0be6b7979",
"sha256:073021bf7776f2215d1f5805a26c5794d53b60203f734fca1109881b8375f51c",
"sha256:91d507b1bb6c9858df6ca165f74041be821a3f33ee547e2e7d32398b6954e8fb",
"sha256:c5f84ccebefc33f41a6616734c76a97176a46b3f040e7e1ac2044afa4db10181",
"sha256:5eb0ce0e40e4cf6b46489706246f56c784fe2e082fc87a2852fa7442b00b7abb",
"sha256:ee5737dda42f1dbcbc74328b755148357e06ae026051015560be253e114720f4",
"sha256:e988f77e842b44d71ea71e49cb04c3a994eaa3bde23f068dd70a7ec6f19644ea",
"sha256:114fb9cd6aee830aa686044fb93e919f90f5bbe790ec874a9f7c8694f42ee091",
"sha256:06cf2b7469ca7483774db40d3a6afb0662d970b65378c02f7d3c16b4675bb0bc",
"sha256:5fa4e56556307f5a2d45e85b37fc5b00903f61a9b9be6af7c501cd02f68c2665",
#最顶层
"sha256:5c564fae7856b17515a540f59138e068f4a67019e01c7a92c82701c1ff81d124"
]
},
....
}
]
其实上面的每个sha256:
后面的哈希值称为diff_id,其排列也是有顺序的,从上到下依次表示镜像层的最低层到最顶层。前面我们讲到jenkins的每层文件(diff、link、lower等)都存储在/var/lib/docker/overlay2/<cache_id>
目录下,diff_id如何关联到cache_id呢?
首先我们要知道docker有三个重要的id分别为:diff_id、cache_id、chain_id
-
cache_id: 可以在
/var/lib/docker/overlay2
中查看(此路径下的文件名就是cache_id),也可以通过docker imgae inpect 查看GraphDriver.Data中的dir ID,其本质是宿主机随机生成的uuid。 -
diff_id:通过docker image inpect查看RootFS.Layers下的sha256的值。
-
chain_id: 可以在
/var/lib/docker/image/overlay2/layerdb/sha256
查看。是通过 diff_id 计算出来的,是 Docker 内容寻址机制采用的索引 ID。如果当前镜像层为最底层,则其 chain_id 与 diff_id 相同,如果当前镜像层不是最底层,则其 chain_id 计算方式为:sha256(上层chain_id + " " + 本层diff_id)。
这三个 id 之间存在一一对应的关系,我们可以通过 diff_id 计算得到 chain_id,又可以通过 chain_id 找到对应的 cache_id
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] ls /var/lib/docker/image/overlay2/layerdb/sha256/
02136e5f913821944993a98a873c0ba865ecc5f4421250f8210ea6ba66a39b57 52bcfd64a2223769f6b0f7265c2bc6e394f048c5ce883cac4f763efc3bb68ffa b9fee2a930f8b7ee20b610dbb8171fb5e210857f55e712741563888a7fc5d3ea
0516b2d6a10a886d57abbbf14d5b3344c0a709bfa14974ccebcd30ccd2302e05 535cf16283174e3c8b8af00bb3e1ca2ce71f6ba54baff4fa39cfd7c124dc3a94 bb500bf390e656ab7b8ea8b7415591d4585aea94d01bf219747183e2359796c4
07fa25c1cdf29d33730a21e2f655f45cbe565e2029eda8c9b990eac455b3332f
5480e01742bd7a74cb6e0db2ff7a967828a241c31c41b0715d71daa031a955ee
#恰好这个文件就对应了上面jenkins的RootFS下面的最底层
bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77
09a8df00c08c0de1e52bd5ee9b4a4ce522725adcbf0c5e3b34c5e64cd164baf9 592054921010ee675ddc8bd92f99dcef9c92169ed3c963bc0f0046bcb2cca8e9 bdc97e8befab7e71fe2c9effc269de7ccdbfea1518c0a4ea9bb64e40af1bb488
.....
有了最上一层的 chain_id 我们就可以计算出下一层的 chain_id,至于具体如何计算,以及如何通过 chain_id 找到对应的 cache_id,我们需要先了解 layerdb 目录下的内容
layerdb
我们现在已知 Docker 的镜像层作为只读层,容器层作为读写层,而 Docker 实际上定义了 roLayer 接口与 mountLayer 接口,分别用来描述(只读)镜像层与(读写)容器层,这两个接口的元数据就在目录 docker/image/overlay2/layerdb
下。
roLayer(镜像层)
rolayer 接口用来描述镜像层(即分层结构示意图的只读层),元数据的具体目录在 layerdb/sha256/
下,在此目录下每个文件夹都以每个镜像层的 chain_id 命名。随机进入一个不是最底层的目录下,发现他有五个文件。而进入到刚刚jenkins的最底层下,少了一个parent文件。
#非最底层
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] ls -l bb500bf390e656ab7b8ea8b7415591d4585aea94d01bf219747183e2359796c4/
total 92
-rw-r--r-- 1 root root 64 Jan 8 19:26 cache-id
-rw-r--r-- 1 root root 71 Jan 8 19:26 diff
-rw-r--r-- 1 root root 71 Jan 8 19:26 parent
-rw-r--r-- 1 root root 8 Jan 8 19:26 size
-rw-r--r-- 1 root root 76703 Jan 8 19:26 tar-split.json.gz
#最底层
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] ls -l bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77/
total 384
-rw-r--r-- 1 root root 64 Sep 9 2024 cache-id
-rw-r--r-- 1 root root 71 Sep 9 2024 diff
-rw-r--r-- 1 root root 9 Sep 9 2024 size
-rw-r--r-- 1 root root 376835 Sep 9 2024 tar-split.json.gz
- cache-id:当前 chain_id 对应的 cache_id,用来索引镜像层
- diff:当前 chain_id 对应的 diff_id
- parent:当前 chain_id 对应的镜像层的下一层(父层) chain_id,最底层不存在该文件
- size:当前 chain_id 对应的镜像层物理大小,单位是字节
- tar-split.json.gz:当前 chain_id 对应镜像层压缩包的 split 文件,可以用来还原镜像层的 tar 包,通过
docker save
命令导出镜像时会用到
diff_id计算得出chain_id如下:
那么我们再通过RootFS.Layers查看最底层的下一层,diff_id 为5e0d98401a6b2e688b57e6cfe2504474f7c11bf67de77ea2dabc7dd0be6b7979
,根据公式sha256(上层chain_id(parent文件内容) + " " + 本层diff_id)通过计算 sha256,我们可以得出下一层的 chain_id:
发现下层的chain_id为cd35d1e0163501a3c21d8038bd37d3ff772bcd60b2ed40e809860b543be5ef7a
查看/var/lib/docker/image/overlay2/layerdb/sha256目录,确实存在此chain_id的目录
查看此目录下的parent文件内容, 发现此值就是jenkins的RootFS.Layers中最底层的值。:
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] cat cd35d1e0163501a3c21d8038bd37d3ff772bcd60b2ed40e809860b543be5ef7a/parent
sha256:bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77[
mountLayer(容器层)
mountLayer 接口用来描述容器层(即分层结构示意图的可读写层),元数据的具体目录在/var/lib/docker/image/overlay2/layerdb/mounts,在此目录下的文件夹以每个容器的容器ID(CONTAINER ID)命名
[root@iZwz99mwn4n76i3rw7l4d1Z mounts] ll a6ba5113895e628329b3ea4578ce6b6a2a76e0b4eeb9d87ecae4301e8619a5c6/
total 12
-rw-r--r-- 1 root root 69 Sep 10 2024 init-id
-rw-r--r-- 1 root root 64 Sep 10 2024 mount-id
-rw-r--r-- 1 root root 71 Sep 10 2024 parent
[root@iZwz99mwn4n76i3rw7l4d1Z a6ba5113895e628329b3ea4578ce6b6a2a76e0b4eeb9d87ecae4301e8619a5c6] cat parent
sha256:3d6ca337537ebb36a253c6d3b814baebe1ea9ccf890ff1ac13e2615b77373718
在此容器目录下,存在三个文件:
-
init-id:对应容器 init 层目录名,源文件在
/var/lib/docker/overlay2
目录下 -
mount-id:容器层存储在
/var/lib/docker/overlay2
目录下的名称 -
parent:容器的镜像层最顶层镜像的 chain_id
为了验证parent的值,我们进入/var/lib/docker/image/overlay2/layerdb/的此目录下,打印diff文件的值,发现这个diff文件的值是此jenkins中RootFS.Layers的最顶层,如下:
[root@iZwz99mwn4n76i3rw7l4d1Z 3d6ca337537ebb36a253c6d3b814baebe1ea9ccf890ff1ac13e2615b77373718] ll
total 20
-rw-r--r-- 1 root root 64 Sep 9 2024 cache-id
-rw-r--r-- 1 root root 71 Sep 9 2024 diff
-rw-r--r-- 1 root root 71 Sep 9 2024 parent
-rw-r--r-- 1 root root 3 Sep 9 2024 size
-rw-r--r-- 1 root root 342 Sep 9 2024 tar-split.json.gz
[root@iZwz99mwn4n76i3rw7l4d1Z 3d6ca337537ebb36a253c6d3b814baebe1ea9ccf890ff1ac13e2615b77373718] cat diff
sha256:5c564fae7856b17515a540f59138e068f4a67019e01c7a92c82701c1ff81d124
[root@iZwz99mwn4n76i3rw7l4d1Z sha256] docker image inspect jenkins/jenkins:2.452.2-lts [root@iZwz99mwn4n76i3rw7l4d1Z sha256] docker image inspect jenkins/jenkins:2.452.2-lts |grep -i "rootfs" -A 15
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:bbe1a212f7e9f1baaef62491a51254f3adda514c22632ea719f62713fad80f77",
"sha256:5e0d98401a6b2e688b57e6cfe2504474f7c11bf67de77ea2dabc7dd0be6b7979",
"sha256:073021bf7776f2215d1f5805a26c5794d53b60203f734fca1109881b8375f51c",
"sha256:91d507b1bb6c9858df6ca165f74041be821a3f33ee547e2e7d32398b6954e8fb",
"sha256:c5f84ccebefc33f41a6616734c76a97176a46b3f040e7e1ac2044afa4db10181",
"sha256:5eb0ce0e40e4cf6b46489706246f56c784fe2e082fc87a2852fa7442b00b7abb",
"sha256:ee5737dda42f1dbcbc74328b755148357e06ae026051015560be253e114720f4",
"sha256:e988f77e842b44d71ea71e49cb04c3a994eaa3bde23f068dd70a7ec6f19644ea",
"sha256:114fb9cd6aee830aa686044fb93e919f90f5bbe790ec874a9f7c8694f42ee091",
"sha256:06cf2b7469ca7483774db40d3a6afb0662d970b65378c02f7d3c16b4675bb0bc",
"sha256:5fa4e56556307f5a2d45e85b37fc5b00903f61a9b9be6af7c501cd02f68c2665",
"sha256:5c564fae7856b17515a540f59138e068f4a67019e01c7a92c82701c1ff81d124"
]
Init层
一个完整的容器分为3层:镜像层、init层和容器层,镜像层提供完整的文件系统基础(rootfs),容器层提供给用户进行交互操作与读写权限,而 init 层则是对应每个容器自己的一些系统配置文件,我们可以看一下 init 层的内容
[root@iZwz99mwn4n76i3rw7l4d1Z /] cd /var/lib/docker/overlay2/6e0222bc29c81082eda29610cef936309bad8c2d4f3c82b4681b7080d8e1a1aa-init
[root@iZwz99mwn4n76i3rw7l4d1Z 6e0222bc29c81082eda29610cef936309bad8c2d4f3c82b4681b7080d8e1a1aa-init] tree
.
├── committed
├── diff
│ ├── dev
│ │ ├── console
│ │ ├── pts
│ │ └── shm
│ ├── etc
│ │ ├── hostname
│ │ ├── hosts
│ │ ├── mtab -> /proc/mounts
│ │ └── resolv.conf
│ ├── proc
│ └── sys
├── link
├── lower
└── work
└── work
9 directories, 8 files
可以看到在 diff 目录中有一些 /etc/hosts、/etc/resolv.conf 等配置文件,需要这一层的原因是当容器启动的时候,有一些每个容器特定的配置文件(例如 hostname),但由于镜像层是只读层无法进行修改,所以就在镜像层之上单独挂载一层 init 层,用户通过修改每个容器对应 init 层中的一些配置文件从而达到修改镜像配置文件的目的,而在 init 层中的配置文件也仅对当前容器生效,通过 docker commit
命令创建镜像时也不会提交 init 层
https://zhuanlan.zhihu.com/p/374924046https://zhuanlan.zhihu.com/p/374924046
三、总结
文章介绍了根文件系统rootfs和联合文件系统ufs,其中着重介绍了overlayFS的工作原理,浅浅的解析了一下docker的文件系统实现,有不足之处请批评指正。