docker支持多种graphDriver,包括vfs、devicemapper、overlay、overlay2、aufs等等,其中最常用的就是aufs了,但随着linux内核3.18把overlay纳入其中,overlay的地位变得更重目前docker默认的存储类型就是overlay2,docker版本是1.8,如下

docker默认的存储目录是/var/lib/docker,下面我们简单打印一下这个目录:

[root@docker2 ~]# ll /var/lib/docker
总用量 24
drwx------.   2 root root    24 5月  15 2019 builder
drwx------.   4 root root    92 5月  15 2019 buildkit
drwx------.   3 root root    78 3月   8 11:14 containers
drwx------.   3 root root    22 5月  15 2019 image
drwxr-x---.   3 root root    19 5月  15 2019 network
drwx------. 165 root root 16384 3月   8 11:14 overlay2
drwx------.   4 root root    32 5月  15 2019 plugins
drwx------    2 root root     6 3月   8 11:10 runtimes
drwx------.   4 root root    83 3月   8 11:10 swarm
drwx------    2 root root     6 3月   8 11:10 tmp
drwx------.   2 root root     6 5月  15 2019 trust
drwx------.  21 root root  4096 8月  11 2019 volumes

只关心image和overlay2,image:主要存放镜像中layer层的元数据overlay2:各层的具体信息
做这个实验之前,我们应该先启动一个容器,在这里使用nginx作为实验:

[root@docker2 ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
88984d1d86a9        nginx               "nginx -g 'daemon of…"   45 hours ago        Up 5 hours          0.0.0.0:80->80/tcp   nginx              "nginx -g 'daemon of…"   4 seconds ago       Up 3 seconds        80/tcp              practical_vaughan

可以看到新启动的nginx容器的id是88984d1d86a9,我们继续往下看。

上面说了,我们只需要关心/var/lib/docker/image/var/lib/docker/overlay2,可以先到/var/lib/docker/image打印一下:

[root@docker2 ~]# cd /var/lib/docker/image/
[root@docker2 image]# ll
总用量 0
drwx------. 5 root root 81 3月   6 19:29 overlay2

我们只能看到overlay2这个目录,docker会在/var/lib/docker/image目录下给使用的存储驱动创建一个目录,如这里的overlay2
接下来,使用tree命令浏览一下这个目录:

[root@docker2 image]# tree -L 2 overlay2/
overlay2/
├── distribution      
│   ├── diffid-by-digest
│   └── v2metadata-by-diffid
├── 
│   ├── content
│   └── metadata
├── 
│   ├── mounts
│   ├── sha256
│   └── tmp
└── repositories.json


这里的关键地方是imagedblayerdb目录,看这个目录名字,很明显就是专门用来存储元数据的地方,那为什么区分image和layer呢?因为在docker中,image是由多个layer组合而成的,换句话就是layer是一个共享的层,可能有多个image会指向某个layer。
那如何才能确认image包含了哪些layer呢?答案就在imagedb这个目录中去找。比如上面启动的nginx容器,我们可以先找到这个容器对应的镜像:

[root@docker2 image]# docker images nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              6678c7c2e56c        3 days ago          127MB
nginx               1.13.7-alpine       22f5726c6dc0        2 years ago         15.5MB

可以看到,imageID是6678c7c2e56c,再次记住这个id,我们打印/var/lib/docker/image/overlay2/imagedb/content/sha256这个目录:

[root@docker2 sha256]# ll  |grep 6678c7c2e56c
-rw-------  1 root root  6666 3月   6 19:29 6678c7c2e56c970388f8d5a398aa30f2ab60e85f20165e101053c3d3a11e6663

第一行的6678c7c2e56c970388f8d5a398aa30f2ab60e85f20165e101053c3d3a11e6663正是记录我们nginx镜像元数据的文件,接下来cat一下这个文件,得到一个长长的json:

[root@docker2 sha256]# cat 6678c7c2e56c970388f8d5a398aa30f2ab60e85f20165e101053c3d3a11e6663  |python  -mjson.tool
。。。。。。
    "rootfs": {
        "diff_ids": [
            "sha256:f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da",
            "sha256:71f2244bc14dacf7f73128b4b89b1318f41a9421dffc008c2ba91bb6dc2716f1",
            "sha256:55a77731ed2630d9c092258490b03be3491d5f245fe13a1c6cb4e21babfb15b7"
        ],
        "type": "layers"

由于篇幅原因,我只展示最关键的一部分,也就是rootfs。可以看到rootfs的diff_ids是一个包含了3个元素的数组,其实这3个元素正是组成nginx镜像的3个layerID,从上往下看,就是底层到顶层,也就是说f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da是image的最底层。既然得到了组成这个image的所有layerID,那么我们就可以带着这些layerID去寻找对应的layer了。
接下来,我们返回到上一层的layerdb中,先打印一下这个目录:

[root@docker2 layerdb]# ll
总用量 20
drwxr-xr-x.   3 root root    78 3月   8 11:14 mounts
drwxr-xr-x. 162 root root 16384 3月   6 19:29 sha256
drwxr-xr-x.   2 root root     6 3月   6 19:29 tmp

在这里我们只管mountssha256两个目录,再打印一下sha256目录:

[root@docker2 layerdb]# ll sha256/ |grep f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da
drwx------  2 root root 71 3月   6 19:27 f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da

在这里,我们仅仅发现f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da这个最底层的layer,那么剩余两个layer为什么会没有呢?那是因为docker使用了chainID的方式去保存这些layer,简单来说就是chainID=sha256sum(H(chainID) diffid),也就是f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da..的上一层的sha256 id是:

[root@docker2 sha256]#  echo -n "sha256:f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da sha256:71f2244bc14dacf7f73128b4b89b1318f41a9421dffc008c2ba91bb6dc2716f1" |sha256sum -
1541955a517830d061b79f2b52b1aed297d81c009ce7766a15527979b6e719c4  -

这个时候,你能看到f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da这个layer层的目录了吧?依次类推,我们就能找出所有的layerID的组合。
但是上面我们也说了,/var/lib/docker/image/overlay2/layerdb存的只是元数据,那么真实的rootfs到底存在哪里呢?其中cache-id就是我们关键所在了。我们打印一下/var/lib/docker/image/overlay2/layerdb/sha256/f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da/cache-id:

[root@docker2 layerdb]# cat sha256/1541955a517830d061b79f2b52b1aed297d81c009ce7766a15527979b6e719c4/cache-id 
f77d281af55651a70e5fc8f31de840d5b5461f36d930545db39f01bc839e4097

没错,这个id就是对应/var/lib/docker/overlay2/f77d281af55651a70e5fc8f31de840d5b5461f36d930545db39f01bc839e4097。因此,以此类推,更高一层的layer对应的cache-id也能找到对应的rootfs,当这些rootfs的diff目录通过联合挂载的方式挂载到某个目录,就能完整整个容器需要的rootfs了。

[root@docker2 overlay2]# ll f77d281af55651a70e5fc8f31de840d5b5461f36d930545db39f01bc839e4097

总用量 8

drwxr-xr-x 7 root root 61 3月   6 19:29 diff

-rw-r--r-- 1 root root 26 3月   6 19:29 link

-rw-r--r-- 1 root root 28 3月   6 19:29 lower

drwx------ 2 root root  6 3月   6 19:29 work