我是 Docker 的新手,所以这似乎是一个愚蠢的问题,但我就是想不通。我已经了解到,docker 映像包含运行应用程序所需的压缩库和依赖项,并且容器是从这些映像创建的。
但是,我不明白的是图像中的压缩代码最终如何在容器内执行。在之前的研究中,我发现容器在其文件系统中包含此代码的副本,并且它们向此代码添加了一个写入层以便可以运行。这就是为什么我发现在实践中图像占用的存储空间比基于它们的容器的整个文件系统要多得多的存储空间,我感到很奇怪。您可能会期望,由于容器的文件系统中包含解压缩的代码,因此这些容器将比它们的映像大得多。
可能,我完全误解了 docker 的工作原理,但是有人可以帮助我吗?提前致谢!
答案1
docker 镜像是柏油包含 tar 文件的文件!
例如,如果我们有一个非常简单的 docker 容器
% cat Dockerfile
FROM scratch
COPY hello /
CMD ["/hello"]
如果我们用它来构建,docker build -t hello .
那么生成的图像可以被拆开:
% mkdir /tmp/container
% cd /tmp/container
% docker save hello | tar xvf -
1cb4c0f3e212f5abfb4b0b74d36a6ace8b7fad164b3e460d8cbf7fb1c3905270.json
59aef640ce511eadb6169072a6d8cefd95a3d1b14b73b92bd86fcb7e0b67618e/
59aef640ce511eadb6169072a6d8cefd95a3d1b14b73b92bd86fcb7e0b67618e/VERSION
59aef640ce511eadb6169072a6d8cefd95a3d1b14b73b92bd86fcb7e0b67618e/json
59aef640ce511eadb6169072a6d8cefd95a3d1b14b73b92bd86fcb7e0b67618e/layer.tar
manifest.json
tar: manifest.json: implausibly old time stamp 1970-01-01 00:00:00
repositories
tar: repositories: implausibly old time stamp 1970-01-01 00:00:00
内容在layer
文件中
% tar tf 59aef640ce511eadb6169072a6d8cefd95a3d1b14b73b92bd86fcb7e0b67618e/layer.tar
hello
如果我们有多个 COPY 命令,那么我们会看到多个层
% cat Dockerfile
FROM scratch
COPY hello /
COPY lib64/ /lib64/
CMD ["/hello"]
结果:
% docker save hello-twolayers | tar xvf -
10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/
10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/VERSION
10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/json
10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/layer.tar
1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/
1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/VERSION
1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/json
1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/layer.tar
6d706ccc02fc6c6ead9b6913b8bd21f15a34abcb09b5deb8e5407af5f4203f14.json
manifest.json
tar: manifest.json: implausibly old time stamp 1970-01-01 00:00:00
repositories
tar: repositories: implausibly old time stamp 1970-01-01 00:00:00
每个 COPY 命令创建一个新的“层”
% tar tf 10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/layer.tar
hello
% tar tf 1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/layer.tar
lib64/
lib64/ld-linux-x86-64.so.2
lib64/libc.so.6
该manifest
文件描述了层顺序
% jq '.[] | [ .Layers ]' < manifest.json
[
[
"10e6ef63539659eef459b5b0795ad1d91e44b4d3d6b5cf369f026cabbef333f4/layer.tar",
"1f5263a466b60441b1569ae0bf6d059f6a866d721bde8c8e795313116400fb4f/layer.tar"
]
]
如果我们采用一个更普通的容器,比如基于 Debian 的容器:
% cat Dockerfile
FROM debian:latest
COPY hello /
CMD ["/hello"]
现在,如果我们查看内容,我们可以看到两层,其中一层有 8259 个文件!
% docker save hello-debian | tar xf -
tar: manifest.json: implausibly old time stamp 1970-01-01 00:00:00
tar: repositories: implausibly old time stamp 1970-01-01 00:00:00
% for a in */layer.tar; do print -n "$a: " ; tar tf $a | wc -l ; done
7a83b1430fe3eaed26ecd9b011dcd885c6f04e45bee3205734fea8ce7382a01e/layer.tar: 1
a1ae6cd0978a7d90aea036f11679f31e1a3142b1f40030fb80d4c8748b0a6a01/layer.tar: 8259
(这些例子取自https://www.sweharris.org/post/2017-06-18-buildcontainer/所以 debian 镜像已有 6 年历史,但概念是相同的)。
现在请注意,每一层都有一个很长的数字,例如“a1ae6cd0978a7d90aea036f11679f31e1a3142b1f40030fb80d4c8748b0a6a01”。这是该层的 sha256 哈希值。如果两个不同的容器基于同一个 debian 镜像,那么它们将具有这个一致的层,因此 docker 守护进程仅将其存储在后端一次。
一旦容器被执行,docker守护进程通常覆盖层(只读)这些层,这样它们就不会使用任何额外的磁盘空间。
例如,我可以看到
% grep docker /proc/mounts | head -1
overlay /var/lib/docker/overlay2/c520fff24fdac7d341e240f62e64a8301f8da9c0d44c5bb5fc62a419efdf6a67/merged overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/QVO5L3KITI4IVHIF3CSS7A55ZP:/var/lib/docker/overlay2/l/CB5FLU2Q5C4WAHSSNNA7IA2LOB:/var/lib/docker/overlay2/l/K2DP5JPIRNFNMEREPMNX7QOHO7:/var/lib/docker/overlay2/l/APTRCOCNEYQROETHW7VZJ7AVIJ:/var/lib/docker/overlay2/l/K53M4VNLYJ3VWSMVOBS67TCUYZ:/var/lib/docker/overlay2/l/DKUICFXQR7PSTEBCBMXBFUNTIX:/var/lib/docker/overlay2/l/YQZ5RLIO5FR6W5MVDDMZNWZRRS:/var/lib/docker/overlay2/l/OPZULDPCI2FQPL4H2ENNKBUB3C:/var/lib/docker/overlay2/l/SHZQ6FNTIP3O5T2NUTHL3CR2GA:/var/lib/docker/overlay2/l/ZOAGXZVNGH2DY76LIBZUPN3NWZ,upperdir=/var/lib/docker/overlay2/c520fff24fdac7d341e240f62e64a8301f8da9c0d44c5bb5fc62a419efdf6a67/diff,workdir=/var/lib/docker/overlay2/c520fff24fdac7d341e240f62e64a8301f8da9c0d44c5bb5fc62a419efdf6a67/work 0 0
我们可以看到/var/lib/docker/overlay2/c520fff24fdac7d341e240f62e64a8301f8da9c0d44c5bb5fc62a419efdf6a67/merged
容器的目录以及共同使其工作的所有层。