Docker存储驱动之--overlay2

系列文章:
Docker镜像存储-overlayfs
docker背景知识3 镜像分层(overlay2)
Docker存储驱动之–overlay2

1. 镜像层

docker支持多种graphDriver,包括vfs、devicemapper、overlay、overlay2、aufs等等,其中最常用的就是aufs了,但随着linux内核3.18把overlay纳入其中,overlay的地位变得更重,最近也在自己的虚拟机上用overlay2作为docker存储驱动实验了一番,下面来做一个简单的笔记和总结。

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

drwx------.  2 root root   24 Mar 28 07:13 builder
drwx------.  4 root root   92 Mar 28 07:13 buildkit
drwx------.  6 root root 4096 Mar 29 10:25 containers
drwx------.  3 root root   22 Mar 28 07:13 image          '存放image的元数据'
drwxr-x---.  3 root root   19 Mar 28 07:13 network
drwx------. 17 root root 4096 Mar 30 14:32 overlay2       '存放真实的image对应的layer层'
drwx------.  4 root root   32 Mar 28 07:13 plugins
drwx------.  2 root root    6 Mar 30 14:32 runtimes
drwx------.  2 root root    6 Mar 28 07:13 swarm
drwx------.  2 root root    6 Mar 30 14:32 tmp
drwx------.  2 root root    6 Mar 28 07:13 trust
drwx------.  2 root root   25 Mar 28 07:13 volumes

在这里,我们只关心imageoverlay2就足够了。

/var/lib/docker/image存放image的元数据,/var/lib/docker/存放真实的image对应的layer层(最底层存放操作系统,其他层是新增的文件或文件夹等,例如/home /bin),image的元数据通过计算,可以找到对应的真实的layer层。

2. 演示通过image的元数据计算找到对应的真实的layer层

做这个实验之前,我们应该先启动一个容器,在这里使用nginx作为实验:

[root@10 docker]# docker run -d nginx
86b5733e54c7de5ef20cfb5574adedea6cbe11334517309badfbe7d313631310
[root@10 docker]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
86b5733e54c7        nginx               "nginx -g 'daemon of…"   4 seconds ago       Up 3 seconds        80/tcp              practical_vaughan

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

启动容器的目的是确保本地至少存在一个镜像,如果不存在镜像,则会自动下载。

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

cd /var/lib/docker/image
[root@10 image]# ll
total 0
drwx------. 5 root root 81 Mar 28 10:00 overlay2

我们只能看到overlay2这个目录,想必聪明的你也猜到了,docker会在/var/lib/docker/image目录下按每个存储驱动的名字创建一个目录,如这里的overlay2。

接下来,使用tree命令浏览一下这个目录:

cd /var/lib/docker/image
[root@10 image]# tree -L 2 overlay2/
overlay2/
|-- distribution
|   |-- diffid-by-digest
|   `-- v2metadata-by-diffid
|-- imagedb
|   |-- content
|   `-- metadata
|-- layerdb
|   |-- mounts
|   |-- sha256
|   `-- tmp
`-- repositories.json

这里的关键地方是imagedb(存储镜像元数据)layerdb(存储layer层元数据)目录,看这个目录名字,很明显就是imagedb专门用来存储元数据的地方,那为什么区分image和layer呢?因为在docker中,image是由多个layer组合而成的,换句话就是layer是一个共享的层,可能有多个image会指向某个layer

那如何才能确认image包含了哪些layer呢?答案就在imagedb这个目录中去找。比如上面启动的nginx容器,我们可以先找到这个容器对应的镜像:

[root@10 sha256]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
...
nginx               latest              2bcb04bdb83f        3 days ago          109MB
...

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

cd /var/lib/docker/image/overlay2/imagedb/content/sha256
root@10 sha256]# ll
total 20
-rw-------. 1 root root 6025 Mar 28 09:01 2bcb04bdb83f7c5dc30f0edaca1609a716bda1c7d2244d4f5fbbdfef33da366c
-rw-------. 1 root root 1512 Mar 28 10:00 6d1ef012b5674ad8a127ecfa9b5e6f5178d171b90ee462846974177fd9bdd39f
-rw-------. 1 root root 1497 Mar 28 08:51 d8233ab899d419c58cf3634c0df54ff5d8acc28f8173f09c21df4a07229e1205
-rw-------. 1 root root 1686 Mar 28 10:00 eb426204290971fb3c7cfbf25e34308233b75080b6b3735ba5295056d915a998

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

......
"rootfs":{"type":"layers","diff_ids":
[
"sha256:5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda",
"sha256:dd0338cdfab32cdddd6c30efe8c89d0229d9f939e2bb736fbb0a52f27c2b0ee9",
"sha256:7e274c0effe81c48f9337879b058c729c33bd0199e28e2c55093d79398f5e8c0"
]
}}
......

我们只截取rootfs字段,发现有个diff_ids属性,是多个diff_id集合,这个值有什么用?通过diff_id可以计算得到chainId,每个chainId对应一个layer元数据,通过layer元数据,最终映射到真实的layer层。

diff_ids属性在后面的公式chainID(n)=SHA256(chain(n-1) diffID(n))中会用到,
第一层diffID=sha256:5dacd7…
第二层diffID=sha256:dd0338…
以此类推

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

接下来,我们返回到上一层的layerdb中,先打印一下这个目录:

cd /var/lib/docker/image/overlay2/layerdb
[root@10 layerdb]# ll
total 4
drwxr-xr-x. 3 root root   78 Mar 30 14:55 mounts
drwxr-xr-x. 8 root root 4096 Mar 28 10:00 sha256
drwxr-xr-x. 2 root root    6 Mar 28 10:00 tmp

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

[root@10 sha256]# ll /var/lib/docker/image/overlay2/layerdb/sha256/
total 0
drwx------. 2 root root 85 Mar 28 09:01 166d13b0f0cb542034a2aef1c034ee2271e1d6aaee4490f749e72d1c04449c5b
drwx------. 2 root root 71 Mar 28 10:00 3fc64803ca2de7279269048fe2b8b3c73d4536448c87c32375b2639ac168a48b
drwx------. 2 root root 71 Mar 28 09:01 5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda
drwx------. 2 root root 85 Mar 28 10:00 95c536e393c4ffb38d29829c8ed21ae788832580a77436f15907bf646fe13387
drwx------. 2 root root 71 Mar 28 08:51 adab5d09ba79ecf30d3a5af58394b23a447eda7ffffe16c500ddc5ccb4c0222f
drwx------. 2 root root 85 Mar 28 09:01 dfce9ec5eeabad339cf90fce93b20f179926d5819359141e49e0006a52c066ca

在这里,我们仅仅发现5dacd731af1b038…这个最底层的layer,那么剩余两个layer为什么会没有呢?那是因为docker使用了chainID的方式去保存这些layer,简单来说就是chainID(n)=SHA256(chain(n-1) diffID(n))

  • 如果该镜像层是最底层(没有父镜像层),该层的 diffID 便是 chainID。
  • 该镜像层的 chainID 计算公式为 chainID(n)=SHA256(chain(n-1) diffID(n)),也就是根据父镜像层的 chainID 加上一个空格和当前层的 diffID,再计算 SHA256 校验码。

下面我们来演示下计算过程:

  • 第一层:由于是最底层,chainID1=diffID
  • 第二层:套用上面的公式
    chainID(2)=SHA256(chain(1) diffID(2)
    =SHA256(sha256:5dacd7… sha256:dd0338…)
    =166d13b…

可以用在线SHA256工具http://www.jsons.cn/sha/或linux环境自带的语法计算:

echo -n "sha256:5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda sha256:dd0338cdfab32cdddd6c30efe8c89d0229d9f939e2bb736fbb0a52f27c2b0ee9" | sha256sum -
166d13b0f0cb542034a2aef1c034ee2271e1d6aaee4490f749e72d1c04449c5b  -

这个时候,你能看到166d13b…这个layer层的目录了吧?依次类推,我们就能找出所有的layerID的组合。

但是上面我们也说了,/var/lib/docker/image/overlay2/layerdb存的只是元数据,那么真实的rootfs到底存在哪里呢?其中cache-id就是我们关键所在了。我们打印一下/var/lib/docker/image/overlay2/layerdb/sha256/5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda/cache-id:

[root@10 5dacd731af1b0386ead06c8b1feff9f65d9e0bdfec032d2cd0bc03690698feda]# cat cache-id
dffe31c1db6055910b3cd49366a2989d9cd2f3460844437b2190de44807095fa[

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

2. 总结

Docker存储驱动之--overlay2_第1张图片

参照上图,步骤:

  • 1 通过docker images 命令查看本地的某个镜像id ,假设为image_1
  • 2 去/var/lib/docker/image下去查看对应的元数据,根据驱动类型,假设映射到overlay2下:
    /var/lib/docker/image/overlay2/imagedb/content/sha256/image_1
    • 2.1 cat 这个image_1文件,得到diff_ids,即一组diff_id ,假设diff_1
    • 2.2经过计算,单个diff_id可以得到chainId,每个chainId对应一个layer元数据,假设diff_1得到的chainId为 chain_1
    • 2.3 chain_1对应layer元数据路径为/var/lib/docker/image/overlay2/layerdb/sha256/chain_1
    • 2.4 通过layer元数据,最终映射到真实的layer层。查看/var/lib/docker/image/overlay2/layerdb/sha256/chain_1/cache-id,得到真实的 layerId,我们假设为layer1
  • 3 根据前文得到的layer1,我们去找 `/var/lib/docker/overlay2/layer1,这个路径就是真实的layer层,存储目录文件,如果是最底层的镜像层,则是操作系统。

你可能感兴趣的:(docker,k8s,paas,docker,overlay2,存储,驱动)