在 Docker 中有几种“合并”中间层的方法:
更多细节:
原则上,Dockerfile 中的每个命令都会在最终映像中执行命令后添加一个包含文件系统的新“层”,这里 Docker 的帮助是您可以仅通过与上一层的差异来保存每一层,因此我们不会为相同的文件浪费磁盘空间。
例如,如果我们在某个层 0 之上执行 add 然后是 remove 命令,则 add 命令创建层 1,仅包括添加的文件。 remove 命令创建第 2 层,将文件标记为已删除。由于每一层只比较其与前一层的差异,Docker 在构建过程中并不知道第 2 层与第 0 层相同。如果我们重复添加/删除命令,每次添加时,我们都会创建一个大小等于文件的额外层。因此,我们可能会构建具有相同(最终)内容但大小不同的多个图像。例如,我们可以创建一个 32MB 的文件,然后将它添加/删除两次到同一个图像,如:
from ubuntu:latest
ADD big_file .
RUN rm big_file
ADD big_file .
RUN rm big_file
使用docker build . -t big_file:latest 构建它会得到一个大小等于 + 32 MB * 2 的图像:
REPOSITORY TAG IMAGE ID CREATED SIZE
big_file latest ddd32b7a8519 2 minutes ago 140MB
ubuntu latest ba6acccedd29 5 weeks ago 72.8MB
我们可以通过docker history <IMAGE>检查big_file内的层并得到
IMAGE CREATED CREATED BY SIZE COMMENT
ddd32b7a8519 4 minutes ago /bin/sh -c rm big_file 0B
c20573523c30 4 minutes ago /bin/sh -c #(nop) ADD file:937071a2cba4a5d8b… 33.6MB
80ae0642e3ad 4 minutes ago /bin/sh -c rm big_file 0B
0538ebbf489c 4 minutes ago /bin/sh -c #(nop) ADD file:937071a2cba4a5d8b… 33.6MB
ba6acccedd29 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
那么以上三种方法的作用是什么?
它丢弃前一阶段的所有层,只将指定的文件复制到下一阶段。例如
from ubuntu:latest
ADD big_file .
RUN rm big_file
ADD big_file .
RUN rm big_file
ADD big_file .
from ubuntu:latest
COPY --from=0 big_file .
构建它会给出两个图像,一个用于 stage-0,另一个用于 stage-1。
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 6d844f18d92e 5 seconds ago 173MB
big_file latest 4b1025db1335 33 seconds ago 106MB
ubuntu latest ba6acccedd29 5 weeks ago 72.8MB
查看stage-1图像,很明显stage-0图像中的图层没有被复制。它只包含一个由COPY --from=0 big_file . 命令创建的额外层。
IMAGE CREATED CREATED BY SIZE COMMENT
4b1025db1335 47 seconds ago /bin/sh -c #(nop) COPY file:937071a2cba4a5d8… 33.6MB
ba6acccedd29 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
它适合您从第 0 阶段图像中清楚自己需要什么的情况。一个很好的例子是您可以在 stage-0 中编译并且只将二进制文件复制到 stage-1。然而,一个常见的错误是,人们可能会忘记复制二进制文件所需的动态库,这些动态库在 stage-1 映像中丢失,因为这两个映像是具有各自基础映像和层的两个不同映像。
类似于 git 中的squash。它在每一层加载并应用差异来创建一个新层,并且只使用构建图像中的新层。
使用docker build . -t big_file:latest --squash 构建会给出三个图像s
REPOSITORY TAG IMAGE ID CREATED SIZE
big_file latest 6903fba8cef3 2 seconds ago 72.8MB
<none> <none> 28ed65140111 3 seconds ago 140MB
ubuntu latest ba6acccedd29 5 weeks ago 72.8MB
28ed65140111是squash之前的图像,检查big_file中的层
IMAGE CREATED CREATED BY SIZE COMMENT
6903fba8cef3 10 seconds ago 0B merge sha256:28ed65140111012d7604df5123b9be16ab4bfc62dd799259001b5d609ceb8e18 to sha256:ba6acccedd2923aee4c2acc6a23780b14ed4b8a5fa4e14e252a23b846df9b6c1
<missing> 11 seconds ago /bin/sh -c rm big_file 0B
<missing> 12 seconds ago /bin/sh -c #(nop) ADD file:937071a2cba4a5d8b… 0B
<missing> 13 seconds ago /bin/sh -c rm big_file 0B
<missing> 14 seconds ago /bin/sh -c #(nop) ADD file:937071a2cba4a5d8b… 0B
<missing> 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
加载所有差异后,与基础图像没有什么不同,因此合并层6903fba8cef3为空。但squash build 目前是一个实验性功能。
请注意,导出适用于 容器 而不是 图像,它只会转储容器文件系统的当前状态,并忽略 中的层信息图片。如果我们转储一个运行 big_file 映像的容器,然后使用 docker export <CONTAINER_ID> > big_file.tar && docker import - big_file:load < big_file.tar 重新导入它,我们会得到一个“空”映像,如下所示:
IMAGE CREATED CREATED BY SIZE COMMENT
06f4d01022e7 13 seconds ago 72.8MB Imported from -
现在我们无法知道图像是如何构建的,因为没有转储层。
哪个更好真的取决于...,但是 Docker 中层的概念非常重要。 Docker 永远不会忘记任何事情,除非您以某种方式删除或合并该层。