Docker实战05|Docker构建流程分析

在上一文《Docker实战04|Union File System》中主要介绍了Linux Union File System的基本原理以及实现。相信读完以后可以更加深入的了解Docker关于Union File System的底层实现原理了。

本文继续针对Docker 在构建时都干了哪些事情做一些详细的讲解。

本系列所有代码均已经开源。关公众号回复「Go语言实现Docker」即可获得。

具体分析

以构建镜像方式演示以下 docker 是如何使用 overlayfs 的。

先拉一下 Ubuntu:20.04 的镜像:

~# docker pull ubuntu:20.04
20.04: Pulling from library/ubuntu
527f5363b98e: Pull complete 
Digest: sha256:f2034e7195f61334e6caff6ecf2e965f92d11e888309065da85ff50c617732b8
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04

然后写个简单的 Dockerfile :

 FROM ubuntu:20.04

 RUN echo "Hello world" > /tmp/newfile

开始构建:

docker build -t hello-ubuntu .

Docker实战05|Docker构建流程分析_第1张图片
使用docker history命令,查看镜像使用的 image layer 情况:

Docker实战05|Docker构建流程分析_第2张图片
带 missing 标记的 layer 是自 Docker 1.10 之后,一个镜像的 image layer image history 数据都存储在 个文件中导致的,这是 Docker 官方认为的正常行为。

可以看到,c9afac9d4fa7 这一层在最上面,只用了 12Bytes,而下面的两层都是共享的,这也证明了 AUFS 是如何高效使用磁盘空间的。

然后去找一下具体的文件:

docker 默认的存储目录是/var/lib/docker,具体如下:

Docker实战05|Docker构建流程分析_第3张图片
在这里,我们只关心image和overlay2就足够了。

  • image:镜像相关
  • overlay2:docker 文件所在目录,也可能不叫这个名字,具体和文件系统有关,比如可能是 aufs 等。

先看 image目录:

docker 会在/var/lib/docker/image目录下按每个存储驱动的名字创建一个目录,如这里的overlay2。

Docker实战05|Docker构建流程分析_第4张图片
这里的关键地方是imagedb和layerdb目录,看这个目录名字,很明显就是专门用来存储元数据的地方。

  • layerdb:docker image layer 信息
  • imagedb:docker image 信息

因为 docker image 是由 layer 组成的,而 layer 也已复用,所以分成了 layerdb 和 imagedb。

先去 imagedb 看下刚才构建的镜像:


可以看到,都是 64 位的 ID,这些就是具体镜像信息,刚才构建的镜像 ID 为c9afac9d4fa7,所以就找c9afac9d4fa7开头的文件:

Docker实战05|Docker构建流程分析_第5张图片
这就是 image 的 metadata,这里主要关注 rootfs:

"rootfs":{"type":"layers","diff_ids":["sha256:3a03f09d212915b240e9d216069aba5652ed4765c7e4b098c65e71860d47b8e1","sha256:f37c6b555bc81f96fc6352f3d6be66f6c24f043feab35b15eb2bdad09a8c6a0f"]}}

可以看到 rootfs 的 diff_ids 是一个包含了两个元素的数组,这两个元素就是组成 hello-ubuntu 镜像的两个 Layer 的diffID。

从上往下看,就是底层到顶层,即3a03f09d212915b…是 image 的最底层。

然后根据 layerID 去layerdb目录寻找对应的 layer:

# tree -L 2 layerdb/
layerdb/
├── mounts
├── sha256
└── tmp

在这里我们只管mounts和sha256两个目录,先打印以下 sha256 目录

Docker实战05|Docker构建流程分析_第6张图片

可以看到,layer 里也是 64 位随机 ID 构成的目录,找到刚才 hello-ubuntu 镜像的最底层 layer:


文件含义如下:

  • cache-id:为具体/var/lib/docker/overlay2/存储路径
  • diff:diffID,用于计算 ChainID
  • size:当前 layer 的大小

docker 使用了 chainID 的方式来保存 layer,layer.ChainID 只用本地,根据 layer.DiffID 计算,并用于 layerdb 的目录名称。

chainID 唯一标识了一组(像糖葫芦一样的串的底层)diffID 的 hash 值,包含了这一层和它的父层(底层),

  • 当然这个糖葫芦可以有一颗山楂,也就是 chainID(layer0)==diffID(layer0);
  • 对于多颗山楂的糖葫芦,ChainID(layerN) = SHA256hex(ChainID(layerN-1) + " " + DiffID(layerN))。
# cat diff
sha256:3a03f09d212915b240e9d216069aba5652ed4765c7e4b098c65e71860d47b8e1

由于这是 layer0,所以 chainID 就是 diffID,然后开始计算 layer1 的 chainID:

ChainID(layer1) = SHA256hex(ChainID(layer0) + " " + DiffID(layer1))

layer0 的 chainID 是3a03f…,而 layer1 的 diffID 根据 rootfs 中的数组可知,为f37c6…

计算 ChainID:

#echo -n "sha256:3a03f09d212915b240e9d216069aba5652ed4765c7e4b098c65e71860d47b8e1 sha256:f37c6b555bc81f96fc6352f3d6be66f6c24f043feab35b15eb2bdad09a8c6a0f" | sha256sum| awk '{print $1}'
2089b2e0fb03697ce2198bd03207fa229e27fb8d6d1620b15db60dbc98931a9d

一定注意要加上 “sha256:”和中间的空格“ ” 这两部分

因此 layer1 的 chainID 就是2089b2e…

找到 layerdb 里面以sha256+2089b2e 开头的目录

#cd  /var/lib/docker/image/overlay2/layerdb/sha256/2089b2e0fb03697ce2198bd03207fa229e27fb8d6d1620b15db60dbc98931a9d
#cat size
12
# 查看 cache-id 找到 文件系统中的具体位置
#cat cache-id
kejewb3wshnuy5rmxotue6moi

根据 cache-id 进入具体数据存储目录:

Docker实战05|Docker构建流程分析_第7张图片
可以看到,我们新增的 newfile 就在这里。

未完待续。。。

在前几篇文章中,依次详细讲解了Namespace、Cgroups以及AUFS。相信看完这三部分,面试官再问你多深的Docker问题,你都可以对答如流了。

在后续的文章中,将会继续讲解如何使用Go语言构造容器、构造镜像。

所有Docker实战内容合集:《Docker就应该这么学》

关注我学习更多的云原生知识。

— End 小福利 —

本文所有内容都是基于「动手写Docker」此书。关注公众号,后台回复“动手写Docker”即可领取。

同时,准备了一份云原生实战大礼包送给大家,关注公众号,后台回复“云原生资料”即可领取。
Docker实战05|Docker构建流程分析_第8张图片

Docker实战05|Docker构建流程分析_第9张图片

你可能感兴趣的:(Docker,Go,docker,容器,云原生,kubernetes)