Docker 镜像拼接技术

标签:Docker

公司的服务是 Docker 搭建的,给客户升级时存在一个问题:镜像太大了。
虽然我已经找了很多方法,甚至对于 Go 服务而言,已经全换成 scratch 镜像 + 单可执行文件的方式了,但是对于 Python 服务却没啥办法。大部分的 Python 库是有 C 库依赖的,因此不能用 scratch 镜像,甚至因为 alphine 镜像不是用 libc,也会出现很多兼容性问题。而在 Ubuntu 的基础上安装完依赖后,就轻松超过 300 MB 了。
因为不能在客户那搭建一个 docker repository 把镜像推过去,那只能想些奇技淫巧来实现了。

首先要弄清 Docker 镜像的原理。都说 Docker 镜像是分层存储的,docker pull 时也会发现已有的层不需要重新下载,那么这是咋实现的呢?

最简单的方法就是看看,以 nginx 为例:
$ docker save nginx:1.23 > nginx-1.23.tar
$ mkdir nginx-1.23
$ tar xvf nginx-1.23.tar -C nginx-1.23
x 2b7d6430f78d432f89109b29d88d4c36c868cdbf15dc31d2132ceaa02b993763.json
x 2b886ba09e39e22371be8269603796975a24014b93e4089cdbe8d717cb92c860/
x 2b886ba09e39e22371be8269603796975a24014b93e4089cdbe8d717cb92c860/VERSION
x 2b886ba09e39e22371be8269603796975a24014b93e4089cdbe8d717cb92c860/json
x 2b886ba09e39e22371be8269603796975a24014b93e4089cdbe8d717cb92c860/layer.tar
x 431930a1955ffb133e2e3fc8f0fb09cd933997e0e6c4cf4ed80ad7416ec12249/
x 431930a1955ffb133e2e3fc8f0fb09cd933997e0e6c4cf4ed80ad7416ec12249/VERSION
x 431930a1955ffb133e2e3fc8f0fb09cd933997e0e6c4cf4ed80ad7416ec12249/json
x 431930a1955ffb133e2e3fc8f0fb09cd933997e0e6c4cf4ed80ad7416ec12249/layer.tar
x 58d5fbecd85452f24343ea107726d5e3a4a7551070d6ef93870ffa34d191e924/
x 58d5fbecd85452f24343ea107726d5e3a4a7551070d6ef93870ffa34d191e924/VERSION
x 58d5fbecd85452f24343ea107726d5e3a4a7551070d6ef93870ffa34d191e924/json
x 58d5fbecd85452f24343ea107726d5e3a4a7551070d6ef93870ffa34d191e924/layer.tar
x 6b14c835e7c094e5d9f45f905c30b99ea28dbb55978d4eb0273439548e36e8a4/
x 6b14c835e7c094e5d9f45f905c30b99ea28dbb55978d4eb0273439548e36e8a4/VERSION
x 6b14c835e7c094e5d9f45f905c30b99ea28dbb55978d4eb0273439548e36e8a4/json
x 6b14c835e7c094e5d9f45f905c30b99ea28dbb55978d4eb0273439548e36e8a4/layer.tar
x 7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e/
x 7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e/VERSION
x 7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e/json
x 7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e/layer.tar
x ad36892dced6435f4ea3c8584654dcf90536c7166a4b61bc8ecbf46aa2048c64/
x ad36892dced6435f4ea3c8584654dcf90536c7166a4b61bc8ecbf46aa2048c64/VERSION
x ad36892dced6435f4ea3c8584654dcf90536c7166a4b61bc8ecbf46aa2048c64/json
x ad36892dced6435f4ea3c8584654dcf90536c7166a4b61bc8ecbf46aa2048c64/layer.tar
x manifest.json
x repositories
可以看到,Docker 镜像其实把每一层都用 SHA256 命名,层里的内容存放在 layer.tar 中。如果打开看看 manifest.json 的话,则描述了这些层的组成顺序。

再来比较下不同版本的文件:
$ du -h nginx-1.23
 12K	nginx-1.23/ad36892dced6435f4ea3c8584654dcf90536c7166a4b61bc8ecbf46aa2048c64
 16K	nginx-1.23/431930a1955ffb133e2e3fc8f0fb09cd933997e0e6c4cf4ed80ad7416ec12249
 12K	nginx-1.23/58d5fbecd85452f24343ea107726d5e3a4a7551070d6ef93870ffa34d191e924
 80M	nginx-1.23/7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e
 59M	nginx-1.23/2b886ba09e39e22371be8269603796975a24014b93e4089cdbe8d717cb92c860
 12K	nginx-1.23/6b14c835e7c094e5d9f45f905c30b99ea28dbb55978d4eb0273439548e36e8a4
139M	nginx-1.23
$ du -h nginx-1.22
 12K	nginx-1.22/28a69237f683509b919722050f9dbaed070edb57661be25b2a8c6899e40aa89b
 12K	nginx-1.22/072ff16da6796deb028c8e9319dd427b99412f64214dca3c9d61e3eac5c43077
 16K	nginx-1.22/054e61572efcc7047e359343e711e1f653f1682b6b5225da6d82493bd6ee1d37
 59M	nginx-1.22/8a46cdaae1b864555e31a30336392ac679dbc171424ecf7384350199fa9ffaa1
 80M	nginx-1.22/7e5bd8b30e4e5b8e7abf7c3b8b711cfeafbc119e0821df440d6851ac4312778e
 12K	nginx-1.22/94ecf6cad6b4da33ec01dd6f3e3b0fbc418c76a3d4d957985bcb03722f457e7a
139M	nginx-1.22
这 2 个 139 MB 的镜像,其中 80 MB 的 7e5b 层是相同的。

那么就可以把两个镜像 diff 一下,把相同的层删掉,其他文件保留,然后保存成一个 diff.tar。
再去客户那边,把老镜像导出,解析最新的 manifest.json,把老镜像里不需要的层删掉,把 diff 中的文件 mv 过来,再打包成 tar,就形成了最新的镜像。

当然,对于 nginx 这个镜像来说,只能省 80 MB。但是对于我司的 Python 服务而言,因为可变的主要是最后一层的代码,于是 360 MB 的镜像变成了 18 MB 的 diff,没有用上压缩就达到了 20 倍的压缩率。

2条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?