【Docker】容器文件系统

一、根文件系统(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创建容器的基本流程是:

  1. 获取并准备容器所需的镜像。
  2. 创建各种Namespace用于隔离(包括PID、Mount、Network、UTS、IPC和User等)。
  3. 配置Cgroups进行资源限制。
  4. 在新的Mount Namespace内进行文件系统的挂载(rootfs)。
  5. 配置网络、存储和其他容器参数。
  6. 执行容器的启动命令。

        需要特别注意的是,启动的容器本质上就是一个进程。在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 层 

     具体的关联及更多细节可以参考这两篇文章:
    浅析 Docker overlay2 文件结构-腾讯云开发者社区-腾讯云在讲 overlay2 之前,我们需要先简单了解下什么是 rootfs:rootfs 也叫 根文件系统,是 Linux 使用的最基本的文件系统,是内核启动时挂载的第一个文件系统,提供了根目录 /,根文件系统包含了系统启动时所必须的目录和关键性文件,以及使其他文件系统得以挂载所必要的文件。在根目录下有根文件系统的各个目...https://cloud.tencent.com/developer/article/2272892

    https://zhuanlan.zhihu.com/p/374924046https://zhuanlan.zhihu.com/p/374924046 

       三、总结

              文章介绍了根文件系统rootfs和联合文件系统ufs,其中着重介绍了overlayFS的工作原理,浅浅的解析了一下docker的文件系统实现,有不足之处请批评指正。

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值