bigywater.tech
Docker · 14

BuildKit 缓存挂载:包管理器缓存怎么跨构建复用

“改了一行 Dockerfile,整个 apt/apk 安装又重新下载一遍”是构建慢的常见原因。RUN --mount=type=cache 让包管理器的缓存目录跨构建保留。这篇用 apk 实测:第一次、之后每次各要多久,缓存里的东西会不会进镜像,以及缓存怎么按 id 区分。

193.7 秒 → 0.94 秒
带缓存挂载的 apk add:缓存为空的第一次与缓存已有 13 个文件之后的构建用时
13 个文件 / 4644 KB
apk 缓存目录里累积的内容,不会出现在镜像里
id
不同 id 的缓存互相独立,同一个 id 在多次构建间保留

第一次不会更快

实测 · 本机执行输出
  [没有缓存挂载] BUST=1:退出码 0,构建用时 181917 ms
      BUST=1
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
  [没有缓存挂载] BUST=2:退出码 0,构建用时 98042 ms
      BUST=2
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
  [没有缓存挂载] BUST=3:退出码 0,构建用时 195787 ms
      BUST=3
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
  [RUN --mount=type=cache,target=/var/cache/apk] BUST=1:退出码 0,构建用时 193675 ms
      BUST=1 进入时缓存目录里有 0 个文件
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
      结束时缓存目录里有 13 个文件,共 4644 KB
  [RUN --mount=type=cache,target=/var/cache/apk] BUST=2:退出码 0,构建用时 943 ms
      BUST=2 进入时缓存目录里有 13 个文件
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
      结束时缓存目录里有 13 个文件,共 4644 KB
  [RUN --mount=type=cache,target=/var/cache/apk] BUST=3:退出码 0,构建用时 764 ms
      BUST=3 进入时缓存目录里有 13 个文件
      Executing ca-certificates-20260413-r0.trigger
      OK: 15 MiB in 24 packages
      结束时缓存目录里有 13 个文件,共 4644 KB

缓存挂载不是魔法:缓存为空的那一次构建要照常下载(193.7 秒,这台机器的网络很慢)。之后每次进入这一步时目录里已经有 13 个文件,同一条 apk add 只要 0.94 秒。没有缓存挂载时即使 Dockerfile 只改了一个无关的 ARG 导致这一层重做,每次也要重新下载(98–196 秒)。

不进镜像

实测 · 本机执行输出
  从有缓存挂载的镜像启动后 ls /var/cache/apk | wc -l = 0(目录里没有文件)
  两个镜像的 Size:没有缓存挂载 27.63 MB;有缓存挂载 23.00 MB

缓存目录只在构建那一步里挂载,构建结束就不在镜像里了:从镜像启动后 /var/cache/apk 是空的。所以缓存不会让镜像变大——也就不需要在 RUN 末尾再 rm -rf 缓存。

按 id 区分

实测 · 本机执行输出
  第 1 次构建:id=one 里有 0 个文件 | id=two 里有 0 个文件
  第 2 次构建:id=one 里有 1 个文件 | id=two 里有 0 个文件

同一个 id 的缓存在构建之间持续累积(第二次构建能看到第一次写进去的文件),不同 id 互不影响。本文每次都显式写了 id;不写时的默认规则我没有测。缓存数据在构建器所在的机器上,CI 里如何持久化,本文没有测。

分步演示

BuildKit 缓存挂载
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。包管理器用 apk(容器能访问 Alpine 镜像站,但这台机器的网络较慢,各次下载用时波动很大,绝对时间只代表本次);每次运行用带时间戳的 id 与 salt 层,保证缓存从空开始;“强制该层重做”用 ARG BUST 的值变化;实验后已删除镜像(BuildKit 里的缓存挂载数据没有手动清理)
    • 没有验证:apt 与 pip / npm 等包管理器的缓存挂载写法、sharing=locked / private 在并发构建下的行为、缓存挂载在 CI 里的持久化、缓存被 BuildKit 垃圾回收的策略、没缓存挂载的镜像比有缓存挂载的镜像大 4.63 MB 的原因
    京ICP备2026067502号-1京公网安备11010502063487号