15.7.2 镜像分层与构建缓存机制
镜像分层与构建缓存机制#
镜像采用分层(Layer)结构,每条 Dockerfile 指令通常生成一层。分层只读、可复用,不同镜像可共享相同层以减少存储与分发带宽。容器运行时在只读镜像层之上叠加可写层(OverlayFS/UnionFS),实现增量变更。
原理草图(分层与缓存命中)#
分层构成与影响#
- 基础层:基础镜像决定操作系统与常用库。
- 中间层:
RUN、COPY、ADD指令创建,反映依赖安装与文件变更。 - 元数据层:
ENV、LABEL、EXPOSE、CMD,通常不产生文件变更但仍会形成层,影响缓存命中。
分层过多或临时文件残留会导致镜像体积增大、构建与分发变慢。
构建缓存机制与命令解释#
Docker 构建按层匹配缓存,满足以下条件可复用缓存层:
1. Dockerfile 指令及参数未变化。
2. 指令依赖的上下文文件未变化(COPY/ADD 涉及文件)。
3. 前序层缓存命中(链式依赖)。
关键命令与用途:
- docker build -t app:1.0 .:普通构建并写入缓存。
- docker build --no-cache -t app:1.0 .:禁用缓存,排查构建问题。
- docker history app:1.0:查看镜像分层大小与指令来源。
- docker image prune -f:清理无用镜像层,释放空间。
可执行示例:缓存命中与失效演示#
目录结构
/opt/demo-cache/
├── Dockerfile
└── app/
└── main.py
Dockerfile(优化指令顺序)
# /opt/demo-cache/Dockerfile
FROM ubuntu:22.04
# 1) 稳定依赖前置,便于缓存命中
RUN apt-get update && apt-get install -y python3 \
&& rm -rf /var/lib/apt/lists/*
# 2) 仅复制应用代码,减少无关变更
COPY app/ /app/
# 3) 元数据层
WORKDIR /app
CMD ["python3", "main.py"]
应用代码
# /opt/demo-cache/app/main.py
print("cache demo v1")
构建与运行
cd /opt/demo-cache
docker build -t cache-demo:1.0 .
docker run --rm cache-demo:1.0
# 预期输出:cache demo v1
触发缓存命中
# 不改任何文件再次构建
docker build -t cache-demo:1.0 .
# 预期:每层显示 "Using cache"(命中缓存)
触发缓存失效
# 修改应用代码
sed -i 's/v1/v2/' /opt/demo-cache/app/main.py
# 重新构建
docker build -t cache-demo:1.1 .
# 预期:COPY 层及其后续层重新构建,RUN 依赖层仍命中缓存
缓存命中优化策略(含示例)#
- 稳定依赖前置:将依赖安装放前面,代码放后面。
- 减少 COPY 触发范围:
# 推荐:只复制必要目录
COPY app/ /app/
# 不推荐:COPY . /app/ 可能因无关文件变化导致失效
- 合并 RUN 并清理临时文件:
RUN apt-get update && apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
- 固定版本:提升可复现性和缓存稳定性。
排错与诊断#
- 构建异常或缓存混乱
docker build --no-cache -t cache-demo:debug .
- 镜像体积过大
docker history cache-demo:1.1
# 查看各层大小,定位膨胀指令
- 构建节点空间不足
docker image prune -f
docker builder prune -f
练习#
- 将
COPY app/ /app/改为COPY . /app/,新增一个无关文件(如README.md),观察缓存命中变化。 - 拆分
RUN apt-get update与RUN apt-get install两条指令,比较层数、镜像大小与构建耗时变化。 - 使用
docker history对比优化前后的镜像层大小,写出你对缓存命中率的结论。