docker 守护程序中的文件存储在哪里?
Where are files stored in docker daemon?
我尝试通过 ADD
命令添加一个文件,然后将其删除。但是 docker 图像的大小也表明它包含该文件!如果我将 *
放在 .dockerignore
中,它将不适用于 ADD
。
Docker 文件:
from ubuntu:20.04
ADD myfile /tmp
RUN rm /tmp/*
然后我通过
$ docker build -t testwf .
第一阶段显示如下:
Sending build context to Docker daemon 34.21MB
myfile
个文件的大小约为 33MB
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
testwf latest 96543168ab34 16 minutes ago 107MB
ubuntu 20.04 ba6acccedd29 5 weeks ago 72.8MB
实际上,我应该得到一个 72.8MB
与 ubuntu
相同大小的图像,而不是 107MB
,它大致等于 72.8MB
加上 33MB
!换句话说,如果我没有使用 ADD
命令的那个文件,有什么方法可以访问容器中的文件,因为它被复制到 Docker daemon
?
更新
正如评论中提到的 HansKilian 文件进入其中一层,最终图像构建在该层之上。有什么方法可以去掉该层以减小最终图像的尺寸吗?
$ docker history testwf:latest
IMAGE CREATED CREATED BY SIZE COMMENT
2af2733972ab 4 seconds ago /bin/sh -c rm /tmp/* 0B
40d13da4e0cc 4 seconds ago /bin/sh -c #(nop) ADD file:0ddf694d27b108b4a… 34.2MB
ba6acccedd29 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
您正在寻找的是 Docker 多阶段构建,您首先使用图像进行构建,并使用所有依赖项,然后构建仅包含相关工件的新图像。这样您就不必删除不需要的文件,甚至不包括它们。
https://docs.docker.com/develop/develop-images/multistage-build/
您可以multistage构建:
FROM alpine:latest
ADD myfile /tmp
from ubuntu:20.04
COPY --from=0 /tmp/myfile ./
RUN rm /tmp/*
在Docker中有几种“合并”中间层的方法:
- Romain Prévost 的回答中提到的多阶段构建
--squash
命令选项 docker build
https://docs.docker.com/engine/reference/commandline/image_build/
- 导出容器 运行 使用
docker export
最终图像,然后使用 docker import
重新导入它。
更多详情:
原则上,Dockerfile中的每个命令都会在最终镜像中执行命令后添加一个包含文件系统的新“层”,Docker这里的帮助是您可以保存每个层仅通过它与 前一层 的差异,所以我们不会为相同的文件浪费磁盘 space。
例如,如果我们在第 0 层之上执行 add
然后 remove
命令,则 add
命令创建仅包含添加文件的第 1 层。 remove
命令创建第 2 层,将文件标记为已删除。由于每一层仅比较其与前一层的差异,Docker 在构建期间不知道第 2 层与第 0 层相同。如果我们重复 add/delete 命令,每次添加时,我们都会创建一个与文件大小相等的额外层。因此,我们可能会构建具有相同(最终)内容但大小不同的多个图像。例如,我们可能会创建一个 32MB 的文件,然后 add/delete 将它两次复制到同一张图片,例如:
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
它适合你从stage-0图像中清楚你需要什么的情况。一个很好的例子是您可以在 stage-0 中编译并仅将二进制文件复制到 stage-1。然而,一个常见的错误是,人们可能会忘记复制阶段 1 映像中缺少的二进制文件所需的动态库,因为这两个映像是两个不同的映像,具有各自的基础映像和层。
--squash
类似于git中的squash
。它在每一层中加载并应用 diff 以创建一个新层,并仅在构建的图像中使用新层。
使用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
是压缩前的图像,检查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
构建目前是一项实验性功能。
- export/import
请注意导出适用于容器而不是图像,它只转储容器文件系统的当前状态,并且忽略 图像 中的图层信息。如果我们转储一个容器 运行 我们的 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 永远不会忘记任何东西,除非你以某种方式删除或合并图层。
我尝试通过 ADD
命令添加一个文件,然后将其删除。但是 docker 图像的大小也表明它包含该文件!如果我将 *
放在 .dockerignore
中,它将不适用于 ADD
。
Docker 文件:
from ubuntu:20.04
ADD myfile /tmp
RUN rm /tmp/*
然后我通过
$ docker build -t testwf .
第一阶段显示如下:
Sending build context to Docker daemon 34.21MB
myfile
个文件的大小约为 33MB
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
testwf latest 96543168ab34 16 minutes ago 107MB
ubuntu 20.04 ba6acccedd29 5 weeks ago 72.8MB
实际上,我应该得到一个 72.8MB
与 ubuntu
相同大小的图像,而不是 107MB
,它大致等于 72.8MB
加上 33MB
!换句话说,如果我没有使用 ADD
命令的那个文件,有什么方法可以访问容器中的文件,因为它被复制到 Docker daemon
?
更新
正如评论中提到的 HansKilian 文件进入其中一层,最终图像构建在该层之上。有什么方法可以去掉该层以减小最终图像的尺寸吗?
$ docker history testwf:latest
IMAGE CREATED CREATED BY SIZE COMMENT
2af2733972ab 4 seconds ago /bin/sh -c rm /tmp/* 0B
40d13da4e0cc 4 seconds ago /bin/sh -c #(nop) ADD file:0ddf694d27b108b4a… 34.2MB
ba6acccedd29 5 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
您正在寻找的是 Docker 多阶段构建,您首先使用图像进行构建,并使用所有依赖项,然后构建仅包含相关工件的新图像。这样您就不必删除不需要的文件,甚至不包括它们。
https://docs.docker.com/develop/develop-images/multistage-build/
您可以multistage构建:
FROM alpine:latest
ADD myfile /tmp
from ubuntu:20.04
COPY --from=0 /tmp/myfile ./
RUN rm /tmp/*
在Docker中有几种“合并”中间层的方法:
- Romain Prévost 的回答中提到的多阶段构建
--squash
命令选项docker build
https://docs.docker.com/engine/reference/commandline/image_build/- 导出容器 运行 使用
docker export
最终图像,然后使用docker import
重新导入它。
更多详情:
原则上,Dockerfile中的每个命令都会在最终镜像中执行命令后添加一个包含文件系统的新“层”,Docker这里的帮助是您可以保存每个层仅通过它与 前一层 的差异,所以我们不会为相同的文件浪费磁盘 space。
例如,如果我们在第 0 层之上执行 add
然后 remove
命令,则 add
命令创建仅包含添加文件的第 1 层。 remove
命令创建第 2 层,将文件标记为已删除。由于每一层仅比较其与前一层的差异,Docker 在构建期间不知道第 2 层与第 0 层相同。如果我们重复 add/delete 命令,每次添加时,我们都会创建一个与文件大小相等的额外层。因此,我们可能会构建具有相同(最终)内容但大小不同的多个图像。例如,我们可能会创建一个 32MB 的文件,然后 add/delete 将它两次复制到同一张图片,例如:
from ubuntu:latest
ADD big_file .
RUN rm big_file
ADD big_file .
RUN rm big_file
用 docker build . -t big_file:latest
构建它得到一个大小等于
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
它适合你从stage-0图像中清楚你需要什么的情况。一个很好的例子是您可以在 stage-0 中编译并仅将二进制文件复制到 stage-1。然而,一个常见的错误是,人们可能会忘记复制阶段 1 映像中缺少的二进制文件所需的动态库,因为这两个映像是两个不同的映像,具有各自的基础映像和层。
--squash
类似于git中的squash
。它在每一层中加载并应用 diff 以创建一个新层,并仅在构建的图像中使用新层。
使用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
是压缩前的图像,检查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
构建目前是一项实验性功能。
- export/import
请注意导出适用于容器而不是图像,它只转储容器文件系统的当前状态,并且忽略 图像 中的图层信息。如果我们转储一个容器 运行 我们的 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 永远不会忘记任何东西,除非你以某种方式删除或合并图层。