为什么COPY指令在我的Docker构建中导致缓存未命中

8

我项目中docker文件中的复制指令似乎导致了缓存未命中,即使自上次推送镜像至docker hub以来,被复制的文件都没有改变。这导致所有后续层都未使用缓存,使构建时间比应该长得多。我还注意到每个层的哈希值似乎与在本地机器上docker build时不同。这可能是因为docker版本不匹配吗?这里发生了什么?我该如何诊断?


2
你能分享 Dockerfile 或者 COPY 指令吗? - fly2matrix
2
Github链接似乎有问题(%20https不是有效的协议,即使将其更改为https也会出现错误)。无论如何,StackOverflow指南要求您在问题本身中共享不起作用的代码,以便我们可以查看它。 - Bakuriu
2
不确定那个20%是如何出现在复制指令链接中的。现已修复。 - imalison
3个回答

13

使用 docker history --no-trunc $image 命令检查正在构建的不同镜像的层。在复制步骤中,您将看到被复制文件的 "file:abc" 哈希值:

IMAGE                                                                     CREATED             CREATED BY                                                                                                        SIZE                COMMENT
sha256:202cb043f70a2565ea40629e891642e1e24be7b52e29116a6520736f47183904   9 minutes ago       /bin/sh -c #(nop) COPY file:d523f0d1cac93e44179baf9c36a7a4feff221b604224e26900075ddb02812448 in /test/test.txt    12B

如果正在构建的两个镜像之间哈希值不同,那么将使构建缓存无效并导致缓存未命中。请注意,文件的元数据也可能会导致缓存未命中,特别是文件权限。如果仍然遇到问题,请更新问题以包括不同构建的docker history --no-trunc ...输出。


5
如果您的文件内容确实没有改变,那么权限的差异可能会导致缓存失效。
对于ADD和COPY指令,会检查镜像中文件的内容并为每个文件计算校验和。这些校验和不考虑文件的最后修改时间和最后访问时间。在缓存查找期间,会将校验和与现有镜像中的校验和进行比较。如果文件的任何内容和元数据发生了更改,则缓存将失效。

https://docs.docker.com/develop/develop-images/#leverage-build-cache

编辑

从Docker 20.10开始,如果您使用BuildKit,则COPYADD命令添加了一个--chmod参数(尚未在{{link2:文档中提及)}}。因此,您可以确保镜像权限一致,并避免由权限引起的缓存错误,例如:

COPY --chmod=<4字节八进制掩码> ...


2
目前在GitLab CI中发生这种情况是因为一个bug,导致源代码以666权限进行克隆。 - esmail

4
我在这篇博客文章中讨论过,发现Docker缓存未命中的以下原因:
  • 如果问题发生在 CI pipeline 中,并且您使用 多个 构建代理机器(或“短暂”机器),可能会出现构建作业 #1 在代理 #A 上执行,但构建作业 #2 在代理 #B 上执行的情况,后者具有不同的本地缓存。因此,当您查看 CI pipeline 输出时,请确保检查作业在哪个代理上执行。为了防止这种缓存未命中,可以使用远程缓存,在远程镜像注册表中存储缓存信息。有两种实现方法:内联缓存(其中图像构建器将缓存元数据嵌入其构建的图像中),或使用单独的注册表缓存(其中推送一个仅包含缓存块的单独图像)。远程缓存的使用详细信息取决于您的图像构建工具。例如,docker build 仅支持内联缓存(请参见 此处),docker buildx(或直接使用 BuildKit)支持两种方法(请参见 此处),Buildah 和 kaniko 仅支持注册表缓存。
  • 如果您在 Dockerfile 中使用 ARG,很容易意外破坏缓存失效。每当某个 ARG 的值在两个 docker build 执行之间不同时,第二个执行将无法重用先前缓存的层用于使用 ARG 值的 RUNENV 命令,这也使得所有后续层都失效。有关背景信息,请参见 此处。如果您使用多阶段构建,并且如果您运行 docker build 多次(针对不同的目标),请确保您始终所有 docker build 调用提供相同的 ARG 值!
  • 有时,当发布了一个新的基础镜像(您在 FROM 语句中引用)时,整个镜像会被重新构建。特别是如果您使用 docker build --pull。您需要密切关注构建器的第一层输出,其中包括基础镜像的 SHA-256 校验和。如果它经常改变,就没有真正的“解决方法”。您的镜像应该被重建,以包括基础镜像的最新安全修复程序。但是,如果基础镜像经常重新构建(例如每天多次),则可能希望停止使用 --pull 标志,而是采用仅更少地运行 docker pull <base image>(或删除基础镜像)的不同方法,例如每天一次。
  • COPYADD 语句的层在文件更改时会“意外”重建,这些文件您尚未关注(您尚未通过 .dockerignore 将它们排除)。这可能是 .git 文件夹,或者在构建/测试期间创建的文件(例如单元测试报告文件或日志文件)。当运行 COPY . . 时,通常会发生这种情况,因为然后整个项目目

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接