Docker 构建已挂载 APT 缓存,deb 包仍反复下载
定位基础镜像的 docker-clean 清理钩子,在构建阶段调整 APT 缓存策略,并区分缓存挂载、构建层命中和临时构建器。
Dockerfile 已给 /var/cache/apt 配置 BuildKit 缓存挂载,修改安装包列表后重新构建,之前下载过的 deb 包仍再次下载。构建日志显示安装步骤实际执行了,并非直接命中 CACHED。
在部分 Debian、Ubuntu 系基础镜像中,/etc/apt/apt.conf.d/docker-clean 会在 APT 更新或 dpkg 操作后删除下载的包。缓存目录能够跨构建保留,并不意味着里面的文件不会被 APT 自己清空。
这篇只处理使用 BuildKit RUN --mount=type=cache、基础镜像确实启用了该清理规则的情况。包版本更新、缓存被回收、换了构建器,也会造成重新下载,不能仅凭下载日志就认定是清理钩子。
核对清理规则和缓存是否属于同一个构建器
在基础镜像的构建阶段检查 /etc/apt/apt.conf.d/docker-clean,重点看 DPkg::Post-Invoke 和 APT::Update::Post-Invoke。如果规则删除 /var/cache/apt/archives/*.deb,即使这个路径来自缓存挂载,文件仍会被删除。
同时确认前后构建使用相同的 BuildKit 构建器和缓存标识。type=cache 的内容保存在构建器一侧;新建的临时 CI 构建器不能因为导入了普通构建缓存,就必然拥有上一次的 APT 缓存挂载内容。
若镜像没有这份文件,检查其实际 APT 配置和构建脚本,寻找其他清理命令。不要为了匹配示例去删除整个 /etc/apt/apt.conf.d。
在使用缓存的构建阶段调整 APT 行为
下面是独立构建阶段的完整示例,基于 Ubuntu 24.04,使用稳定 Dockerfile 前端。包名仅用于展示安装步骤,实际项目按需求替换:
# syntax=docker/dockerfile:1
FROM ubuntu:24.04 AS build
RUN rm -f /etc/apt/apt.conf.d/docker-clean \
&& printf '%s\n' 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \
> /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl
第一步只调整这层镜像里的缓存清理配置,不是让你修改宿主机的 APT。移除删除 deb 的钩子,才能让下载目录中的内容留在缓存里;保留下载包的设置与它配合使用。
第二步同时缓存包归档和 APT 状态目录,并用 sharing=locked 避免多个构建同时写同一个 APT 缓存。安装所需索引仍由这次 apt-get update 更新,不能把缓存存在当成索引永远有效。
不要在该步骤末尾再加删除 deb 包或清空这两个缓存目录的命令,否则会主动抹掉要复用的内容。也不要把整个 /var/lib/dpkg 当作可随意丢弃的下载缓存;它保存的是已安装包的状态。
用实际执行的安装步骤验证
为了观察下载复用,需要有一次安装步骤确实重新执行,例如正常修改了所需包列表。若整个 RUN 显示 CACHED,只能说明构建层被复用,还没有验证 APT 下载缓存。
保留相同构建器与缓存标识,检查前后安装步骤的日志:已有且仍符合所需版本的包可以复用,新包或新版包仍需下载。这里不应以“完全没有网络请求”作为成功标准,更新索引本身仍可能访问软件源。
缓存挂载是性能优化,构建必须能在空缓存下正确完成。垃圾回收、另一次构建更改缓存内容或临时构建器销毁,都会让它失去复用效果。--cache-to、--cache-from 的普通构建缓存导入导出也不能代替缓存挂载内容的持久化安排。
保留镜像体积策略的边界
这套设置放在需要 APT 缓存的构建阶段最容易控制。缓存挂载里的文件不会作为该 RUN 的普通镜像层内容保存,但删除 docker-clean 和新增 keep-cache 的配置会保留在这个阶段中。
如果直接把该阶段作为运行镜像,之后在没有缓存挂载的容器中安装软件,下载包可能留在容器文件系统,增加占用。应按最终镜像的用途恢复原清理策略,或采用明确的独立运行阶段,不要顺带改变所有镜像的 APT 行为。
回退时恢复原 Dockerfile 中的 APT 配置步骤并重新构建。仅恢复构建文件不会删除现有缓存;无需为回退这项修改执行全局 Docker 清理,也不应删除其他构建正在使用的缓存。


