3、镜像分层机制:基础镜像层、增量层、层叠与合并原理、UnionFS与OverlayFS

聊到镜像回滚,绕不开的一个核心概念就是「分层」。说白了,Docker 镜像之所以能那么轻量、能秒级回滚,全靠这套分层机制在背后撑着。我刚开始接触容器时,也觉得镜像就是个「大压缩包」,后来踩了几次坑才明白——分层才是精髓。

3.1 基础镜像层:一切的地基

基础镜像层,就是最底下那一层。它通常是一个精简的操作系统核心,比如 Alpine、Ubuntu、CentOS 的 rootfs。这一层是只读的,永远不会被修改。

举个例子,你写 FROM ubuntu:22.04,Docker 就会把 ubuntu:22.04 这个镜像的所有层拉下来,作为你新镜像的「地基」。我习惯把基础镜像层比作「毛坯房」——墙、地板、水管都在,但还没装修。

关键点: 基础镜像层一旦构建完成,内容就固定了。所有基于它的镜像都共享同一份底层数据,不重复存储。

3.2 增量层:每次修改都只存「差异」

你在 Dockerfile 里每写一条 RUN、COPY、ADD,Docker 都会生成一个新层。这个层只记录「相对于上一层的改动」,而不是整个文件系统的快照。

比如你执行:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
COPY index.html /var/www/html/
CMD ["nginx", "-g", "daemon off;"]

这里一共会产生 4 层(基础层 + 安装层 + 复制层 + CMD 元数据层)。每一层都只存增量——安装 nginx 那层只存新增或修改的文件,不会把 ubuntu 的几千个文件再复制一遍。

避坑指南: 我曾经在项目里看到有人把 Dockerfile 写成「一层流」——所有操作塞进一个 RUN 里。结果每次改一行代码,整个镜像都要重新构建。分层的好处全浪费了。记住:经常变动的指令放后面,不常变的放前面,这样能最大化利用缓存。

3.3 层叠与合并原理:堆叠的艺术

这么多层堆在一起,怎么让容器看到「一个完整的文件系统」?答案是:层叠与合并。

Docker 会把所有层按照从下到上的顺序堆叠起来。当容器读取一个文件时,它会从最上层开始找,找到就返回;如果上层没有,就往下找。这叫做「写时复制(Copy-on-Write)」的读取策略。

我画了一张图,帮你理解这个堆叠过程:

镜像分层堆叠与合并示意图 容器可写层(Writable) 镜像层 4:CMD / ENTRYPOINT 镜像层 3:COPY index.html 镜像层 2:RUN apt-get install nginx 镜像层 1:ubuntu:22.04 基础层 说明: • 读取文件时,从上往下找,找到即返回(上层覆盖下层) • 写入文件时,在容器可写层创建副本(Copy-on-Write) • 删除文件时,在容器可写层创建「白障文件」标记删除

合并的过程其实不复杂。Docker 把每一层看作一个「增量补丁」,最终合并成一个完整的文件系统视图。你想想看,如果每次都要完整复制整个文件系统,那一个镜像动辄几个 GB,根本没法玩。

3.4 UnionFS 与 OverlayFS:背后的引擎

分层堆叠的想法很美好,但谁来实现它?答案是联合文件系统(Union Filesystem)。

特性 UnionFS(早期) OverlayFS(主流)
内核支持 需要额外模块 Linux 内核 3.18+ 原生支持
层数限制 理论上无限制,但性能随层数下降 最多 128 层(实际够用)
性能 较差,尤其大量小文件场景 接近原生文件系统性能
写时复制 支持,但实现较复杂 支持,且效率更高
当前状态 基本被淘汰 Docker 默认驱动(overlay2)

OverlayFS 的工作原理,说白了就是「把多个目录合并挂载成一个目录」。它有两个核心目录:lowerdir(只读下层)和 upperdir(可写上层),然后通过 merged 目录呈现合并后的视图。

我记得有一次排查容器磁盘 IO 异常,发现是 overlay2 的层数太多(一个镜像叠了 60 多层),导致每次文件查找都要遍历所有层。后来优化了 Dockerfile,把频繁变动的层合并,问题就解决了。

注意: OverlayFS 的删除操作不是真的删除下层文件,而是在 upperdir 里创建一个「白障文件」(whiteout file)。这个文件会「遮挡」下层的同名文件。所以,删除操作并不会释放下层镜像的磁盘空间——这也是镜像回滚能「恢复已删除文件」的根本原因。

3.5 分层机制与回滚的关系

理解了分层,回滚的原理就一目了然了。每个镜像层都是只读的、不可变的。当你回滚时,只需要把容器引用的「层指针」指向旧的层即可。不需要复制数据,不需要重新构建,几乎是瞬间完成。

我习惯把镜像层比作 Git 的 commit。每个层都有一个唯一的 SHA256 摘要,层与层之间通过指针串联。回滚就是「把 HEAD 指针移回旧的 commit」——数据还在,只是引用变了。

核心结论: 镜像分层机制是回滚保护的「物理基础」。没有分层,回滚就是全量复制;有了分层,回滚就是指针切换。这也是容器技术相比传统虚拟机在「版本管理」上的巨大优势。