Docker 映像缓存何时失效



也许我的Google Foo不够强大,但我找不到关于缓存中Docker映像何时失效的明确列表。具体来说,我至少对以下情况感兴趣:

  • 由于 mtime 更改与校验和更改而失效。哪个适用于何时?它可以处理不同的源路径(例如,不同目录中存储库的克隆)吗?
  • 由于更新了基本映像而失效。在什么时候,例如 Debian 的(安全)更新会向我冒泡?
  • 是否有任何显式 API 可供持续集成工具用来告诉 Docker 哪些缓存的映像可以重用,哪些不能(例如,由于wget foo.com/latest.gz)?
从 Docker

1.8 开始,Docker 不再使用 mtime 使缓存失效(这在此拉取请求 #12031 中发生了变化)。

构建映像时;

  • 对于本地内容(ADD myfiles /somewhere/COPY myfiles /somewhere),docker 使用校验和更改使缓存失效
  • 始终下载远程内容 ( ADD http://example.com/foobar /somewhere ),但构建缓存会根据校验和更改而失效
  • RUN指令(如wget foo.com/latest.gz)永远不会使缓存失效,除非指令被更改;即缓存基于指令中的文本。如果需要可重现的版本,请确保这些 URL 指向特定版本 ( wget http://example.com/package-major.minor.patch.gz

Docker 1.9 引入了对构建时参数的支持,使您能够传递可在 Dockerfile 中使用的变量,这样您就不必编辑 Dockerfile 来破坏缓存或安装不同版本的包。

例如

FROM foobar
ARG MAJOR=1
ARG MINOR=0
ARG PATCH=0
ADD http://example.com/package-$MAJOR.$MINOR.$PATCH.gz /

默认情况下将添加 http://example.com/package-1.0.0.gz,但是,传递"主要"、"次要"或"补丁"构建时参数可以覆盖要下载的版本,并使缓存无效;

docker build --build-arg MINOR=2 .                                           Sat Jan 16 13:22:40 2016
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu
 ---> 1c9b046c2850
Step 2 : ARG MAJOR=1
 ---> Using cache
 ---> a149d88772ba
Step 3 : ARG MINOR=0
 ---> Using cache
 ---> e3dae0189ffd
Step 4 : ARG PATCH=0
 ---> Using cache
 ---> 678d7ae33054
Step 5 : ADD http://example.com/package-$MAJOR.$MINOR.$PATCH.gz /
Get http://example.com/package-1.2.0.gz: dial tcp 127.0.0.1:80: getsockopt: connection refused
有关构建缓存

的更多信息,请阅读文档中的构建缓存部分。

在什么时候,例如 Debian 的(安全)更新会向我冒泡?

Docker 不会自动下载更新的映像,也不会更新基于这些映像的映像。但是,如果docker pull yourbaseimage ,并且下载了较新的映像,则基于该映像的生成缓存将失效,因此下一个生成将不使用缓存。

对于 Docker 中心上的自动生成,可以确保在更新基础映像时自动重新生成映像,请参阅有关自动生成的文档

TL;DR

关于@thaJeztah的答案,我已经尝试过了。而--no-cache不仅强制当前阶段重建,还强制任何从属阶段完全重建。这不是我们想要的。

但是有一种方法可以仅在特定阶段强制失效:使用命名ARG并且不要在Dockerfile中使用它。并将其作为--build-arg传递给docker build.

这将创建一个"不同的层",因此使后面的任何内容无效。

理由

这是我的最终 dockerfile 的摘录,分为 4 个阶段:

  • repo-sources-base => 下载我的库的基础 - 不要重建
  • repo-sources => 我的库 - 重建
  • base => Ubuntu 与我的 apache、php 等 - 不要重建
  • production => 我的项目和库的基础,用于生产服务器 - 重建

看这里:

#===========================================================================#
# Stage `repo-sources-base`                                                 #
# -------------------------                                                 #
# This image installs git so we don't have to install git it each time we   #
# rebuild the repo-sources.                                                 #
#===========================================================================#    
FROM ubuntu:20.04 AS repo-sources-base
# Install git.
RUN 
    apt-get update && 
    apt-get install -y git && 
    :
# Scan the gitlab host key.
RUN 
    touch /root/.ssh/known_hosts && 
    ssh-keyscan gitlab.com >> /root/.ssh/known_hosts && 
    :

#===========================================================================#
# Stage `repo-sources`                                                      #
# --------------------                                                      #
# This image contains the SSH private keys to make the clone of the         #
# source code from gitlab. This key is passed via ARG to avoid hardcoding   #
# it here inside.                                                           #
#===========================================================================#
FROM repo-sources-base AS repo-sources
# NOTE THIS ARG!!!!!!!
# Not used anywhere... just declared.
# But invalidates the docker build cache on purpose!
# See the answer text for explanation.
ARG INVALIDATE_CACHE_TIMESTAMP="0000-00-00T00:00:00.000000Z"
# Inspired here https://vsupalov.com/build-docker-image-clone-private-repo-ssh-key/    
# Add credentials.
ARG SSH_PRIVATE_KEY
RUN 
    mkdir /root/.ssh && 
    echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa && 
    chmod 600 /root/.ssh/id_rsa && 
    :
# Clone the needed repos.
RUN mkdir -p whatever-path/repos
WORKDIR /whatever-path/repos
RUN git clone --quiet git@gitlab.com:my-nice-account/my-nice-project-1.git
RUN git clone --quiet git@gitlab.com:my-nice-account/my-nice-project-2.git maybe_deploy_dir_2
RUN git clone --quiet git@gitlab.com:my-nice-account/my-nice-project-3.git
RUN git clone --quiet git@gitlab.com:my-nice-account/my-nice-project-4.git

#===========================================================================#
# Stage `base`                                                              #
# ------------                                                              #
# This image contains the base operating system to build the production     #
# release on top of it. It is expected to mutate very slowly so we can have #
# the layers pre-cached when building.                                      #
# TODO: Separate this in 2 bases: one for production and the other for      #
#     development or testing, like in here:                                 #
#     https://www.docker.com/blog/advanced-dockerfiles-faster-builds-and-smaller-images-using-buildkit-and-multistage-builds/ #
#===========================================================================#
FROM ubuntu:20.04 AS base
# Install apache, php, yarn or whatever "base production server"
# BUT NOT your source code, just "the base"

#===========================================================================#
# Stage `release`                                                           #
# ---------------                                                           #
# This image will be the one released to pre or prod and configured at      #
# runtime via env-vars like backing-services, databases, etc.               #
#===========================================================================#
FROM base AS release
# Copy the project files
COPY . /whatever-maybe-other-path/repos/app
# Copy the dependency files
COPY --from=repo-sources /whatever-path/repos /whatever-maybe-other-path/repos
# Continue with the "fine-tuning" after copying the source code, like static building, etc.

此处的 4 个目标以 2 个为块分组:

  • repo-sources-baserepo-sources 适用于具有 SSH 密钥的本地下载,这些密钥永远不会被部署,也不会锁定到进入生产的中间层中。
  • baseproduction生成映像以投入生产。

问题是@Perseids所说的:运行docker build只是忽略了缓存存储库的克隆,并且传递--no-cache重建太多。

通过这种 4 目标结构,我们构建"一次"repo-sources-basebase,我们只想重建repo-sourcesproduction .

这里的关键是名为INVALIDATE_CACHE_TIMESTAMPARG

以下是行为:

  • 如果您多次重建--target repo-sources,它会从缓存加载,我们不希望这样。
  • 如果你重建--target repo-sources --no-cache它迫使重建repo-sources-base,我们不希望这样。
  • 如果重建--target repo-sources --build-arg X=Y则仅当Dockerfile中引用 X 并且即使以后不使用 X 时,它才会强制重建。这就是为什么我在ARG行中"命名"INVALIDATE_CACHE_TIMESTAMP

所以做

docker build --target repo-sources --build-arg INVALIDATE_CACHE_TIMESTAMP=first [...]

将建造它。

"再做一次"这个

docker build --target repo-sources --build-arg INVALIDATE_CACHE_TIMESTAMP=first [...]

使用缓存。

像这样更改值

docker build --target repo-sources --build-arg INVALIDATE_CACHE_TIMESTAMP=second [...]

强制重建

现在使用 firstsecond 将使用缓存,但新值将强制重建。

所以我在我的构建脚本中做了什么:

NOW=$(date --utc --iso-8601='ns' | sed 's/,/./' | cut -c 1-26 | sed 's/$/Z/')
docker build --target repo-sources --build-arg INVALIDATE_CACHE_TIMESTAMP=${NOW} [...]
docker build --target production [...]

因此,它"强制"再次构建repo-sources,因此目标production将从缓存中获取base,并从我强制重建的最新缓存版本中获取repo-sources

最新更新