【问题标题】:Docker build not using cache when copying Gemfile while using --cache-from使用--cache-from复制Gemfile时Docker构建不使用缓存
【发布时间】:2019-07-01 15:46:35
【问题描述】:

在我的本地机器上,我构建了 latest 映像,并运行另一个 docker build 在任何地方使用缓存。

然后我将图像以latest 的形式上传到注册表,然后在我的 CI 服务器上,我正在拉取我的应用程序的latest 图像,以便将其用作构建缓存来构建新版本:

docker pull $CONTAINER_IMAGE:latest

docker build --cache-from $CONTAINER_IMAGE:latest \
             --tag $CONTAINER_IMAGE:$CI_COMMIT_SHORT_SHA \
             .

从构建输出中我们可以看到GemfileCOPY 没有使用来自latest 图像的缓存,而我还没有更新该文件:

Step 15/22 : RUN gem install bundler -v 1.17.3 &&     ln -s /usr/local/lib/ruby/gems/2.2.0/gems/bundler-1.16.0 /usr/local/lib/ruby/gems/2.2.0/gems/bundler-1.16.1
 ---> Using cache
 ---> 47a9ad7747c6
Step 16/22 : ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile     BUNDLE_JOBS=8
 ---> Using cache
 ---> 1124ad337b98
Step 17/22 : WORKDIR $APP_HOME
 ---> Using cache
 ---> 9cd742111641
Step 18/22 : COPY Gemfile $APP_HOME/
 ---> f7ff0ee82ba2
Step 19/22 : COPY Gemfile.lock $APP_HOME/
 ---> c963b4c4617f
Step 20/22 : RUN bundle install
 ---> Running in 3d2cdf999972

除了节点:它在我的本地机器上运行良好。

查看 Docker 文档 Leverage build cache 似乎并没有解释这里的行为,因为 Dockerfile 和 Gemfile 都没有改变,所以应该使用缓存。

什么会阻止 Docker 使用 Gemfile 的缓存?

更新

我尝试使用COPY --chown=user:group source dest 复制设置正确权限的文件,但它仍然不使用缓存。

已打开 Docker 论坛主题:https://forums.docker.com/t/docker-build-not-using-cache-when-copying-gemfile-while-using-cache-from/69186

【问题讨论】:

  • 我也遇到了同样的问题,你有什么更新吗?
  • 对不起,不,我不@Marcelo,我不得不切换到另一个任务,这个问题在我这边处于待处理状态。我会回来的,但我不知道什么时候。
  • 谢谢,我还在调查中,如果有消息我会告诉你她的
  • 在我的例子中,我试图在 Travis 中构建一个图像,该图像将从 Dockerhub 中构建的另一个图像缓存(然后我遇到了和你一样的问题)。我还不是 100% 确定这是否是问题所在,但 Travis 可能没有从 Dockerhub 映像缓存,因为它们使用不同的 docker 版本(为相同的内容产生不同的哈希值)。对我来说,解决方法是在 Travis 中构建和推送所有内容,并且只使用 docker 作为镜像注册表,因为 Dockerhub 可能正在使用旧的 docker 版本来生成镜像。
  • @Marcelo 我得到了确切的问题,我确认 DockerHub 自动构建有一些东西使COPY 总是被缓存破坏。非常烦人,我没有找到任何参考。

标签: docker caching


【解决方案1】:

在过去的几天里,我一直在为 Docker 构建和 --cache-from 的问题摸不着头脑,对于 --cache-from 的正确行为缺乏文档有点令人沮丧,而在狂野的。

我想我终于设法解决了我遇到的问题,在我将在这里分享一些见解之后,希望它对其他人有用。

当提供多个--cache-from时,顺序很重要!

顺序非常重要,因为在第一次匹配时,Docker 将停止寻找其他匹配项,它将使用该匹配项来执行所有其余命令

This is explained by the fellow who implemented the feature in the Github PR:

当使用多个 --cache-from 时,会按照用户指定的顺序检查缓存命中。如果其中一个图像对命令产生缓存命中,则仅该图像用于构建的其余部分。

还有a lenghtier explanation in the initial ticket proposal

指定多个 --cache-from 图像有点问题。如果两个图像都匹配,则无法(不进行多次传递)确定要使用的图像。所以我们选择第一个(让用户控制优先级),但这可能不是我们最终可以匹配的最长链。如果我们允许针对某些命令匹配一个图像,然后切换到具有更长链的不同图像,我们可能会在图像之间泄漏一些信息,因为我们只验证历史和缓存层。目前我留下了它,这样如果我们得到一个匹配,我们只使用这个目标图像来执行其余的命令。

使用--cache-from是独占的:不会使用本地Docker缓存

这意味着它不会添加新的缓存源,您提供的图像标签将是 Docker 构建的唯一缓存源。

即使你只是在本地构建了相同的镜像,下次你为它运行 docker build 时,为了从缓存中受益,你需要:

  1. 使用--cache-from 提供正确的标签(并具有正确的优先级);或

  2. 根本不使用--cache-from(这样它将使用本地构建缓存)

如果父图发生变化,缓存失效

例如,如果您有一个基于docker:stable 的图像,并且docker:stable 得到了更新,那么您的图像的缓存构建将不再有效,因为基础图像的层发生了变化。

这就是为什么,如果您正在配置 CI 构建,it can be useful to docker pull the base image as well and include it in the --cache-from, as mentioned in this comment in yet another Github discussion

【讨论】:

    【解决方案2】:

    对于与DockerHub automated builds--cache-from 战斗的人。我意识到从 DockerHub 构建的图像在拉取并用作构建缓存源时,总是会导致 COPY 命令上的缓存崩溃。 @Marcelo 似乎也是这种情况(参考 his comment)。

    我通过创建一个非常简单的图像进行了调查,执行了几个RUN 命令和后来的COPY。除了COPY,所有东西都在使用缓存。尽管被复制的文件的内容和权限在拉取的镜像和本地构建的镜像上是相同的(通过sha1sumls -l 验证)。

    对我来说,解决方案是从 CI(在我的例子中是 Travis)将镜像发布到注册表,而不是让 DockerHub 自动构建。让我在这里强调一下,我在这里谈论的是文件绝对相同且不应缓存 bust 的特定情况,但您使用的是 DockerHub 自动构建。

    我不确定为什么会这样,但我知道例如旧的 docker-engine 版本,例如1.8.0之前的版本没有忽略文件时间戳来决定是否使用缓存,参考https://docs.docker.com/release-notes/docker-engine/#180-2015-08-11https://github.com/moby/moby/pull/12031

    【讨论】:

      【解决方案3】:

      我在这个问题上苦苦挣扎,就我而言,当校验和可能发生变化时,我使用了 COPY(但仅在技术上,内容在功能上是相同的)。所以,我是这样解决的:

      Dockerfile:

      ARG builder_image=base-builder
      
      # Compilation/build stage
      FROM golang:1.16 AS base-builder
      RUN echo "build the app" > /go/app
      
      # This step is required to facilitate docker cache. With the definition of a `builder_image` build tag
      # we can essentially skip the build stage and use a prebuilt-image directly.
      FROM $builder_image AS builder
      
      # myapp docker image
      FROM ubuntu:20.04 AS myapp
      
      COPY --from=builder /go/app /opt/my-app/bin/
      

      然后,我可以运行以下命令:

      # build cache
      DOCKER_BUILDKIT=1 docker build --target base-builder -t myapp-builder .
      docker push myapp-builder
      
      # use cache
      DOCKER_BUILDKIT=1 docker build --target myapp --build-arg=builder_image=myapp-builder -t myapp .
      docker push myapp
      

      这样我们可以强制 Docker 使用预构建的镜像作为缓存。

      【讨论】:

      • 这是我在另一个构建主机上使用预构建映像的唯一方法(我在使用 --cache-from 方法时给出了)。非常感谢!
      【解决方案4】:

      对于要缓存的COPY 命令,要复制的源的校验和需要相同。您可以在 docker 历史输出中比较缓存映像和您刚刚构建的映像之间的校验和。最重要的是,除了文件内容之外,校验和还包括文件所有者和文件权限等元数据。文件中的空格更改(例如在 Linux 和 Windows 样式之间更改为换行符)也会影响这一点。如果您从 repo 下载代码,则元数据(如所有者)可能与缓存值不同。

      【讨论】:

      • 感谢您的评论,我会看看的。顺便说一句,您对校验和中包含的修改时间是错误的,因为文档说“这些校验和中不考虑文件的最后修改和最后访问时间。” (请参阅我的“利用构建缓存”链接;))
      • @ZedTuX 确实,看起来构建确实忽略了构建缓存的时间戳更改。我在考虑层差异,它查看要包含在新层中的文件。
      猜你喜欢
      • 1970-01-01
      • 2018-06-27
      • 2018-02-06
      • 2020-11-02
      • 1970-01-01
      • 2020-07-05
      • 2019-08-10
      • 2018-03-16
      • 1970-01-01
      相关资源
      最近更新 更多