docker镜像由多个只读层叠加构成,每条dockerfile指令生成一层,仅记录文件系统增量变化;容器运行时 atop 添加可写层,共享底层实现空间复用与高效缓存。
docker 镜像不是一整块打包的文件系统,而是一层层叠加起来的只读快照。理解这一点,就抓住了构建高效、轻量、可复用镜像的关键。
分层是怎么形成的
每一条 Dockerfile 指令(如 FROM、RUN、COPY、ENV)在构建时都会生成一个新层。这些层按顺序堆叠,底层是基础操作系统(比如 centos:7 或 python:3.9-slim),上层逐步添加依赖、配置和代码。
- 每一层只记录“变化”:新增文件、修改内容、删除路径——不是复制整个文件系统
- 所有层都是只读的,只有容器运行时才在最顶上加一个可写层
- 多个镜像若共用某一层(例如相同的基础镜像),宿主机上只存一份物理数据
分层直接影响构建速度和镜像大小
构建过程会逐条执行 Dockerfile 指令,并尝试复用已有缓存层。一旦某条指令的输入发生变化(比如源码更新、包版本变动),它及之后的所有层都会失效并重新构建。
- RUN apt-get install 和 RUN rm -rf /var/cache/apt 写在两行?那就多出一层缓存的临时文件,最终镜像体积变大
- COPY . /app 放得太早,只要代码一改,后续所有 RUN 指令都无法利用缓存
- 把变更少的指令(如安装依赖)放在前面,变更频繁的(如复制源码)放后面,能显著提升缓存命中率
分层也决定了你该怎么写 Dockerfile
优化目标很明确:减少层数、压缩单层体积、提高缓存复用率。这不是靠删指令,而是靠合并逻辑和控制变更节奏。
- 用 && 合并多个 RUN 命令(如
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*) - 避免无意义的中间文件残留,比如清理缓存、日志、文档要跟在同一条 RUN 里完成
- 用 .dockerignore 过滤掉不需要 COPY 的文件(如
node_modules、.git),防止触发不必要的层重建
怎么看自己镜像到底分了几层
执行 docker history <image-name></image-name> 就能列出每一层的大小、创建命令和时间。这是排查“为什么镜像这么大”或“哪一步没走缓存”的第一手依据。
- 看到某一层异常大(比如几百 MB),大概率是 RUN 没清理干净或 COPY 了不该进镜像的东西
- 某条指令显示
<missing></missing>,说明它被合并或跳过了,可能用了多阶段构建 - 多阶段构建(multi-stage build)本质就是用前一阶段的产物覆盖后一阶段的 FROM,只保留最终需要的层,大幅瘦身











