DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

BuildKit 只复制一个文件,新增别的文件仍让缓存失效

RUN 只复制一个文件,但绑定源是整个目录,新增其他文件仍可能使缓存失效。按真实依赖缩小 source,分别验证缓存和产物完整性。

RUN 中的命令只复制目录里的 a 文件,向同目录添加 y 后,这条构建步骤却不再显示 CACHED。如果该目录通过 RUN --mount=type=bind 整体传入,先检查挂载源的范围。缓存判断会考虑绑定源的文件输入,不会自动追踪命令最后实际读取了哪几个文件。

下面限定 BuildKit 构建中的 bind 输入,不是运行容器的文件挂载,也不是包管理器的 cache 挂载清理问题。

输出相同,构建输入仍可能不同

以下示例要求构建上下文有 test/a,基础镜像带有 cp:

# syntax=docker/dockerfile:1
FROM alpine:3.22
RUN --mount=type=bind,source=test,target=/test \
    cp /test/a /a

命令只把 a 复制到镜像的 /a,但传给这一条指令的源是整个 test 目录。新增 test/y 改变了这份输入,即使最终 /a 的字节没有变化,构建器也可能重新执行该步骤。

这是执行前的缓存判断,不能等同于“运行命令后比较产物,没有变化就算缓存命中”。两次构建使用的指令、基础镜像、构建器和其他参数也必须一致,否则单凭新增文件前后的耗时不能确认原因。

确实只依赖一个文件时,缩小 source

如果这条步骤不依赖目录中的其他文件,可以把源明确为那个文件:

RUN --mount=type=bind,source=test/a,target=/input-a \
    cp /input-a /a

source 可以指定文件或目录,这个片段只缩小该步的输入。更改指令本身会使第一次构建重新执行,不能要求修改后的第一次就显示 CACHED。

实际编译通常还依赖脚本、配置、锁文件、头文件或生成输入。不能因为日志里只出现一个主文件,就把所有隐含依赖排除。需要多份输入时,可以按真实依赖准备专用目录或分别声明挂载,保留构建所需内容。

单独修改 mtime 不等于新增文件

当前缓存规则中,bind 输入的文件元数据参与校验,但单独的文件修改时间 mtime 不参与这项校验。仅调整已有文件的时间戳,与新增一个文件、改变内容或其他相关属性,不是同一种变化。

因此不能用批量 touch 当作控制该步骤缓存的办法,也不需要为排查一个输入范围问题清空全局缓存。

分别验证缓存与产物

在独立构建环境、同一构建器中,对同样输入连续构建,检查这条步骤能否复用缓存。随后仅改变原大目录中已被移出该步输入范围的文件,再看相应步骤是否仍可复用;最后改变真正必需的输入,确认该步按预期重新执行。

每次还要核对产物内容和实际应用行为,避免为了命中缓存漏掉依赖。缓存可能因被清理或更换构建器而不可用,不能把所有重新执行都归为目录范围。

保存原 Dockerfile 和构建输入布局,出现遗漏依赖时恢复它们,重新构建完整产物。这个调整改变构建依赖关系,不需要改线上数据卷,也不能用“命中缓存”代替发布镜像的功能验证。