【问题标题】:Where are files stored in docker daemon?docker daemon 中的文件存储在哪里?
【发布时间】:2021-11-22 09:05:07
【问题描述】:

我尝试通过ADD 命令添加文件,然后将其删除。但是 docker 图像的大小也表明它包含这些文件!如果我将* 放在.dockerignore 中,它将无法与ADD 一起使用。

Dockerfile:

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 在 cmets 中提到的那样,该文件进入了在其上构建最终图像的层之一。有什么办法可以去掉那个层以减小最终图像的大小?

$ 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 不只是存储最终图像。它存储了获得最终图像所需的所有步骤。它将每个步骤存储在自己的“层”中。在您的情况下,您向 ubuntu 映像添加了两个新层:一个包含文件,另一个被删除。
  • 实际上,当我从网上下载该文件然后删除它时,docker 图像的大小保持不变!有没有办法摆脱那个文件,因为它使 docker 图像的大小变得难以置信地大?

标签: docker dockerfile docker-machine


【解决方案1】:

在 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 &lt;IMAGE&gt;检查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 映像中丢失,因为这两个映像是具有各自基础映像和层的两个不同映像。

  • --squash

类似于 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 &lt;CONTAINER_ID&gt; &gt; big_file.tar &amp;&amp; docker import - big_file:load &lt; big_file.tar 重新导入它,我们会得到一个“空”映像,如下所示:

IMAGE          CREATED          CREATED BY   SIZE      COMMENT
06f4d01022e7   13 seconds ago                72.8MB    Imported from -

现在我们无法知道图像是如何构建的,因为没有转储层。

哪个更好真的取决于...,但是 Docker 中层的概念非常重要。 Docker 永远不会忘记任何事情,除非您以某种方式删除或合并该层。

【讨论】:

  • 感谢您的详细解答。不幸的是,在这种情况下,多阶段构建对我没有帮助,因为即使我设置了最后一个命令RUN rm /tmp/*,大小仍然是107MB--squash 仅在启用了实验功能的 Docker 守护程序上受支持!。导出/导入生成了一个简洁的图像,但我想在 Dockerfile 中进行。
  • multistage 在这里没有帮助,因为在 Maroun 的示例中,COPY --from=0 /tmp/myfile ./ 仍将 big_file 复制到 Stage-1 图像。副本创建一个图层 0,然后rm /tmp/* 创建另一个图层 1,因此big_file 仍然存在于图层 0 中。只有当您不将大文件复制到 stage-1 图像时,Multistage 可能会有所帮助。您可以根据stage-0 image中的文件完成所有步骤,并将除了大文件之外的nenessary文件复制到stage-1 image。
【解决方案2】:

您正在寻找的是 Docker 多阶段构建,您首先在构建中使用包含所有依赖项的映像,然后构建仅包含相关工件的新映像。这样您就不必删除不需要的文件,甚至不包括它们。

https://docs.docker.com/develop/develop-images/multistage-build/

【讨论】:

    【解决方案3】:

    你可以有一个multistage 构建:

    FROM alpine:latest  
    ADD myfile /tmp
    
    from ubuntu:20.04
    COPY --from=0 /tmp/myfile ./
    RUN rm /tmp/*
    

    【讨论】:

    • 对我不起作用!它仍然是 107MB!
    • 其实有无最后一行--RUN rm /tmp/*--,图片大小为107MB
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多