Docker构建未使用层缓存

10

给定:我想要构建一个Dockerfile来编译Scala应用程序。为了加快构建速度,我希望将依赖项下载缓存。

问题:命令./sbt -sbt-dir ./sbt-dir -ivy ./ivy update由于某种原因无法被缓存。

FROM openjdk:8 as workspace

ARG BUILD_VERSION

WORKDIR /build

COPY ./sbt ./sbt
COPY ./sbt-dist ./sbt-dist
COPY ./build.sbt ./build.sbt
COPY ./project/build.properties ./project/build.properties
COPY ./project/plugins.sbt ./project/plugins.sbt

RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy update

COPY ./ ./

# Embedded postgres need to be run as non-root user
RUN useradd -ms /bin/bash runner
RUN chown -R runner /build
USER runner

RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy clean test
RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy docker:stage -Ddocker.image.version="${BUILD_VERSION}"

因为每次都在新的虚拟机中运行此构建,我会在本次构建后推送工作空间镜像,并在下次运行时从中拉取以构建缓存。

docker build --rm=false --cache-from=workspace --build-arg BUILD_VERSION=1 -t workspace .

这里是一个输出结果的片段。
Step 2/22 : ARG BUILD_VERSION
 ---> Using cache
 ---> de98ffcfad8e
Step 3/22 : WORKDIR /build
 ---> Using cache
 ---> 253b71142240
Step 4/22 : COPY ./sbt ./sbt
 ---> Using cache
 ---> 3091fa1e1821
Step 5/22 : COPY ./sbt-dist ./sbt-dist
 ---> Using cache
 ---> f9c68659cd91
Step 6/22 : COPY ./build.sbt ./build.sbt
 ---> Using cache
 ---> d30058c451fc
Step 7/22 : COPY ./project/build.properties ./project/build.properties
 ---> Using cache
 ---> 7451eb63303f
Step 8/22 : COPY ./project/plugins.sbt ./project/plugins.sbt
 ---> Using cache
 ---> 79ac2d1e5ff5
Step 9/22 : RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy update
 ---> Running in 609104e7045e
Getting org.scala-sbt sbt 1.0.3 ...

有人能解释一下为什么Docker在这里没有使用缓存吗?如果有一个解释缓存何时真正决定使用缓存的链接也可以。据我所知,在这里,Docker应该使用缓存,直到RUN命令的签名发生变化。谢谢!
2个回答

9

顺便说一下,我找到了答案:) Dockerfile 本身没问题,但我通过 docker history 找到了一个问题。该命令会显示由 Docker 执行的真实 shell 命令。

问题在于 ARG BUILD_VERSION 会导致 docker 在每次运行命令时添加环境变量,如 /bin/sh -c "ARG=123 ./sbt ..."。这将导致每当参数更改时都有不同的调用签名和哈希值,因此不会从缓存应用运行命令。要解决此问题,只需将 ARG 移动到第一个需要它的 RUN 命令处。

FROM openjdk:8 as workspace

WORKDIR /build

COPY ./sbt ./sbt
COPY ./sbt-dist ./sbt-dist
COPY ./build.sbt ./build.sbt
COPY ./project/build.properties ./project/build.properties
COPY ./project/plugins.sbt ./project/plugins.sbt

RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy update

COPY ./ ./

# Embedded postgres need to be run as non-root user
RUN useradd -ms /bin/bash runner
RUN chown -R runner /build
USER runner

RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy clean test

ARG BUILD_VERSION
RUN ./sbt -sbt-dir ./sbt-dir -ivy ./ivy docker:stage -Ddocker.image.version="${BUILD_VERSION}"

4
我真的希望有一种方法可以显示或记录为什么正在使用缓存或不使用缓存。当我需要防止缓存时,我已经非常擅长了,但是我经常希望它可以缓存以提高性能,而且我知道在停止缓存的步骤中没有任何更改,但它却没有缓存。我确保将ARG命令尽可能晚地放置 - 在最后或在需要之前的右侧。 - nroose

0

正如 Docker 构建输出所示,构建在以下阶段停止使用缓存:

4:32:35 PMStep 4/22 : ADD ./sbt ./sbt
4:32:36 PM---> 7a9e21819cea

这是因为主机上的./sbt文件夹发生了变化,因此docker将重新运行ADD指令。从那时起,docker将不再使用缓存,因为基础层已被使无效,导致其后面的层也被使无效。
更改可能在目录内容中,但可能只是时间戳等微不足道的东西。
作为最佳实践,为了利用构建缓存机制,您应该将经常更改的指令延迟到Dockerfile的底部。一般来说,向docker镜像添加源代码是这些步骤之一,这些步骤在构建之间非常频繁地更改。

抱歉,我复制了错误的日志输出。请再看一遍。 - fragsalat

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