环境
backend-filesystem: xfs(ftype=1)
镜像准备
首先从docker官网拉取镜像 library/registry:2.5.0
,然后用其搭建一个私有镜像仓库 192.168.1.103:8021
,然后再把该镜像上传到私有镜像仓库中
目录树
我们在主机A上pull镜像192.168.1.103:8021/library/registry:2.5.0
,接下来,我们看这台主机上镜像的存储结构。主机A上docker的安装目录为/app/docker
。
/app/docker
下有多个目录,与镜像相关的有两个:image
与overlay2
Copy $ tree -L 1 /app/docker
/app/docker
├── containers
├── image
├── network
├── overlay2
├── swarm
├── tmp
├── trust
└── volumes
Copy $ tree image
image
└── overlay2
├── distribution
│ ├── diffid-by-digest
│ │ └── sha256
│ │ ├── 06ba8e23299fcf9dd9efb3c5acd4c9d03badac5392953001c75d38197113a63a
│ │ ├── 2ee5ed28ffa762104505295c3c256c52a87fe8af0114b9e0198e9036495e10b8
│ │ ├── 802d2a9c64e8f556e510b4fe6c5378b9d49d8335a766d156ef21c7aeac64c9d6
│ │ ├── d1562c23a8aa4913a2fc720a3c478121f45d26597b58bbf9a29238276ca420a7
│ │ └── e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58
│ └── v2metadata-by-diffid
│ └── sha256
│ ├── 35039a507f7ae2cb74fd2405e6230036ee912588fcaac4d3c561774817590e97
│ ├── 3bb5bc5ad373d4855414158babfedcd81a8e27cca04a861a5640c7ec9079bcfb
│ ├── 4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f
│ ├── aa3a31ee27f3d041998258e135f623696d2c21a63ddf798ae206322c7d518247
│ └── d00444e19d6513efe0e586094adb85fe5fc1c425d48e5b94263c65860a75d989
├── imagedb
│ ├── content
│ │ └── sha256
│ │ └── c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98
│ └── metadata
│ └── sha256
├── layerdb
│ ├── sha256
│ │ ├── 273edac7c3ab13711e95ed35a4eb397e10ae9b69c896c9ad28b64cb9097be327
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── 3d9b8d55844ef4dc948d650855a2be52c6193502ba13b9afea9169495f254a03
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── 4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── aff0ec55a7b1c314b647de027c36c25688f9784fee9ca34cbee0de56309fd5ea
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ └── e553e3aa34103ab20e92e15af09af55aab8a3c8b1608a2f86c2ec3ee38b7ea45
│ │ ├── cache-id
│ │ ├── diff
│ │ ├── parent
│ │ ├── size
│ │ └── tar-split.json.gz
│ └── tmp
└── repositories.json
overlay2的目录树如下(注意:这里只显示两层 )
Copy $ tree -L 2 overlay2
overlay2
├── 0f3f3124f1b247e8f20973ea11218589ec3f93e7acbc531acd9420029928dedf
│ ├── diff
│ ├── link
│ ├── lower
│ ├── merged
│ └── work
├── 15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d
│ ├── diff
│ ├── link
│ ├── lower
│ ├── merged
│ └── work
├── 3b14865a1ffd6e2926786e44da9176dfc0d98ce9ffffe23b8ed1f40993700960
│ ├── diff
│ ├── link
│ ├── lower
│ ├── merged
│ └── work
├── 9d8ccd3c87b4c56076c0e8924d8313ca77f5b2dcff5c8701bae7c91ee115e13c
│ ├── diff
│ ├── link
│ ├── lower
│ ├── merged
│ └── work
├── d388553d01e501540f5866e98bb3949ecfea24704bc900c01272e3fc74353753
│ ├── diff
│ └── link
└── l
├── IDKNGIJXKXKT55FYGI7Z6SMRI6 -> ../9d8ccd3c87b4c56076c0e8924d8313ca77f5b2dcff5c8701bae7c91ee115e13c/diff
├── KVYTHEQMEN2OKZWGWBCT5FSDUZ -> ../0f3f3124f1b247e8f20973ea11218589ec3f93e7acbc531acd9420029928dedf/diff
├── N62LB6PKCXTZOY7MNOFDZKVRL7 -> ../d388553d01e501540f5866e98bb3949ecfea24704bc900c01272e3fc74353753/diff
├── OBXTURUR4WON6PIOZ66VAOSWRP -> ../3b14865a1ffd6e2926786e44da9176dfc0d98ce9ffffe23b8ed1f40993700960/diff
└── YOEFSN7RITY7NUQM33XC3ZIY5N -> ../15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/diff
repositories.json
repositories.json记录了本地repository相关的信息,主要是repository:tag
到iamgeID
的映射关系,如下:
其中c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98
就是image-id,51d8869caea35f58dd6a2309423ec5382f19c4e649b5d2c0e3898493f42289d6
是image-digest。如下:
Copy $ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
192.168.1.103:8021/library/registry 2.5.0 sha256:51d8869caea35f58dd6a2309423ec5382f19c4e649b5d2c0e3898493f42289d6 c6c14b3960bd 2 years ago 33.31 MB
ImageConfig
每一个镜像(ImageID)都会有一个配置文件,比如上面的镜像c6c14b3960bd
的配置文件就是image/overlay2/imagedb/content/sha256/c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98
,查看该文件的内容如下:
在镜像的配置文件中,有该镜像所包含的所有layer,每一个layer用diff-id标识。diff_ids数组中,diff-ids[n]
是diff-ids[n+1]
的parent,diff-ids[0]
是最底层,它没有parent,diff-ids[size-1]
表示最顶层。
image-id其实就是根据镜像的配置文件做哈希得到的,比如对上面的配置文件的内容做哈希,会发现哈希值就是image-id:
Copy $ sha256sum image/overlay2/imagedb/content/sha256/c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98
c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98
注意:这里我们可以对imageConfig文件重命名然后再做哈希,得到的哈希值还是一样的,也就是说是其实是对文件内容做了哈希
layer-diffid 与 layer-digest的映射关系
image/overlay2/distribution
下存储了layer-diffid
与layer-digest
的映射关系,distribution的目录树如下
Copy distribution
├── diffid-by-digest
│ └── sha256
│ ├── 06ba8e23299fcf9dd9efb3c5acd4c9d03badac5392953001c75d38197113a63a
│ ├── 2ee5ed28ffa762104505295c3c256c52a87fe8af0114b9e0198e9036495e10b8
│ ├── 802d2a9c64e8f556e510b4fe6c5378b9d49d8335a766d156ef21c7aeac64c9d6
│ ├── d1562c23a8aa4913a2fc720a3c478121f45d26597b58bbf9a29238276ca420a7
│ └── e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58
└── v2metadata-by-diffid
└── sha256
├── 35039a507f7ae2cb74fd2405e6230036ee912588fcaac4d3c561774817590e97
├── 3bb5bc5ad373d4855414158babfedcd81a8e27cca04a861a5640c7ec9079bcfb
├── 4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f
├── aa3a31ee27f3d041998258e135f623696d2c21a63ddf798ae206322c7d518247
└── d00444e19d6513efe0e586094adb85fe5fc1c425d48e5b94263c65860a75d989
diffid-by-digest: 存放layer-digest到layer-diffid的映射关系
v2metadata-by-diffid: 存放layer-diffid到layer-digest的映射关系
查看第一层的layer-digest与layer-diffid的映射关系
Copy $ cat diffid-by-digest/sha256/e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58
sha256:4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f
$ jq . v2metadata-by-diffid/sha256/4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f
[
{
"Digest": "sha256:e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58",
"SourceRepository": "192.168.1.103:8021/library/registry"
}
]
layer的元数据
layer的元数据都存储在image/overlay2/layerdb
目录下,见目录树。每一个layer对应着一个sha256下的一个目录,目录的名字为layer的chainid。
第一层的chainid就为diffid,第N层的chainid的算法如下:
Copy chainid(layerN) = sha256("chainid(layer1) chainid(layer2) ... chainid(layerN-1) diffid(layerN)")
我们来算一下第二层的chainid
Copy $ echo -n "sha256:4fe15f8d0ae69e169824f25f1d4da3015a48feeeeebb265cd2e328e15c6a869f sha256:aa3a31ee27f3d041998258e135f623696d2c21a63ddf798ae206322c7d518247" | sha256sum
aff0ec55a7b1c314b647de027c36c25688f9784fee9ca34cbee0de56309fd5ea -
在每一层对应的目录下有以下五个文件(第一层只有四个):
cache-id:docker下载layer的时候在本地生成的一个随机uuid
diff:diff 文件存放layer的diffid
parent:parent文件存放当前layer的父layer的diffid,注意第一层没有父layer故没有该文件
layer的数据
所有layer的文件系统数据都存放在/app/docker/overlay2
目录下,查看[目录树]。每一个layer都对应着该目录下的一个目录,目录的名子就是layer的cache-id。
另外还有一个l
目录,在本地有多少个layer,该目录下就有多少个符号链接文件。符号链接文件指向layer的文件系统目录。如下:
Copy lrwxrwxrwx. 1 root root 72 Aug 27 16:34 IDKNGIJXKXKT55FYGI7Z6SMRI6 -> ../9d8ccd3c87b4c56076c0e8924d8313ca77f5b2dcff5c8701bae7c91ee115e13c/diff
lrwxrwxrwx. 1 root root 72 Aug 27 16:34 KVYTHEQMEN2OKZWGWBCT5FSDUZ -> ../0f3f3124f1b247e8f20973ea11218589ec3f93e7acbc531acd9420029928dedf/diff
lrwxrwxrwx. 1 root root 72 Aug 27 16:34 N62LB6PKCXTZOY7MNOFDZKVRL7 -> ../d388553d01e501540f5866e98bb3949ecfea24704bc900c01272e3fc74353753/diff
lrwxrwxrwx. 1 root root 72 Aug 27 16:34 OBXTURUR4WON6PIOZ66VAOSWRP -> ../3b14865a1ffd6e2926786e44da9176dfc0d98ce9ffffe23b8ed1f40993700960/diff
lrwxrwxrwx. 1 root root 72 Aug 27 16:34 YOEFSN7RITY7NUQM33XC3ZIY5N -> ../15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/diff
接下来我们来看layer对应目录下的内容(第一层与其他层的内容会不一样,第一层只有diff目录与link文件)
我们看第二层目录下的内容
Copy ll 15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/
total 8
drwxr-xr-x. 6 root root 50 Aug 27 16:34 diff
-rw-r--r--. 1 root root 26 Aug 27 16:34 link
-rw-r--r--. 1 root root 28 Aug 27 16:34 lower
drwx------. 2 root root 6 Aug 27 16:34 merged
drwx------. 2 root root 6 Aug 27 16:34 work
diff目录下是该layer的文件系统,如下:
Copy ll 15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/diff
total 0
drwxr-xr-x. 5 root root 79 Jun 24 2016 etc
drwxr-xr-x. 3 root root 61 Jun 24 2016 lib
drwxr-xr-x. 7 root root 66 Jun 24 2016 usr
drwxr-xr-x. 3 root root 19 Jun 24 2016 var
link文件的内容就是该layer的符号链接文件的名字:
Copy $ cat 15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/link
YOEFSN7RITY7NUQM33XC3ZIY5N
该文件的内容是父layer的符号链接文件的名字,根据这个文件可以索引构建出整个镜像的层次结构
Copy $ cat 15e3398435f9e280ce76c0006ebb9c01c717e1cf9c167d06cf9ea24fc6f6632d/lower
l/N62LB6PKCXTZOY7MNOFDZKVRL7
每当启动一个容器时,会将 link 指向的镜像层目录以及 lower 指向的镜像层目录联合挂载到 merged 目录,因此,容器内的视角就是 merged 目录下的内容。
而 work 目录则是用来完成如 copy-on-write 的操作。
在没有启动容器的时候,这两个目录下都是空的。
manifest
前面已经介绍了 config 文件和 layer 的存储位置,但唯独不见 manifest,去哪了呢?
manifest 里面包含的内容就是对 config 和 layer 的 sha256 + media type 描述,目的就是为了下载 config 和 layer,等 image 下载完成后,manifest 的使命就完成了,里面的信息对于 image 的本地管理来说没什么用,所以 docker 在本地没有单独的存储一份 manifest 文件与之对应。
不过我们可以看一下manifest文件长什么样。通过命令curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" 192.168.1.103:8021/v2/library/registry/manfiests/2.5.0
下载library/registry:2.5.0
的manifest,内容如下:
发现,config.digest
就是镜像的image-digest。layers数组中就是各层的layer-digest;layers[0]是第一层,layers[N-1]是最顶层。
镜像的下载过程
docker 发送 image 的名称+tag(或者 digest)给 registry 服务器,服务器根据收到的 image 的名称+tag(或者 digest),找到相应 image 的 manifest,然后将 manifest 返回给 docker
docker 得到 manifest 后,读取里面 image 配置文件的 digest(sha256),这个 sha256 码就是 image 的 ID
根据 ID 在本地找有没有存在同样 ID 的 image,有的话就不用继续下载了
如果没有,那么会给 registry 服务器发请求(里面包含配置文件的 sha256 和 media type),拿到 image 的配置文件(Image Config)
根据配置文件中的 diff_ids(每个 diffid 对应一个 layer tar 包的 sha256,tar 包相当于 layer 的原始格式),在本地找对应的 layer 是否存在
如果 layer 不存在,则根据 manifest 里面 layer 的 sha256 和 media type 去服务器拿相应的 layer(相当去拿压缩格式的包)。
拿到后进行解压,并检查解压后 tar 包的 sha256 能否和配置文件(Image Config)中的 diff_id 对的上,对不上说明有问题,下载失败
根据 docker 所用的后台文件系统类型,解压 tar 包并放到指定的目录
等所有的 layer 都下载完成后,整个 image 下载完成,就可以使用了
附录
接下来总结一下各种id
image-id
image-id是imageConfig的sha256的哈希值。
image-digest
image-digest是manifest的sha256的哈希值。
layer-diffid
layer-diffid是对layer的未压缩的tar包的内容做sha256得到的哈希值。
我们可以通过registry的API下载到某个layer的压缩后的tar包layer.tar.gzip
,但是实验中发现手动用tar xzvf layer.tar.gzip -C layer/
先解压,然后再用tar cvf layer.tar layer/*
打包,对layer.tar做sha256sum layer.tar
,得到的哈希值并不是layer的diffid。猜测layer的解压与打包用shell的命令不对或打包时有些参数不对。
layer-digest
layer-digest是对layer的压缩后的tar包的内容做sha256得到的哈希值。
我们可以通过registry的API GET /v2/{repository}/blobs/{layer-digest}
下载压缩后的layer,然后对该文件内容做sha256哈希,得到的哈希值就是layer-digest。
比如,我们下载镜像library/registry:2.5.0
的第一层:
Copy curl 192.168.1.103:8021/v2/library/registry/blobs/sha256:e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58 -o layer1.tar.gzip
下载得到的文件是经过gizp压缩算法压缩的,接下来我们对该layer做哈希(文件名可以随便取,没有影响):
Copy $ sha256sum layer1.tar.gzip
e110a4a1794126ef308a49f2d65785af2f25538f06700721aad8283b81fdfa58
发现,哈希值就是第一层的layer-digest。
不过,如果我们尝试用先用tar xzvf layer1.tar.gzip -C layer1/
解压,然后再用命令tar czvf layer1.tar.gizp layer1/*
压缩,然后再对我们自已压缩过的文件做哈希,得到的哈希值与layer-digest不一致。猜测是不能使用shell的tar命令或者参数不对。
Reference