使用--cache-from复制Gemfile时,Docker构建未使用缓存

62lalag4  于 2023-02-21  发布在  Docker
关注(0)|答案(4)|浏览(218)

在我的本地机器上,我已经构建了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文档,利用构建缓存似乎没有解释这里的行为,因为Dockerfile和Gemfile都没有更改,所以应该使用缓存。
什么可以阻止Docker使用宝石文件的缓存?

更新

我尝试使用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

vm0i2vca

vm0i2vca1#

让我与您分享一些信息,这些信息帮助我解决了Docker构建和--cache-from的一些问题,同时优化了CI构建。
我已经挣扎了好几天,因为我没有正确的理解,我是基于自己的错误解释发现在网络上。
所以我在这里分享这些,希望对你们有用。

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

顺序非常重要,因为在第一个匹配项时,*Docker将停止查找其他匹配项,并将该匹配项用于所有其余命令 *。
在Github PR中实现该功能的人对此进行了解释:
当使用多个--cache-from时,它们会按照用户指定的顺序检查缓存命中。如果其中一个映像为某个命令生成缓存命中,则只有该映像用于构建的其余部分。
在最初的票建议中,还有一个更长的解释:
指定多个--cache-from图像有点问题。如果两个图像匹配,就没有办法(不需要进行多次扫描)来确定要使用的图像。因此,我们选择第一个图像(让用户控制优先级)但那可能不是我们最终能匹配的最长链。如果我们允许对某些命令的一个映像进行匹配,然后切换到具有更长链的另一个映像,则会有在映像之间泄漏某些信息的风险,因为我们仅验证缓存的历史和图层。目前我离开它,以便如果我们得到一个匹配,我们只使用这个目标图像的其余命令。

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

这意味着它不会添加新的缓存源,您提供的图像标签将是Docker构建版本的唯一缓存源。
即使您刚刚在本地构建了相同的映像,下次运行docker build时,为了该高速缓存中获益,您需要:
1.提供--cache-from的正确标签(以及正确的优先级);或
1.根本不使用--cache-from(这样它将使用本地构建缓存)

如果父映像更改,该高速缓存将失效

例如,如果您有一个基于docker:stable的图像,并且docker:stable已更新,则由于基础图像的层已更改,因此图像的缓存版本将不再有效。
这就是为什么,如果您正在配置CI构建,那么docker pull基本映像并将其包含在--cache-from中也是有用的,正如在Github的另一个讨论中的评论所提到的。

ws51t4hk

ws51t4hk2#

我一直在努力解决这个问题,在我的例子中,当校验和可能发生变化时,我使用了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使用预构建的映像作为缓存。

llew8vvj

llew8vvj3#

对于那些正在与DockerHub automated builds--cache-from斗争的人。我意识到从DockerHub构建的图像在被拉取并用作构建缓存源时总是会导致COPY命令的缓存崩溃。@Marcelo似乎也是如此(参考他的评论)。
我通过创建一个非常简单的映像进行了调查,执行了几个RUN命令和后来的COPY命令。除了COPY之外,所有内容都使用该高速缓存。即使在拉取的映像和本地构建的映像上复制的文件的内容和权限相同(通过sha1sumls -l验证)。
对我来说,解决方案是从CI(Travis)将映像发布到注册表,而不是让DockerHub自动构建来做这件事。让我在这里强调一下,我在这里谈论的是一个特定的情况,其中文件肯定是相同的,不应该缓存崩溃,但您正在使用DockerHub自动构建。
我不知道这是为什么,但我知道例如旧的docker-engine版本,例如1.8.0之前的版本,没有忽略文件时间戳来决定是否使用该高速缓存,参考https://docs.docker.com/release-notes/docker-engine/#180-2015-08-11和https://github.com/moby/moby/pull/12031

5uzkadbs

5uzkadbs4#

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

相关问题