本地存储
环境
os: centos 7.3-1611
kernel: 4.16.13
docker-engine: 1.12.6
backend-filesystem: xfs(ftype=1)
storage-driver: overlay2
镜像准备
首先从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
$ tree -L 1 /app/docker
/app/docker
├── containers
├── image
├── network
├── overlay2
├── swarm
├── tmp
├── trust
└── volumesimage的目录树如下:
overlay2
overlay2的目录树如下(注意:这里只显示两层)
repositories.json
repositories.json记录了本地repository相关的信息,主要是repository:tag到iamgeID的映射关系,如下:
其中c6c14b3960bdf9f5c50b672ff566f3dabd3e450b54ae5496f326898513362c98就是image-id,51d8869caea35f58dd6a2309423ec5382f19c4e649b5d2c0e3898493f42289d6是image-digest。如下:
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:
注意:这里我们可以对imageConfig文件重命名然后再做哈希,得到的哈希值还是一样的,也就是说是其实是对文件内容做了哈希
layer-diffid 与 layer-digest的映射关系
image/overlay2/distribution下存储了layer-diffid与layer-digest的映射关系,distribution的目录树如下
diffid-by-digest: 存放layer-digest到layer-diffid的映射关系
v2metadata-by-diffid: 存放layer-diffid到layer-digest的映射关系
查看第一层的layer-digest与layer-diffid的映射关系
layer的元数据
layer的元数据都存储在image/overlay2/layerdb目录下,见目录树。每一个layer对应着一个sha256下的一个目录,目录的名字为layer的chainid。
第一层的chainid就为diffid,第N层的chainid的算法如下:
我们来算一下第二层的chainid
在每一层对应的目录下有以下五个文件(第一层只有四个):
cache-id:docker下载layer的时候在本地生成的一个随机uuid
diff:diff 文件存放layer的diffid
parent:parent文件存放当前layer的父layer的diffid,注意第一层没有父layer故没有该文件
size:该层的大小,单位为字节
tar-split.json.gz:layer压缩包的split文件,通过这个文件可以还原layer的tar包,详情可参考 https://github.com/vbatts/tar-split
layer的数据
所有layer的文件系统数据都存放在/app/docker/overlay2目录下,查看[目录树]。每一个layer都对应着该目录下的一个目录,目录的名子就是layer的cache-id。
另外还有一个l目录,在本地有多少个layer,该目录下就有多少个符号链接文件。符号链接文件指向layer的文件系统目录。如下:
接下来我们来看layer对应目录下的内容(第一层与其他层的内容会不一样,第一层只有diff目录与link文件)
我们看第二层目录下的内容
diff
diff目录下是该layer的文件系统,如下:
link
link文件的内容就是该layer的符号链接文件的名字:
lower
该文件的内容是父layer的符号链接文件的名字,根据这个文件可以索引构建出整个镜像的层次结构
merged和work 目录
每当启动一个容器时,会将 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的第一层:下载得到的文件是经过gizp压缩算法压缩的,接下来我们对该layer做哈希(文件名可以随便取,没有影响):
发现,哈希值就是第一层的layer-digest。
不过,如果我们尝试用先用
tar xzvf layer1.tar.gzip -C layer1/解压,然后再用命令tar czvf layer1.tar.gizp layer1/*压缩,然后再对我们自已压缩过的文件做哈希,得到的哈希值与layer-digest不一致。猜测是不能使用shell的tar命令或者参数不对。
Reference
[1] https://www.yangcs.net/posts/how-manage-image/
[2] https://segmentfault.com/a/1190000009309347
[3] https://segmentfault.com/a/1190000009730986
[4] https://yq.aliyun.com/articles/57752
[5] https://windsock.io/explaining-docker-image-ids/
[6] https://docs.docker.com/registry/spec/api/#pulling-an-image
Last updated
Was this helpful?