1. 简介
Docker 的构建过程通常会比较耗时,尤其是涉及拉取基础镜像、复制文件、下载和安装依赖包等操作。为了提升构建效率,docker build
命令默认会利用缓存机制来加速重复构建。
本文将深入解析 Docker 构建缓存的工作原理,并探讨在哪些场景下应避免使用缓存。
2. Docker 构建缓存简介
Docker 镜像是由多个层(Layer)组成的,每一层对应 Dockerfile 中的一条指令。这些层是叠加在一起的,每层都在前一层的基础上添加新的功能。
我们来看一个简单的 Dockerfile 示例:
FROM alpine:latest
RUN apk add --no-cache bash
ADD entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
这个镜像运行一个名为 entrypoint.sh
的脚本,它每分钟打印一次当前时间并休眠 60 秒:
#!/bin/bash
while :
do
echo $(date)
sleep 60
done
第一次构建时,所有步骤都会执行:
$ docker build -t print-date-time .
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM alpine:3.12
---> a24bb4013296
Step 2/4 : RUN apk add --no-cache bash
---> 52f7aaec5411
Step 3/4 : ADD entrypoint.sh /
---> 66ba9eee7c3c
Step 4/4 : ENTRYPOINT ["/entrypoint.sh"]
---> 91a39deabc0b
Successfully built 91a39deabc0b
Successfully tagged print-date-time:latest
但当我们再次构建时,如果 Dockerfile 没有变化,输出如下:
$ docker build -t print-date-time .
...
Step 2/4 : RUN apk add --no-cache bash
---> Using cache
Step 3/4 : ADD entrypoint.sh /
---> Using cache
Step 4/4 : ENTRYPOINT ["/entrypoint.sh"]
---> Using cache
Successfully built 91a39deabc0b
✅ 构建过程使用了缓存,跳过了重复执行步骤。
⚠️ 只要某一层发生变化,后续所有层都会重新构建。
此外,Docker 会检测 ADD
和 COPY
指令所引用的文件是否发生变化。例如,如果我们修改了 entrypoint.sh
,则第 3 层和第 4 层都会被重新构建。
3. 何时不使用缓存?
在某些情况下,缓存可能导致构建结果不是最新的,特别是当依赖项在外部更新但 Dockerfile 本身未改动时。
举个例子:
我们在 Alpine Linux 中安装了 bash
包,如果这个包因为安全修复而更新了版本,但由于 Dockerfile 没变,缓存不会触发重新安装,从而导致镜像未包含最新修复。
类似问题也常见于以下场景:
- ✅ 使用
git clone
拉取代码仓库时(命令不变,内容可能变) - ✅ 安装依赖包时(如 apt-get install、apk add)
- ✅ 构建过程中依赖远程资源(如远程配置、脚本等)
解决方案
3.1 禁用缓存构建
使用 --no-cache
参数可完全跳过缓存:
$ docker build -t print-date-time --no-cache .
✅ 这会强制所有层重新构建。
⚠️ 注意:FROM
指令不受 --no-cache
影响。如果本地已有对应基础镜像,它不会被重新拉取。
3.2 强制拉取基础镜像
如果你想确保基础镜像也是最新的,可以加上 --pull
参数:
$ docker build -t print-date-time --pull .
✅ 这会强制拉取最新的基础镜像。
⚠️ 生产环境慎用 latest
标签。推荐使用固定版本标签,以避免因基础镜像更新导致的不可控问题。
4. 小结
- ✅ Docker 构建缓存能显著提升构建速度
- ✅ 缓存机制基于层(Layer)进行对比,任何一层变化都会触发后续所有层重建
- ✅ 适用于开发阶段快速迭代
- ❌ 在依赖更新频繁、安全要求高的场景下,应谨慎使用缓存
- ✅ 推荐使用
--no-cache
和--pull
来规避缓存陷阱 - ❌ 生产构建应避免使用
latest
标签,建议使用固定版本号
合理使用缓存,能让你的构建更高效;但懂得何时绕开缓存,才是构建稳定镜像的关键。