15.7.2 镜像分层与构建缓存机制

镜像分层与构建缓存机制#

镜像采用分层(Layer)结构,每条 Dockerfile 指令通常生成一层。分层只读、可复用,不同镜像可共享相同层以减少存储与分发带宽。容器运行时在只读镜像层之上叠加可写层(OverlayFS/UnionFS),实现增量变更。

原理草图(分层与缓存命中)#

文章图片

分层构成与影响#

  • 基础层:基础镜像决定操作系统与常用库。
  • 中间层RUNCOPYADD 指令创建,反映依赖安装与文件变更。
  • 元数据层ENVLABELEXPOSECMD,通常不产生文件变更但仍会形成层,影响缓存命中。

分层过多或临时文件残留会导致镜像体积增大、构建与分发变慢。

构建缓存机制与命令解释#

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/*
  • 固定版本:提升可复现性和缓存稳定性。

排错与诊断#

  1. 构建异常或缓存混乱
docker build --no-cache -t cache-demo:debug .
  1. 镜像体积过大
docker history cache-demo:1.1
# 查看各层大小,定位膨胀指令
  1. 构建节点空间不足
docker image prune -f
docker builder prune -f

练习#

  1. COPY app/ /app/ 改为 COPY . /app/,新增一个无关文件(如 README.md),观察缓存命中变化。
  2. 拆分 RUN apt-get updateRUN apt-get install 两条指令,比较层数、镜像大小与构建耗时变化。
  3. 使用 docker history 对比优化前后的镜像层大小,写出你对缓存命中率的结论。