Moby: 新功能请求:有选择地禁用 Dockerfile 中特定 RUN 命令的缓存

创建于 2013-09-24  ·  245评论  ·  资料来源: moby/moby

从#1384 开始讨论:

我知道 -no-cache 将禁用整个 Dockerfile 的缓存。 但是如果我可以禁用特定 RUN 命令的缓存会有用吗? 例如更新存储库或下载远程文件.. 等。据我所知,现在运行 apt-get update 如果缓存实际上不会更新存储库? 这会导致结果与 VM 不同吗?

如果可以禁用 Dockerfile 中特定命令的缓存,那么文件中的后续命令是否会不使用缓存? 或者他们会做一些更智能的事情 - 例如,如果与之前的运行相比,之前的命令产生相同的结果(fs 层),则使用缓存?

arebuilder kinfeature

最有用的评论

Dockerfile 中的 CACHE ON 和 CACHE OFF 怎么样? 每条指令都会影响后续的命令。

所有245条评论

我认为解决这个问题的方法是在 Dockerfile 中找到您想要缓存到的点,并将其标记为一个图像,以便在您未来的 Dockerfile 的FROM ,然后可以用-no-cache构建

但这不会限制轻松地交错缓存和非缓存命令吗?

例如,假设我想从服务器更新我的 repo 和 wget 文件并在两者之间执行一系列步骤 - 例如从 repo 安装软件(可能已更新) - 对下载的文件执行操作(可能已在服务器)等。

理想的方法是在 Dockerfile 中指定 docker 以每次运行特定命令而无需缓存,并且如果没有更改(例如 repo 中没有更新),则仅重用先前的图像。

这不是很有用吗?

Dockerfile 中的 CACHE ON 和 CACHE OFF 怎么样? 每条指令都会影响后续的命令。

是的,我在我的 Dockerfile 中使用git clone命令,如果我想让它重新克隆更新,我需要在行尾添加一条注释以触发重建线。 我不需要为此步骤创建一个全新的基础容器。

容器 ID 可以作为“不缓存超过此 ID”的指令传递给“docker build”吗? 类似于“docker build”将所有步骤缓存到 Dockerfile 中更改行的方式?

我同意我们需要对构建缓存进行更强大和更细粒度的控制。 目前我不确定如何将其公开给用户。

我认为随着即将到来的 API 扩展,特别是命名和自省,这会变得更容易。

将是一个很棒的功能。 目前我正在使用诸如RUN a=a some-command类的愚蠢东西,然后使用RUN a=b some-command来破坏缓存

更好地控制缓存将使使用 CI 中的 docker 更加快乐。

@shykes

--no-cache从 bool 更改为字符串并让它在 docker 中我们想要破坏缓存的位置使用正则表达式怎么样?

docker build --no-cache "apt-get install" .

我同意并在 IRC 上建议了这个确切的功能。

除了我认为为了保持反向兼容性,我们应该创建一个新标志(比如“--uncache”),这样我们就可以将 --cached 保留为解析为“--uncache .*”的(不推荐使用的)bool 标志

2014 年 2 月 7 日星期五上午 9:17,Michael Crosby通知@github.com
写道:

@shykes
--no-cache从 bool 更改为字符串并让它在 docker 中我们想要破坏缓存的位置使用正则表达式怎么样?

docker build --no-cache "apt-get install" .

直接回复本邮件或在 GitHub 上查看:
https://github.com/dotcloud/docker/issues/1996#issuecomment -34474686

其他人对此有何看法? 有人准备实施该功能吗?

如果没有其他人开始,我今天准备尝试实施这个吗?

我已经开始研究它 - 想验证该方法看起来不错。

  • buildfilenoCache字段变为*regexp.Regexp

    • 那里的nil值意味着utilizeCache = true曾经使用过的值。

  • 将字符串传递给docker build --no-cache现在会将验证正则表达式字符串发送到服务器。
  • 仅调用--no-cache导致默认值.*
  • 然后在新方法buildfile.utilizeCache(cmd []string) bool使用正则表达式来检查忽略缓存的命令

一件事:据我所知,flag/mflag 包不支持没有值的字符串标志,所以我需要做一些额外的调整来支持--no-cache--no-cache some-regex

我真的认为这应该是一个单独的新标志。 --no-cache的行为和语法已经被很多不同的人在很多很多地方很好地定义和使用。 我会投票给--break-cache或类似的东西,并让--no-cache完全按照它今天所做的去做(因为这是许多人依赖并且仍然想要的非常有用的行为)。

无论如何,IANTM(我不是维护者)所以这些只是我个人的想法。 :)

@tianon --no-cache当前是 bool,所以这只是扩展了现有的行为。

  • docker build --no-cache - 与之前相同的行为:忽略缓存
  • docker build --no-cache someRegex - 忽略与someRegex匹配的任何RUNADD命令

没错,这一切都好。 问题是--no-cache是一个布尔值,所以现有的行为实际上是:

  • --no-cache=true - 明确禁用缓存
  • --no-cache=false - 显式启用缓存
  • --no-cache - --no-cache=true简写

我还认为,通过制作“真”和“假”特殊情况正则表达式字符串来解决这个问题,我们会对自己造成伤害,因为这将在未来为我们的用户创造潜在的令人惊讶的行为。 (“当我使用带有 'true' 或 'false' 正则表达式的--no-cache时,它无法正常工作!”)

@tianon是的,你是对的。 快速浏览一下,人们正在使用 =true/false。

很高兴按照您的建议修改PR以添加新标志,维护者怎么看( @crosbymichael ,@shykes)? 这也意味着我可以删除添加到 mflag 的代码以允许字符串/布尔标志。

+1 @wagerlabs方法

@crosbymichael , @timruffles如果 Dockerfile 的作者决定应该缓存哪个构建步骤,哪个不应该缓存,不是更好吗? 创建 Dockerfile 的人不一定是构建镜像的人。 将决定转移到 docker build 命令需要只想使用特定 Dockerfile 的人的详细知识。

考虑一个公司环境,有人只想重建现有的映像层次结构以更新某些依赖项。 现有的 Dockerfile 树可能是其他人几年前创建的。

+1 @wagerlabs方法

+1 @wagerlabs方法虽然如果有一种方法也可以在时间间隔内缓存胸围会更好,例如

CACHE [interval | OFF]
RUN apt-get update
CACHE ON

我很欣赏这可能与容器具有不确定性的想法背道而驰,但这正是您在管道具有良好自动化测试的持续部署场景中想要做的事情。

作为一种解决方法,我目前正在脚本中生成缓存破坏者,用于运行 docker build 并将它们添加到 dockerfile 中以强制缓存破坏

FROM ubuntu:13.10
ADD ./files/cachebusters/per-day /root/cachebuster
...
ADD ./files/cachebusters/per-build /root/cachebuster
RUN git clone [email protected]:cressie176/my-project.git /root/my-project

我希望使用容器进行持续集成,并且能够在缓存中的特定元素上设置超时将非常有价值。 没有这个我就无法部署。 每次都强制完全重建太慢了。

我目前解决这个问题的计划是动态注入诸如RUN echo 2014-04-17-00:15:00 ,并将生成的行向下舍入到最后 15 分钟,以便在舍入后的数字跳跃时使缓存元素无效。 ala 每 15 分钟一次。 这对我有用,因为我每次都有一个生成 dockerfile 的脚本,但是没有该脚本它就无法工作。

+1 为该功能。

我也想为这个功能投票。 从仅在主分支上更新的 git 存储库构建容器的一部分时,缓存很烦人。
:+1:

@hirorotagonist在您的ENTRYPOINT git pull ENTRYPOINT可能会有所帮助吗?

@amarnus我已经解决了类似于@tfoote的想法。 我正在从 jenkins 作业运行构建,而不是直接运行 docker build 命令,作业启动构建 skript,它从模板生成 Dockerfile 并在 git 命令上方添加“RUN echo currentsMillies”行。 感谢 sed 和管道,这只是几分钟的事情。 无论如何,我仍然喜欢将此功能作为 Dockerfile 本身的一部分。

@wagerlabs方法添加我的 +1。 CI也有这个问题。 我暂时只是使用动态 echo RUN 语句,但我会喜欢这个功能。

+1 用于缓存开/关。 我的用例也是 CI 自动化。

+1,尤其是能够像@cressie176的示例一样设置运行命令缓存间隔

“例如更新存储库或下载远程文件”

+1

如果它对任何人有帮助,这是我在 Jenkins 构建中使用的一段代码:

echo "Using build $BUILD_NUMBER for docker cachebusting"
sed -i s/cachebust_[0-9]*/cachebust_"$BUILD_NUMBER"/g Dockerfile

+1 缓存开/关

作为 CACHE ON/OFF 方法的可能替代方案,像“ALWAYS”这样的额外关键字怎么样。 该关键字将与现有命令(例如“ALWAYS RUN”或“ALWAYS ADD”)结合使用。 按照设计,“ALWAYS”关键字不会进入缓存来完成相邻的命令。 但是,它会将结果与 CACHE(隐式缓存其他时间执行同一行)进行比较,如果 ALWAYS 命令的结果未更改,则链接到缓存的图像。

我相信潜在的需求是识别“非幂等指令” 。 ALWAYS 命令非常明确地执行此操作。 我的印象是 CACHE ON/OFF 方法可以同样有效地工作,但也可能需要大量切换代码块(这可能会鼓励用户阻止比实际需要的更多的行)。

我也更喜欢命令的前缀,例如 ALWAYS 或 CACHE 1 WEEK ADD ...

所以我在这个问题上挣扎了一段时间,我只是想分享我的工作,以防它在解决这个问题时有所帮助。 我真的不想在 docker 文件之外添加任何内容到构建调用或每次更改文件。 无论如何,这是一个愚蠢的例子,但它使用添加机制来破坏缓存并且不需要任何文件操作。

From ubuntu:14.04

RUN apt-get -yqq update
RUN apt-get -yqq install git
RUN git clone https://github.com/coreos/fleet
ADD http://www.random.org/strings/?num=10&len=8&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new uuid
RUN cd fleet && git pull

显然,您可以选择自己的用例和网络随机生成。 无论如何,也许它会帮助一些人摆脱 idk。

@wagerlabs方法的另一个 +1

该功能的另一个+1。 同时使用@cruisibesarescondev解决方法。

对于功能请求,还有一个 +1。 并感谢@cruisibesarescondev的解决方法

该功能的另一个+1。

为解决方法干杯@cruisibesarescondev

我认为 ALWAYS 关键字是一个很好的方法,特别是因为它具有简单清晰的语义。 稍微复杂一点的方法是增加最短时间(在诸如构建农场或持续集成之类的事情中很有用)。 为此,我建议使用一种语法“EVERY XXX”,其中 XXX 是超时。 如果自从构建该命令的缓存以来它比 XXX 长,它必须重新运行该命令。 并检查输出是否已更改。 如果没有变化重用缓存的结果,注意最后更新的时间。 这意味着每个 0 将与 ALWAYS 相同。

对于目前的解决方法,我使用 python 中的 empy 模板生成了我的 Dockerfile,我嵌入了以下代码片段,这些代码片段的工作原理与上述相同,只是在两次连续运行中没有检测到相同的结果,但确实每 XXX 秒强制重新触发一次。 在顶部:

@{
import time
def cache_buster(seconds):
    ts = time.time()
    return ts - ts % seconds
}@

我想强制重新运行的地方:

RUN echo @(cache_buster(60))

在 Dockerfile 中看起来像这样

RUN echo 1407705360.0

如您所见,它四舍五入到最接近的 60,因此每次 60 秒过去后,下一次运行将重新运行以下所有命令。

+1 表示 ALWAYS 语法。 +.5 用于缓存开启/缓存关闭。

+1 表示 ALWAYS 语法。

是的,ALWAYS 语法看起来非常直观。

我不喜欢 CACHE ON/OFF 因为我认为行应该是“自包含的”并且向 Dockerfiles 添加块会引入很多“麻烦”(比如在合并时必须检查“这条线是否被缓存覆盖?”。) .)

@kuon我认为已经有许多命令会影响后续指令,例如USERWORKDIR

是的,这是真的,但我不会出于同样的原因使用它们。 我总是做RUN cd ... &&RUN su -c ...&&

我更喜欢块符号:

CACHE OFF {
    RUN ...
}

这更明确,避免错误插入CACHE OFF行(它会触发语法错误)。

我可能多虑了,Dockerfiles 实际上并没有在生产中运行(只是在构建映像时),因此在构建时禁用缓存实际上不会造成太大伤害。 但我也觉得 Dockerfiles 真的很有限(必须在一次 RUN 中用 && 链接所有命令以避免创建大量图像,无法使用变量......)。

也许这个问题是一个新的 Dockerfile 格式的机会。

我想回到我刚才说的话。 我在另一个问题https://github.com/docker/docker/pull/2266 中阅读了@shykes所说的内容,我也同意他的观点(Dockerfile 需要保持一种非常简单的汇编语言)。

我说我想要变量或类似的东西,但可以用其他语言覆盖,但在这种情况下,Dockerfile 中的每一行都应该是自包含的,例如:

NOIMAGE ALWAYS RUN USER:jon  apt-get update

它将始终运行命令(无缓存),但也不会创建图像并使用用户 jon。

这种自包含的行容易从任何其他语言生成。 如果您必须担心上下文(用户、缓存、工作目录),则更容易出错。

请为方便起见,它可以是RUN!吗?

这个有任何状态更新吗?

有选择地禁用缓存将非常有用。 我通过 awscli 命令(来自 amazon AWS 工具包)从远程 amazon s3 存储库中获取文件,并且我没有简单的方法通过 ADD 命令破坏缓存(至少我想不出不编辑 Dockerfile 的方法触发它)。 我相信很有可能在使用 RUN 时将控制权交还给用户以选择性地破坏缓存。 如果有人对我有建议,我很高兴收到你的来信。

想把这个问题稍微提一下,因为这是我们非常需要的东西。

仍然相信ALWAYS语法是理想的语法。

一个简单的BREAK语句怎么样。

@cpuguy83也适用于我的特定用例。

我不确定在技术上是否可以只缓存一个命令,但缓存其余的命令。 可能不是,因为 docker 是基于增量差异的。

根据@CheRuisiBesares 的建议,支持BREAK会让我的功能与我当前的解决方法相同。

关于我之前的帖子,从脚本中的那个点开始破坏缓存确实就足够了,剩下的就是智能脚本设计(我相信这将满足大多数人的要求)。 这是可行的,而不是有选择地禁用缓存破坏吗?

@orrery您可能可以通过添加COPY _before_ 构建步骤来“破坏”缓存。 如果复制的文件不同,则此后的所有步骤都不应再使用缓存(请参阅此部分)。 肮脏的伎俩,但可能会解决您的问题。

ALWAYS (或类似的概念,如EVERY # DAYS )的关键是附加命令后的缓存比较。 对于我自己(我假设还有很多其他人),目标不是破坏缓存本身。

  • 目标是确保我们在命令结果(即“升级到最新版本”)发生变化时停止使用缓存。
  • 相比之下,如果结果与缓存版本匹配,我们希望利用缓存。

这解决了@hellais的评论,因为您可以为后续命令利用缓存……当且仅当ALWAYS的输出与缓存版本匹配时(大多数情况下这很容易)。

自然地,相同的逻辑_可以_包含在 CACHE ON/OFF 模型中。 与缓存的比较可能比重新运行所有后续命令便宜,但仍然可能很昂贵。 如果 CACHE ON/OFF 块鼓励用户在 OFF 块中包含额外的命令( ALWAYS不会发生这种情况),它可能会导致性能的显着差异。

我的情况与@tfoote 完全相同:我将 Docker 用于 CI,需要强制缓存过期。

+1 表示EVERY语法。 ALWAYS语法也可以完成工作。

@claytondaley这是一个很好的观点。 但是,具有完全禁用命令缓存的能力仍然很重要。 总有一些隐藏状态对 Docker 来说本质上是不可见的。 例如,执行命令可能会改变远程服务器上的状态。

@mkoval ,您提出了一个关于_creating _ hidden states 作为使用ALWAYS的重要时间的好点,但我认为它不会影响我关于恢复缓存的逻辑。 为了使示例具体(如果有点琐碎),升级第 3 方系统的命令:

  • 创建一个隐藏状态(需要运行ALWAYS )和
  • 不改变当前容器

如果下一个命令不涉及隐藏状态(简单地说,容器上的 mv 命令),缓存将是 100% 可靠的。 相同的容器,相同的命令,不依赖于隐藏信息。

如果下一个命令(或任何后续命令)涉及隐藏信息,那么它应该使用ALWAYS关键字,只有在生成的容器与缓存匹配时才恢复缓存。

@claytondaley你的解决方案对我来说似乎非常优雅和高效。 如果这能被实施,我将不胜感激。 :+1: :章鱼:

使用 ALWAYS 和 EVERY X 建议语法为此功能 +1。 CACHE ON/OFF 对我来说有点笨拙,但我会使用它。 我也非常喜欢@claytondaley的建议,即在可能的情况下恢复缓存。

+1 表示 ALWAYS 语法。 特别是对于从 git repo 拉取代码。

+1 对于这些解决方案中的任何一个。

我有点困惑。 缓存关闭后如何重新打开? 一旦您将其关闭并在容器中进行任何类型的更改,重新打开缓存是否不会基本上消除缓存关闭时运行的那些 Dockerfile 命令所做的任何更改? 我认为我们可以进行缓存的全部原因是因为我们确切地知道之前运行的命令的完整列表,并且可以保证容器中的内容完全相同。 如果您关闭缓存(我正在谈论它的查找方面),那不会破坏保证吗? 或者这只是不填充缓存?

我对这些建议的理解是,您可以将“ALWAYS”指定为 Dockerfile 命令的一部分,以始终再次运行该步骤。 例如,“RUN ALWAYS git clone https://example.com/myrepo.git ”将始终运行(从而始终克隆存储库)。 然后@claytondaley建议的是,在再次运行此命令后,Docker 会根据缓存检查更改。 如果校验和相同(即如果克隆的 repo 没有变化,所以最新的层与缓存中的同一层相同),我们可以继续缓存。 您是对的,一旦缓存失效,之后的所有步骤都无法使用缓存。 这些建议只是可以更精细地控制何时使用缓存,并且在可能的情况下从缓存恢复也很聪明。

@curtiszimmerman... 正是

@duglin ... 如果我们使用数学代理,这个想法可能会更明显。 缓存(在此上下文中)只是action B应用于state A的结果的内存,因此您不必重新处理它。 假设,我运行了一系列命令:

  • 6开头
  • 始终运行* x ,其中x是从 git repo 下载的(因此可能会更改)
  • 运行+ 12

我第一次运行命令时, x是 8,所以我得到(并缓存)以下序列:

  • 6
  • 48 (作为* x应用于6
  • 60 (作为+ 12应用于48

如果我的机器再次达到状态48 (按任何顺序)...并收到命令+ 12 ,我就不必再次进行处理。 我的缓存知道这个命令的结果是60

困难的部分是弄清楚您何时再次处于相同状态 ( 48 )。

  • 理论上我们可以将每个命令后的机器与每个其他缓存图像进行比较,但这是资源密集型的,并且找到匹配的几率非常低。
  • 我的建议是保持简单。 每次我们处于状态(例如6 )并点击命令(例如* x )时,我们都会将结果与上次(或多次)处于同一状态的缓存进行比较运行相同的命令。 如果此过程后机器的状态相同(例如仍然48 ),我们将恢复缓存。 如果下一个命令仍然是+ 12 ,我们从缓存中提取结果而不是处理它。

@claytondaley,但我不明白您如何确定当前状态。 正如您所说,我们不会比较容器中的所有文件。 缓存现在的工作方式基本上只是 strcmp 我们要针对当前容器中的所有已知子容器运行的下一个命令。 在您跳过流程中的容器的那一刻,我不知道您如何假设您当前的容器就像任何其他缓存容器一样,无需检查文件系统中的所有文件。 但也许我不明白你在做什么。

让我重新表述一下......给定一个随机容器(如果你不使用缓存,这基本上就是你所拥有的)你怎么能在缓存中找到一个匹配它的容器,而无需比较缓存中的所有文件容器?

@claytondaley @duglin正如您所描述的

就个人而言,如果我所拥有的只是确保命令始终运行的能力,我会非常高兴。 以 Dockerfile 为例:

RUN install_stuff_take_forever
RUN always_do_it   #will not run every time
RUN more_stuff

目前, always_do_it行只会在第一次运行,除非我编辑文本以强制缓存破坏。 我认为我们大多数人都会乐于接受more_stuff有时会不必要地运行(当always_do_it没有改变时,如果作为交换,我们可以保留install_stuff_take_forever的缓存。

RUN install_stuff_take_forever
NOCACHE
RUN always_do_it
RUN more_stuff

@pikeas我完全得到了一个 NOCACHE 命令,这很容易做到。 我没有得到一个命令,它可以在没有差异/散列/无论整个文件系统的情况下重新打开它。

我阅读了Docker

  • Docker 为每个命令创建一个“层”。
  • 该层仅包括由该命令更改(或可能“保存”,无论更改或未更改)的文件。
  • 文件系统的当前状态是通过按顺序检查每一层直到找到该特定文件的(最近更新的)版本在逻辑上(如果不是在操作上)实现的。

在这种情况下,比较同一命令的两个实例的成本相对较低。 您只需要比较顶层(因为每个底层都是共享的)。 有一个由命令更改的特定文件列表。 只有那些文件包含在图层中。 当然……您需要比较该层中的所有文件……但不是整个文件系统。

也可以(虽然不能保证是详尽无遗的)只将新层与上次运行命令的时间进行比较:

  • 在大多数情况下(git pull 或软件升级),当前版本要么是 (1) 与上一个版本相同,要么是 (2) 一个新版本……但绝不会——至少很少——回滚到以前的版本版本。
  • 在极少数情况下(例如升级到 dev-master 然后恢复到稳定版本),可以切换回旧版本。 但是,这些非常罕见,因此大多数人可能最好(经常)只检查最新版本并在他们回滚的罕见情况下重新运行命令。

当然,您也可以对所有以前的版本进行哈希检查……然后是完整的文件检查……以提供完全支持而不会产生开销。

如果您查看https://github.com/docker/docker/pull/9934的底部,您会看到有关 Dockerfile 命令支持选项的讨论。 如果所有(甚至只是 RUN)都有一个 --no-cache 选项可用,这意味着该命令“不使用缓存”怎么办? 例如:
运行 --no-cache apt-get install -y my-favorite-tool
然后这也会自动禁用剩余命令的缓存(我认为)。
这能解决问题吗?

在语义相同的“RUN ALWAYS”和“RUN --no-cache”之间,我个人更喜欢看起来更自然的“RUN ALWAYS”语法。 我同意关于该 PR 的最后一条评论:我认为 --option 破坏了可读性并使 Dockerfiles 变得丑陋。 此外,我认为 Dockerfile 命令需要与跟随它们的实际命令非常不同。 想象一下像“RUN --no-cache myapp --enable-cache”这样的复杂语法示例,它会开始用这种选项快速表达自己。

@curtiszimmerman你的例子对我来说很清楚。 --no-cache 用于 RUN 而 --enable-cache 用于 myapp。 安置很重要。 例如,看看:
docker run -ti ubuntu ls -la
人们明白 -ti 代表“run”,而“-la”代表“ls”。 这是人们似乎习惯的语法。
像 RUN ALWAYS 这样的问题之一是可扩展性。 我们需要一种适用于所有 Dockerfile 命令并支持传入值的语法。 例如,人们表示有兴趣为某些命令指定 USER。
运行用户=foo myapp
从技术上讲,在 myapp 的 shell 中将 env var USER 设置为“foo”。 所以我们在这里是模棱两可的。
而: RUN --user=foo myapp 没有这个问题。
是:RUN var=foo myapp
试图设置和名为“var”的 env var 还是试图获得一些 RUN 选项的错字?
IOW,当我们开始时,与现有的有效命令 IMO 重叠的可能性要小得多——而不仅仅是允许一个词出现在那里

我实际上提倡相反的顺序, EVERY <options> COMMAND 。 几个原因:

  • 在“用户”和“缓存”(至少)的情况下,它们实际上是可以包装任何命令的环境特征(尽管它们可能不会对其他命令产生实质性影响)。
  • 语法RUN ALWAYS意味着更改RUN命令解释器,这听起来没有必要。
  • 这个问题对于RUN EVERY 5 days来说更糟,因为附加到 EVERY 的选项会造成更多的歧义。 EVERY 5 days RUN很清楚选项影响的命令。 我们对RUN USER usrUSER usr RUN有同样的问题。 只要 (1) 命令关键字永远不是选项或 (2) 有一种简单的方法来逃避它们,这就是明确的。

我可以在命令前加上它们的选项( ALWAYS AS user RUN ... )。 我真的很担心使用 GNU 风格的 longopts 作为选项,因为它们与旧的或呆滞的眼睛并不是很分开。 我可以想象自己在 20 小时后盯着一个复杂的 Dockerfile 命令,想知道 wtf 是怎么回事。 但我预测 --options 无论如何都会发生。

但我预测 --options 无论如何都会发生。

相反,什么都没有决定; 语法@duglin是在暗示是_counter proposal_该提议/决定早语法。 请阅读 #9934 以获取更多信息。

此外, @duglin _not_做出该决定的人(至少,不是一个人)。 您提出的一些要点已在另一个线程中提到。

我同意您对可读性的担忧,但也认为如果必须指定多个选项,建议的其他语法可能存在相同的问题。

这个问题可以通过格式化 Dockerfile 以提高可读性来解决。 我认为编写更多示例来测试/检查在格式正确时可读性是否是一个问题会很好。

而且,是的,欢迎您对此提出意见。

对于让 Dockerfile 本身定义缓存的位置,我仍然非常-1
应该和不应该被应用。 我还没有看到一个很好的例子
Dockerfile 无法适当地重写为 cache-bust 和
自然是在底层资源需要更新时。

在“docker build”上有一个标志以在特定位置停止缓存
会更灵活,IMO(并将缓存的控制权放回
到无论如何都要管理该缓存的系统操作员的手中)。

@tianon的 -1 上 +1(所以这是一个 -1!),并且在第 N 步添加一个中断标志似乎是合理的。 考虑到一旦缓存损坏,Dockerfile 的其余部分无论如何都会损坏,我认为这是有道理的。

这样做的主要需要是因为 docker 的缓存机制直接与图像的存储和传输相关联,这使得缓存有效,但需要权衡大得多的图像。 所以让我们解决这个问题!

不说我对这个功能的看法 - 说实话还不确定 - 你们如何想象有人说(来自“docker build”)在第 N 步停止? 当今天第 N 步明天将是第 N+1 步时,这似乎有点脆弱。
似乎我们可能需要一种在 Dockerfile 中添加某种“标签”的方法,以便人们可以从构建 cmd 行中引用该标签。
如果我们有那个,那么我不确定我是否看到它与添加出现在 Dockerfile 中的“STOP-CACHING”命令之间有很大区别。
每次都会破坏缓存的 Dockerfile cmd 的一个很好的例子是什么?

嗯,这就是为什么最初讨论它是为了使它成为一个
基于行内容的正则表达式,我也可以(尤其是因为
编写脚本比确切知道您的步骤编号要容易得多
不想缓存——我不可能写当前的完整副本
Bash 中的 Dockerfile 解析器,谢谢:D)。

Tianon Gravi [email protected]写道:

嗯,这就是为什么最初讨论它是为了使它成为一个
基于行内容的正则表达式,我也可以(尤其是
自从
编写脚本比确切知道哪个步骤编号要容易得多

不想缓存——我不可能写当前的完整副本
Bash 中的 Dockerfile 解析器,谢谢:D)。

想重申我之前的建议,即 ALWAYS/cache-breaking
“运行”应该只是“运行!” 保持 1 字命令结构(?)。

为了在特定步骤上破坏缓存,必须编辑 Dockerfile(通过添加基本上随机的内容,因为它是一个占位符)似乎很笨拙。 我会使用始终运行某个步骤的docker build CLI 选项,但完全同意@duglin 的观点,即必须跟踪特定行号才能将其提供给命令是笨拙的。 我不想在git clone之前立即向 Dockerfile 添加一些随机字符 (!) 的额外步骤,只是为了促使 Docker 实际克隆 repo 而不是从缓存中工作。

@curtiszimmerman我建议 (!) 因为它表示类似于英语的紧迫性。 (“你应该做这个!”)

我认为 Dockerfile 至少是一个合适的地方来定义哪些命令应该是不可/缓存的。 必须使用“--no-cache=git”进行构建(我意识到这不是您建议的内容,但您没有建议我引用/比较任何内容)似乎更笨拙。

为什么关注RUN? 为什么不允许缓存因任何命令而被破坏?
似乎添加了一个:
缓存
Dockerfile 命令的类型会灵活得多。 为了真正增加灵活性,它可以选择允许一个标志:
BUST-CACHE $doit
它仅适用于 $doit 已定义的情况 - 那么如果我们在构建时添加对 -e 选项的支持(https://github.com/docker/docker/pull/9176),那么人们可以这样做:
docker build -e doit=true ...

@zamabe哦,我完全会使用RUN! ,抱歉。 在这里,我使用 (!) 说“这很不寻常!” 关于每次我想在特定步骤上破坏缓存时编辑 Dockerfile。 我可以在特定步骤之前破坏 Dockerfile 中的缓存的任何方式都是有用的(并且为了额外的胜利,如果该缓存破坏命令之后的步骤与缓存中的结果相同,那么聪明到可以从缓存中继续)。 BUST-CACHEALWAYS RUN (或RUN ALWAYS )或RUN! ... 真的任何支持此功能的机制,我都会使用它。

@duglin对不起? 错误标题说 RUN 作为一个例子更容易给出。

@curtiszimmerman啊。

作为旁白; 我认为缓存重新验证(?)有点超出了这个错误正在寻找的缓存失效。 虽然我喜欢你的建议,但我只是重新排列我的 Dockerfile 以将缓存破坏命令尽可能地放在最后。 这抵消了从 _possible_ 缓存命中获得的好处,因为您_总是_进行必要的计算/比较,这可能比正常完成 Dockerfile 构建更重,因为使用缓存破坏的人可能希望/期待缓存未命中。

@zamabe同意。 我建议,如果实现是相当简单的,也许是一个特殊的命令从缓存继续,它与缓存破坏标识符是分开的。 类似于DISABLE-CACHE在某个时刻禁用缓存,如果您有一个用例,其中 Dockerfile 的其余部分与从缓存中继续相比会很昂贵,那么像DISABLE-CACHE?这样的东西会如果可能,从缓存继续。 这不是一个建议,只是一个演示来传达我在说什么。

+1 用于从 git repo 中提取代码

+1

这将是巨大的! 现在,我有一部分持续集成将 git commit 哈希写入 Dockerfile(覆盖占位符),只是为了破坏 git 克隆的缓存。

我提交了这个 PR: https :
虽然它不支持重新打开缓存,但我认为这在今天是不可能的。

+1

我在 Dockerfile 中生成一个随机数,它被缓存......
+1 表示 NOCACHERUN 指令

+1
对于我们每次需要在不重建所有内容的情况下执行的某些 RUN 操作来说应该非常有用

我一直注意到git clone会命中缓存,但go get -d不会。 任何想法为什么?

_集体评论@LK4D4 @calavera @jfrazelle @crosbymichael @tiborvass_

关闭它,因为我们没有看到很多现实世界的用例(有关更多详细信息,请参阅相关的 #10682)。

+1 表示运行。 会好的。

+1

docker 1.9 引入了构建时变量; 有可能(错误地)使用它们来强制破坏缓存; 有关更多信息,请参阅https://github.com/docker/docker/issues/15182

这怎么还不是功能?

@hacksaw6你可以看看这里说的是什么: https :

+1

+1

+1 这怎么还不是一回事!!!???!

+1 我们需要此功能来为构建提供更精细的控制。

+1

+1

+1

+1

+1 非常有用
(现在使用@timruffles解决方法)

+1

+1

+1

人们发布他们的用例而不是 +1 可能很有用,这样人们就可以明白为什么这只是一个需要的功能。

+1,通过 Google 到达这里,寻找缓存的 git 克隆的解决方案。

我的用例:
我有一个 docker 配置,它在构建过程中将通过 gradle 调用干运行模式下的 groovy 微服务应用程序。 这将导致所有依赖的 Java 库(来自远程 mvn 存储库)都将下载到本地 docker mvn 存储库中。 试运行将只运行应用程序并立即返回,但确保加载所有 Java 库依赖项。
在 docker run 阶段,将通过 gradle --offline 模式执行相同的应用程序。 即微服务应用程序将仅从本地 mvn 存储库目录加载。 不会发生昂贵、耗时的远程库获取。 当我现在发布此类库的新快照版本时,除非我修改 docker 目录,否则 docker 在构建期间不会触发远程获取(即他不会调用我的 gradle dryrun cmd)。

我的用例:获取要在图像上使用的库的最新 3rd 方版本。 我正在使用 docker hub 和 AFAIK,它不会缓存任何东西。 但谁知道什么时候可能会改变。
如果docker中有NOCACHE这样的命令标志,那么无论镜像是在哪里构建的,都可以保证这一点。

依赖构建系统“功能”比依赖最新版本更糟糕,恕我直言。

添加新语法如何: FORCE RUN git clone ....

现在我正在使用RUN _=$(date) git clone ...使缓存无效。

@c9s真的有效吗? 我认为不会。

@duglin设置环境变量对我

@c9s我不知道设置 env var 是如何工作的,因为这是由容器的 shell 完成的,而不是 Dockerfile 处理。 当我尝试RUN _=$(date) echo hi它在第二次构建时使用缓存。

@duglin你说得对:| 它不会使缓存无效

@c9s试试这个

FROM foo
ARG CACHE_DATE=2016-01-01
RUN git clone ...
docker build --build-arg CACHE_DATE=$(date) ....

@thaJeztah谢谢! 有用!

+1 克隆 git repos(用例)

如此多的 +1,如果您在 docker 文件中提取 git repo,缓存会阻止构建您的图像。 使得通过 CI 推动构建变得有点困难。

+1 克隆 git 存储库(每次在 git 存储库中进行小的编辑时都需要从头开始构建图像非常烦人)

@Vingtoft如果您正在更新存储库中的文件,那么您的缓存将失效。

@itsprdp我不知道,谢谢你的澄清。

@itsprdp我刚刚测试过。 当我更新 repo 并构建映像时,Docker 仍在使用缓存。
也许我误解了什么?

@itsprdp根据我的经验,这是不正确的。 我对一个 repo 进行了新的提交以进行测试,当再次构建时,它使用相同的缓存。

如果我在 repo 之前更改 docker 文件,当然它会被缓存破坏,但是简单地更新 repo 似乎并不能解决这个问题。

@RyanHartje很抱歉造成混乱。 如果存储库更新,它应该使缓存无效,这是贡献者需要考虑的事情。
@Vingtoft期望的用例是缓存存储库并仅更新存储库中更改的文件。 这可能实现起来很复杂。

@itsprdp只更新 repo 中更改的文件会很棒,但更少(或者我应该说更多?)也可以。
在我的用例(和许多其他用例)中,实际的 git pull 不需要很长时间:它的构建会扼杀开发流程。

+1,在 git clone 期间使用的缓存:(

一个集成的解决方案会很好,但与此同时,您可以使用ARG在特定的 Dockerfile 指令处破坏缓存。

在 Dockerfile 中:

ARG CACHEBUST=1
RUN git clone https://github.com/octocat/Hello-World.git

在命令行上:

docker build -t your-image --build-arg CACHEBUST=$(date +%s) .

CACHEBUST设置为当前时间意味着它将始终是唯一的,并且不会缓存 Dockerfile 中 ARG 声明之后的指令。 请注意,您也可以在不指定CACHEBUST build-arg 的情况下进行构建,这将导致它使用默认值 1 并保留缓存。 这可用于始终检查 git repos 的新副本、拉取最新的 SNAPSHOT 依赖项等。

编辑:这就是@thaJeztah所说的。 我将把它作为他的解决方案的附加描述。

@shane-axiom 如何使用 git commit hash 作为CACHEBUST

export CACHEBUST=`git ls-remote https://[email protected]/username/myRepo.git | grep refs/heads/develop | cut -f 1` && \
echo $CACHEBUST && \
docker build -t myDockerUser/myDockerImage \
   --build-arg blah=blue \
   --build-arg CACHEBUST=$CACHEBUST \
   .

基于来自http://stackoverflow.com/questions/15677439/how-to-get-latest-git-commit-hash-command#answer -15679887 的线索

@pulkitsinghal这对于破坏 git repos 的缓存看起来很棒。 对于其他用途(例如拉入 SNAPSHOT 依赖项等),始终破坏的时间戳方法效果很好。

+1 为缓存开启 | 离开

+1

+1

请记住@CheRuisiBesares 方法,您始终可以使用ADD https://www.random.org/strings/?num=16&len=16&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new uuid作为缓存问题的解决方法。

要发布额外的用例....

COPY package.json /usr/src/
RUN npm install

在我们的package.json我们通常会master标签。 这意味着我们永远不会真正获得最新的master除非我们更改package.json文件(通常只是将-到描述中,然后在测试时将其删除)。

RUN NO CACHE代替RUN似乎是一个很好的解决方案。

+1

我对 npm install 有类似的问题,它使用缓存并且不在 npm 中使用我新发布的库。

如果我可以在 docker 文件中禁用每个 RUN 命令的缓存,那就太好了。

@brycereynolds @mmobini参见https://github.com/docker/docker/issues/1996#issuecomment -172606763 手动破坏缓存。 但是,_not_ 指定需要安装的特定版本的包可能不是最佳实践,因为不再保证 Dockerfile(和源代码)的最终结果是可重现的(即,它今天成功构建,但不是明天,因为其中一个软件包已更新)。 我可以在开发过程中看到这“没问题”,但对于生产(以及 Docker Hub 上的自动构建),最好的方法是明确指定一个版本。 这样做还允许用户验证用于生成映像的确切包。

我有一个用例,无法使缓存无效会导致问题。 我正在运行来自 Docker 的 Dropwizard 应用程序(使用 Maven 构建的 Java REST 服务),一个自动化系统正在为我完成所有容器的构建和部署。 我在我的 repo 中包含了一个 Dockerfile,剩下的就交给它了。 系统运行我的应用程序的一个生产版本和一个或多个开发版本。 开发版本是我遇到问题的地方。

在开发过程中,项目的某些依赖项在其版本号中包含 SNAPSHOT。 这会指示 Maven 版本正在开发中,它应该在每次构建时降低一个新版本。 因此,相同的文件结构可能会导致两个不同的构建。 这是所需的行为,因为错误可能已在 SNAPSHOT 依赖项中修复。 为了支持这一点,强制 Docker 运行特定命令会很有帮助,因为无法根据文件系统的当前状态确定命令的效果。 大多数 Java 项目都会遇到这种情况,因为 Maven 风格的 SNAPSHOT 依赖项被几个不同的构建系统使用。

@ctrimble您可以使用--no-cache--build-arg使缓存无效。
您可以通过使用包含所有可缓存命令的基本映像来最小化--no-cache的影响。

@cpuguy83感谢您的回复。 我阅读了线程并了解当前的选项。 我已经用我用来提供缓存破坏参数的构建系统打开了一张票。 为单个应用程序生成两个不同的图像似乎需要通过很多步骤来加快构建速度。 能够指定以下内容会容易得多:

  1. 如果文件系统相同,则执行可以缓存的操作
  2. 做一些可能会根据执行时间改变文件系统的事情
  3. 如果上一步没有改变文件系统,那么做一些可以缓存的事情

这种模式会经常出现在开发版本中。 在 Dockerfile 中为它提供语义会很好。

@ctrimble在一个步骤中破坏缓存将导致缓存在每个后续步骤中始终被破坏。

@cpuguy83正是如此。 我的构建系统的语义对于开发构建是暂时的。 我必须选择正确的构建而不是缓存。 我真的很想两者兼得。

这里有相当多的讨论,如果已经有人提出了,请道歉,但如果有这样的事情怎么办:

CHECK [FILE_PATH]

docker 要做的就是存储文件的 MD5(或任何其他哈希值),如果它发生更改,则此后的所有步骤都将无效。

我可能会做这样的事情:

CHECK Gemfile
CHECK package.json
CHECK composter.json
CHECK project.json

可能还想启用检查一段时间后经过的情况。 Ansible 的cache_valid_time apt插件的cache_valid_time参数可能会提供一些灵感: http :

为此,语法将是:

EXPIRE 1234567 
RUN apt-get update
RUN bundle install

Docker 会知道上次运行时间并根据“现在”计算时间是否已经过去。

@atrauzzi我们现在在 1.13 中只支持--squash构建(目前只是实验性的)。

@cpuguy83是否有任何关于--squash文档或解释可供我阅读? 一开始,这个名字并没有让它听起来像我在想什么。 但我可能(而且很可能是)错了!

@atrauzzi是的,在构建参考中。

基本上, --squash既保留了层缓存,又创建了第二个图像,就好像 Dockerfile 中的所有内容都发生在单个层中一样。

我不明白为什么需要单独检查文件缓存是否仍然有效, ADDCOPY已经对正在复制的所有内容执行了此操作。

@cpuguy83好点,没想到,当然我已经在使用它了。

时间戳/持续时间方法怎么样? 这对已经可用的东西可行吗?

时间戳/持续时间方法怎么样? 这对已经可用的东西可行吗?

通过构建参数;

ARG expire_after=never
RUN do some thing
docker build --build-arg expire_after=2016-12-01 -t foo .

更改构建参数以破坏缓存

+1 以更清洁的方式

+1 以更清洁的方式

还应该有单独的选项来禁用读取缓存和禁用写入缓存。 例如,您可能希望从头开始重新构建图像并忽略任何缓存图层,但仍将生成的新图层写入缓存。

+1

我可以建议将步骤号传递给构建命令吗?

像这样的东西:
docker build --step 5 .

它会在构建过程中忽略包括步骤 5 之后的所有缓存。

+1
请。

缓存开启|关闭 +1

这些CACHE ON|OFF命令的问题在于,在缓存关闭的任何步骤中,都无法缓存进一步的步骤。 唯一合理的命令是ENDCACHE

这是一个有效的想法/精神。 该命令应该在缓存重新打开时将所有非缓存层合并为一个层。 当然,您仍然可以争论该功能的最佳命名/语义正确性/首选语法。

+1

+1 必备功能

同意 CACHE ON|OFF +1

+1 会很棒。

之前不是很了解Docker缓存步骤的方式,花了半天时间调查为什么我的系统构建不正确。 这是“git clone”缓存。

很想拥有ALWAYS关键字。

怎么关的?

最好的解决方法是什么?

我试过https://github.com/moby/moby/issues/1996#issuecomment -185872769 并且有效
在 Dockerfile 中:

ARG CACHEBUST=1
RUN git clone https://github.com/octocat/Hello-World.git

在命令行上:

docker build -t your-image --build-arg CACHEBUST=$(date +%s)

为什么不创建一个类似于 RUN 的新命令,但永远不会为 RUN NO CACHE 缓存 RUNNC?

我可以确认, @habeebr (https://github.com/moby/moby/issues/1996#issuecomment-295683518) - 我将它与https://github.com/moby/moby/issues/1996#结合使用

+1

RUNNC 是个好主意!

为什么这个问题被关闭了?在要求基本相同的东西的无数重复和不止一个这些重复的冗长评论历史之间,_似乎_很明显,人们对看到此功能可用有健康的兴趣。

我知道这很难,也许没有人提出一个足够优雅的解决方案,既满足需求又足够干净,是一个有吸引力的 Docker 补充......但这并不意味着_不需要_。

我听到的唯一一个支持关闭它的论点是还有其他方法可以实现这一点……但那个论点也没有真正通过。仅仅为了解决缓存控制不足的问题而创建多个基本图像是笨拙的,通过 ARG 设计失效是迟钝和不直观的。我想用户想要利用这些“变通方法”就像 Docker 开发人员想要正式将草率的 hack 合并到工具中一样。

这并不难: https :
简单的解决方案,简单的用户体验。 只是对于是否应该这样做没有明确的共识。

哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇

我会已经实施它只是为了不必听到它,更不用说有一个明确的共识,即用户群想要它。 我没有参与过这么大的开源项目的开发人员,只参与过小得多的项目,所以也许我遗漏了一些东西。

+1

+1 以获得合理的安全性和更好的性能

+1

+1

+1

+1

+1

+1

你们能停止垃圾邮件+1吗? 只需使用反应功能即可投票。

任何变化?
仍然不知道为什么这个问题被关闭了。
在我看来,它是一个必备功能,可以完美地处理从远程 git 存储库的版本拉取。

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

为什么要关闭这个? 我觉得很有用

+1

+1

+1

目前禁用图层缓存的最简单方法(以及以下):

文件

ARG CACHE_DATE
RUN wget https://raw.githubusercontent.com/want/lastest-file/master/install.sh -O - | bash

并且在构建镜像时,需要添加--build-arg

docker build  --build-arg CACHE_DATE="$(date)"

然后每次构建图像​​时都会执行wget命令,而不是使用缓存。

RUNNCCACHE OFF会很好

与此同时,这看起来很有希望:
http://dev.im-bot.com/docker-select-caching/

那是:

screenshot 2018-05-26 19 03 09

我要保持冷静并加入牛群:

+1

是的,我需要对命令进行选择性缓存。 如果我只更改配置文件中的一个词,我的COPY 80% 的时间会失败。 我不想缓存我的COPY而是缓存其他所有内容。 拥有CACHE ONCACHE OFF会很棒。

RUN X
RUN X
CACHE OFF
COPY /config /etc/myapp/config
CACHE ON

@shadycuz在使用任何方法禁用/使其无效后,您将永远无法“重新启用”缓存。 构建将无法验证(在合理数量的资源的合理时间内)非缓存层没有更改文件系统中它需要在较新层中考虑的其他内容。 为了尽量减少总是需要拉入外部配置文件的影响,你应该把你的COPY指令尽可能地放在 Dockerfile 中(这样 Docker 可以将构建缓存用于尽可能多的在缓存失效之前尽可能地构建过程)。

要在构建过程中的特定点使缓存无效,您可以参考前面提到的有关使用--build-argARG任何其他注释。

@shadycuz @curtiszimmerman是的,我们可能只保留CACHE OFF而不是CACHE ON ,因为如果更改前一层,则需要重建以下层。

我同意CACHE ON从技术角度来看没有意义。 它有助于更​​清楚地表达意图,然而实际上哪些层是要失效的。

一个更灵活的解决方案是类似于RUN ,它允许一些 shell 代码来确定缓存是否应该失效。 退出代码 0 可能意味着“使用缓存”和 1“使缓存无效”。 如果没有给出 shell 代码,默认情况下可能是从这里开始使缓存无效。 例如,该命令可以称为 INVALIDATE。

为什么这个没有评论就关闭了?

有评论,但是被github隐藏了
https://github.com/moby/moby/issues/1996#issuecomment -93592837

+1

这个功能现在对我来说将是一个救星。

+1

关闭这个,因为我们没有看到很多现实世界的用例

212 条评论和计数,但仍然没有用例? 显得很无知。

+1

+1

+1

+1

+1

问题仍然存在,仍然需要解决方案。 在现实世界中仍有许多用途。

+1

我怀疑 Docker 开发人员没有动力去实现这一点,以保护他们的集中式构建基础设施免受无缓存请求的 DDsS 影响。

我还怀疑企业用户可能会对促进无缓存构建的并行基础设施感兴趣。

总的来说,这个问题与软件功能无关,而是服务扩展问题。

@jaromil这不完全正确,因为这在自托管存储库上也是不可能的。

有什么软件可以运行自托管存储库? 我真的不知道你指的是什么。
一个简单的自托管解决方案可能是 cron 克隆 git repos 和 runnig docker build --no-cache - 我确信这个问题不会发生在开源软件上:任何人都可以修改 docker build 命令行。

@jaromil我不认为这是问题所在。 将它用于 DockerHub 的开源项目(以及付费项目,他们不收取构建数量的费用)会更有效。 在频繁构建的 CI/CD 环境中,情况变得更糟。

只要您需要这样做(您正在使用 docker 和 git 并且不想让 5 个容器运行共享卷),您必须在每次上传新版本时重建容器并上传。 整个容器。
使用代码中的无缓存标志,每次运行构建时,您只需构建和替换单个层而不是整个容器来更新版本。

关于自托管代表,您会感到惊讶。 我理解@bluzi 的评论,如果您自托管(或使用 aws

好的,这当然是我设想的一个更​​复杂的场景。 现在我想……使用一种 nocache 单层哈希上传……推送和覆盖,你说出来的。 我不确定

TLDR:我认为对 Docker 文档的一些改进可能会有很大帮助。

在遇到自己的缓存问题/混淆后,我最终来到了这里。 在阅读了此处和 https://github.com/moby/moby/pull/10682 中的所有评论后,我为我的特定用例找到了一个可行的解决方案。 然而不知何故,我仍然对 Docker 对此的回应感到沮丧,而且似乎许多其他人也有同样的感觉。

为什么? 在从几个不同的角度考虑之后,我认为这里的问题是模糊用例的组合,反对提议更改的过于笼统的论点(可能是有效的,但没有直接解决所提出的用例),以及缺乏Docker 对一些常见用例的建议的文档。 也许我可以帮助澄清事情并确定可以改进以帮助解决这种情况的文档。

从字里行间看,在我看来,大多数对此功能请求的早期评论者都会对使用docker image build附加参数来禁用 Dockerfile 中特定点的缓存的解决方案感到满意。 听起来 Docker 当前的解决方案(在 https://github.com/moby/moby/issues/1996#issuecomment-172606763 中描述)在大多数情况下应该足够了,而且听起来很多用户对此很满意. (如果有人有一个用例,他们可以为docker image build提供额外的参数,但这个解决方案仍然不够充分,那么添加一条评论来解释为什么这是不够的可能会有所帮助。)

所有挥之不去的挫折似乎都与将附加参数传递给docker image build以控制缓存行为的要求有关。 但是,与此相关的用例还没有得到很好的描述。

再次阅读字里行间,在我看来,所有这些用例要么与代表用户运行docker image build有关,要么与分发给其他用户然后运行docker image build Dockerfile 相关docker image build是一个问题,那么添加注释来详细解释您的用例可能会有所帮助。)

在许多情况下,听起来用例实际上并不需要在 Dockerfile 中的特定点(这是此功能请求的原始点)禁用缓存的能力。 相反,听起来许多用户对完全从 Dockerfile 中禁用缓存的能力感到满意,无需在docker image build使用“--no-cache”参数,也无需在每次之前手动修改 Dockerfile建造。 (在描述用例时,提及是否实际需要部分缓存或完全禁用缓存是否足以满足您的用例可能会有所帮助。)

在服务代表用户运行docker image build的情况下,听起来 Docker 期望所有此类服务要么无条件禁用缓存,要么为用户提供禁用缓存的选项。 根据 https://github.com/moby/moby/pull/10682#issuecomment-73777822,Docker Hub 无条件禁用缓存。 如果服务还没有这样做,Docker 有 https://github.com/moby/moby/pull/10682#issuecomment-159255451 建议向服务提供商投诉。

在我看来,这是 Docker 对运行docker image build服务采取的合理立场。 但是,这个位置确实需要在显眼的地方正式记录下来,以便服务提供商和用户都知道会发生什么。 除了那些深埋在巨大/古老/封闭拉取请求中的即兴评论之外,目前似乎没有其他任何地方记录此立场或 Docker Hub 缓存行为,因此服务提供商和用户都经常这样做也就不足为奇了弄错了。 也许在docker build参考中添加信息来描述 Docker 对构建服务使用缓存的看法,并向Docker Hub 自动构建文档添加有关 Docker Hub 缓存行为的信息可能会消除这个问题?

对于将 Dockerfile 分发给其他用户然后自己运行docker image build ,有些人认为使用简单的docker build .命令(没有附加参数)是如此普遍,以至于Dockerfile 构建者要求用户添加参数是不合理的,而其他人(例如:https://github.com/moby/moby/issues/1996#issuecomment-72238673 https://github.com/moby/moby/pull /10682#issuecomment-73820913 https://github.com/moby/moby/pull/10682#issuecomment-73992301) 认为通过硬编码缓存覆盖到 Dockerfile 来无条件阻止用户使用缓存是不合适的。 在缺乏详细/令人信服的用例的情况下,Docker 已做出执行决定,需要额外的命令行参数来控制缓存,这似乎是许多挥之不去的挫败感的根源。 (如果有人有与此相关的引人注目的用例,添加详细解释它的评论可能会有所帮助。)

然而,在我看来,Docker 或许可以通过打破用户不带额外参数就运行docker build .的习惯来让每个人都开心。 任何相关的 Docker 教程(例如thisthis
这个)。 此外,虽然docker build文档确实列出了“--no-cache”参数,但它没有解释它的重要性或强调它在许多常见用例中很重要的事实。 (还要注意docker image build文档是空的。它至少应该引用docker build文档。)似乎只有Dockerfile 参考最佳实践文档实际上描述了缓存行为并提到了角色“--no-cache”参数。 但是,这些文档可能只能由高级 Dockerfile 编写者阅读。 因此,只有高级用户才熟悉“--no-cache”参数也就不足为奇了,而且大多数用户只会在没有其他参数的情况下运行docker build .然后在它不运行时感到困惑他们或 Dockerfile 编写者如何期望/想要。 也许更新教程和docker build文档以提及“--no-cache”参数及其重要性可能会消除这个问题?

+1

+1

docker的官方工具

+1

+1

我现在正在使用的用例是希望将短暂的、短暂的秘密作为构建参数传递来安装私有包。 这完全破坏了缓存,因为这意味着每次秘密更改(基本上是每次构建)时,缓存都会被破坏并重新安装包,即使唯一的变化是秘密。

我已经尝试通过在指定 ARG 之前获取 COPY 的脚本中使用 ARG 来绕过此问题,但是如果 ARG 输入已更改,则在声明 ARG 后,Docker 似乎会使所有内容无效。

我希望看到的行为是能够在调用 build.gradle 时在 Dockerfile 或 CLI 中将 ARG 标记为始终缓存。 对于像秘密这样的用例,这通常是您想要的; 包列表的内容应该指示缓存何时失效,而不是传递给 ARG 的参数。

我理解可以将这些部分提取到第二个图像中的理论,然后将其用作基础图像,但是当项目使用包时,例如在 package.json、requirements.txt、Gemfile 等中,这相当尴尬. 基础镜像也会不断地重建。

从这条线指令 +1 到CACHE OFF - 我已经等了好几年了。

我不得不禁用 docker hub / docker cloud 上的缓存,如果我可以缓存大层,然后在 dockerfile 末尾附近运行 nocache update 命令,这将节省大量时间和构建。

我希望看到的行为是能够在调用 build.gradle 时在 Dockerfile 或 CLI 中将 ARG 标记为始终缓存。 对于像秘密这样的用例,这通常是您想要的; 包列表的内容应该指示缓存何时失效,而不是传递给 ARG 的参数。

--build-arg PASSWORD=<wrong>可能产生与--build-arg PASSWORD=<correct>不同的结果,所以我不确定仅查看包列表的内容是否适用。 构建器本身无法预测设置/更改环境变量会对运行的步骤产生什么影响( make DEBUG=1 foomake DEBUG=0 foo是否相同?)。 当前唯一的例外是xx_PROXY环境变量,其中假设网络连接可能需要代理,但切换到不同的代理应该会产生相同的结果。 因此,为了使其工作,需要某种方式来指示要忽略缓存的特定环境变量(/ build arg)。

请注意,BuildKit 现在对RUN --mount=type=secretRUN --mount=type=ssh提供实验性支持,这可能有助于传递机密/凭据,但如果这些机密发生变化,缓存仍可能无效(不确定;这可能是调出 buildkit 问题跟踪器 https://github.com/moby/buildkit/issues)。

我不得不禁用 docker hub / docker cloud 上的缓存

Docker Hub / Cloud 是否真的_使用_缓存? 我认为那里没有使用缓存(如;它使用的是临时构建环境)

我记得 DockerHub 过去不使用构建缓存,但我一直在查看我在这张票之前在 Docker Cloud 上的自动构建,现在每个分支的自动构建滑块旁边都有一个构建缓存滑块,尽管默认情况下它是关闭的。

我不敢启用构建缓存,因为像git clone这样的步骤不会获得最新的 repo 下载,因为它只比较不会改变的指令字符串。 今天向一位多年来一直困扰我们的同事解释这个问题,他感到惊讶,因为这对于许多用例来说似乎是一个很大的缺陷。

我会更喜欢最初的git clone && make build缓存,然后就做了NO CACHE上的git pull && make build一步获得尚未安装作为最后只有一个小得多的代码更新+依赖性层,从而有效地缓存大部分图像,不仅用于构建,更重要的是对于现在必须每次重新下载和替换数百 MB 层的所有客户端,这是非常低效的。

大小是因为许多项目都有大量的依赖项,例如。 系统包 + Perl CPAN 模块 + Python PyPI 模块等。

添加系统包依赖项以及 CPAN 和 PyPI 依赖项后,即使使用 Alpine 也不会小很多,因为我多年来一直使用 Alpine 来尝试查看是否可以创建更小的图像,但是一旦您有很多依赖项,它就不会如果基础开始变小,就会有很大不同,因为添加系统包会立即添加大部分内容。

缓存包括所有系统包 + CPAN + PyPI 模块的较早层意味着在最后一层更新中几乎不会发生变化,因为在大多数情况下我不会更新已安装的工作模块(我使用了我的bash-tools 中的脚本实用程序子模块 repo 仅安装尚未安装的软件包,以避免安装不必要的非错误修复更新)

我一直在考虑使用像更改 ARG 之类的技巧(我通过搜索诸如 http://dev.im-bot.com/docker-select-caching/ 之类的博客获得的想法):

在 Dockerfile 中:

ARG NOCACHE=0

然后像这样运行 docker build:

docker build --build-arg NOCACHE=$(date +%s) ...

但我认为这在 Docker Cloud 中是不可能的。

有环境变量,但似乎不可能使用动态内容,例如上面的 epoch(或者至少没有记录我可以找到),并且对于环境变量,我不确定它会使该指令行的缓存无效。

@thaJeztah是的,如果被误解或滥用,这种行为很容易产生负面后果,但它可以很好地解决某些用例。

--build-arg PASSWORD=<wrong>可能产生与--build-arg PASSWORD=<correct>不同的结果,所以我不确定仅查看包列表的内容是否适用

虽然你说它会产生不同的结果是正确的,但如果包列表没有改变,我真的不在乎密码是对是错; 包已经在之前的镜像中,所以运行它的用户已经拥有访问权限(即,这不是安全问题),如果之前密码错误,我希望 Dockerfile 作者的负担会导致安装失败如果需要,这意味着您仍然有机会在修复密码后正确安装软件包。

是的,我正在想象docker build --force-cache-build-arg SECRET=supersecret 。 这很笨拙,我相信有人可以想出更好的方法。

@HariSekhon 不过,听起来您的用例实际上与我的相反,对吗? 您想选择性地强制未命中缓存,而不是选择性地强制命中缓存?

添加这个对我有用:

ADD http://date.jsontest.com/ /tmp/bustcache

但该网站现在已关闭。 这应该工作

ADD http://api.geonames.org/timezoneJSON?formatted=true&lat=47.01&lng=10.2&username=demo&style=full /tmp/bustcache

@itdependsnetworks

完美,这是一个很好的解决方法,该站点现在已备份。 记录图像的构建日期也很有用。

我试过这个,类似的其他特殊文件每次都应该改变

COPY /dev/random ...

但这不起作用,即使RUN ls -l -R /etc显示存在此类文件,但始终找不到,我怀疑有一些保护措施可以防止使用特殊文件。

现在我在 DockerHub / Docker Cloud 上更多地考虑它,您可能还可以使用预构建挂钩来生成一个包含日期戳的文件,然后将其复制到您想要缓存的层之前的图像,实现类似的结果,尽管 ADD如上所示,我认为对本地 docker 和云构建更具有可移植性。

我们需要为这个缓存破坏解决方法找到一个更可靠的日期打印站点 - 以上两个似乎都是演示/有配额,因此不可靠和随机破坏构建。

第一个一直打破它的每日配额,第二个现在出现这个错误

{"status": {
  "message": "the daily limit of 20000 credits for demo has been exceeded. Please use an application specific account. Do not use the demo account for your application.",
  "value": 18
}}

我认为有一个指令会很棒,我可以将其放入 docker 文件中,该文件将在每次构建时运行,如果输出与上次构建不同,则重建后续层。

示例用法是:

FROM something
... 
CACHE_BUST git ls-remote my-git-repo HEAD
RUN git clone --depth=1 my-git-repo ...
...
CMD ["my-cmd"]

上面的 CACHE_BUST 指令中的命令将从指定 repo 的 HEAD 输出 SHA,这样我的 dockerfile 可以根据对 repo 的更改知道是否克隆。

我在这里的所有用例都与如上所示的非确定性网络相关层有关。 目前我可以在这里使用 ARG 来满足我的所有需求,但这意味着我经常需要一个使用 dockerfile 的构建脚本,如果只维护 dockerfile 本身会很好。 (加上一些工具不允许参数)

我当前的工作流程如下所示:

ARG SHA_TO_BUILD
RUN echo SHA_TO_BUILD
RUN git clone ...
...everything else reliant on that clone
$> ./build-my-image.sh $(get-latest-sha)

我认为为每个命令打开和关闭缓存是一个坏主意,文件的编写方式应该这样,如果一层需要重建,其余的也需要重建。 但我确实认为能够在文件中的某个点强制重建会很好。

这么棒的功能,为什么还悬而未决?

另一个用例是当文件的内容发生变化但Dockerfile没有发生变化时。

例如,如果我有一个COPY file.txt .并且我修改了file.txt Docker 仍将使用缓存的旧版本。

如果 Docker 对它复制的文件执行校验和,并在下次使用它来确定它是否应该使用可以解决问题的缓存层。

截至目前,我被迫使用--no-cache并且下载和执行的方式超出了需要(浪费时间和带宽)。

@brennancheung如果是这样,那就是一个错误。 随意打开一个带有可重现步骤的单独问题。

我认为@brennancheung 的重点是什么
https://github.com/brennancheung是说,例如,当你
加载配置文件(或类似的东西)。 你不需要
重新安装整个应用程序,只需更新这些文件和命令
与之关联,运行 docker 和 voila,系统就设置好了。
当然,通常您可以将配置文件放在更接近
您的堆栈,但情况并非总是如此。

Am Mi., 20. März 2019 um 00:10 Uhr schrieb Tibor Vass <
通知@github.com>:

@brennancheung https://github.com/brennancheung如果是这样的话,
这是一个错误。 随意打开一个带有可重现步骤的单独问题。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/1996#issuecomment-474666893或静音
线程
https://github.com/notifications/unsubscribe-auth/ALBon0edO9m5BU3C5Ik2i__9eogZc1Jiks5vYaaNgaJpZM4BB_sR
.

——
蒂亚戈·罗德里格斯

试过这个https://github.com/moby/moby/issues/1996#issuecomment -185872769

但它只会在第一次使用时/之后影响缓存,而不仅仅是它的定义。

https://docs.docker.com/engine/reference/builder/#impact -on-build-caching

如果 Dockerfile 定义了一个 ARG 变量,其值与之前的构建不同,那么“缓存未命中”发生在它第一次使用时,而不是它的定义。

@samstav “第一次使用”在ARG RUN之后是第一个ARG是一个构建阶段),所以:

ARG FOO=bar

FROM something
RUN echo "this won't be affected if the value of FOO changes"
ARG FOO
RUN echo "this step will be executed again if the value of FOO changes"

FROM something-else
RUN echo "this won't be affected because this stage doesn't use the FOO build-arg"

如果您使用的是下一代构建器 (BuildKit),则上述内容在某种程度上取决于启用DOCKER_BUILDKIT=1 ,因为如果最后阶段不需要它们,BuildKit 可以跳过构建阶段(或跳过构建阶段)如果它们可以被完全缓存)

此页面是否有帮助?
0 / 5 - 0 等级