unionfs是docker实现写时复制(cow)的底层机制,通过lowerdir(只读镜像层)、upperdir(容器可写层)和merged(统一挂载视图)三层分工,以完整文件为粒度触发复制:修改或删除lowerdir文件时复制全量或生成.whiteout标记,新建文件直写upperdir,多容器共享lowerdir但upperdir完全隔离。

UnionFS 是 Docker 实现写时复制(Copy-on-Write,CoW)的底层文件系统机制,它不靠魔法,而靠三层目录的分工与文件粒度的精确控制。
lowerdir、upperdir 和 merged 三目录各司其职
Docker 默认使用 overlay2 驱动,实际在磁盘上维护三个关键目录:
- lowerdir:多个冒号分隔的只读路径,对应镜像的每一层(如基础系统、软件包、应用代码),顺序靠前的层优先级低,被后层覆盖
- upperdir:单个可写目录,属于当前容器独有,所有新增、修改、删除操作都落在此处,初始为空
- merged:对外暴露的统一挂载点,即容器内看到的 / 根文件系统,进程读写时完全感知不到多层叠加
CoW 不是“一写就复制”,而是“首次写同路径才触发”
复制动作只在真正需要写入时发生,且以完整文件为单位,不是按块或字节:
- 修改一个存在于 lowerdir 中的文件(如 echo "new" > /etc/hosts)→ 系统把原文件从某镜像层完整拷贝到 upperdir,再编辑副本
- 删除一个 lowerdir 中的文件(如 rm /bin/bash)→ upperdir 中生成 .wh.bin/bash 白名单文件,遮蔽下层同名项,原始内容不动
- 新建一个原不存在的文件(如 touch /app/log.txt)→ 直接写入 upperdir,无复制开销
- 反复读取文件(如 cat /etc/passwd)→ 始终走 lowerdir,零拷贝,不触发 CoW
共享与隔离靠层哈希与路径解析实现
镜像层本质是只读的差分快照,每层以 SHA256 哈希命名存储:
- 多个镜像若共用某层(比如都基于 debian:12-slim),Docker 只保留一份物理数据,所有引用指向同一目录
- 容器启动时,overlay2 在镜像层顶部动态叠加一个独立 upperdir,不同容器的 upperdir 完全隔离
- 访问路径时,UnionFS 从 merged 视图出发,优先查找 upperdir;未命中则逐层向下扫描 lowerdir,找到第一个匹配即返回
- 同名文件天然被上层覆盖——例如基础层有 /bin/bash,应用层 COPY 新版,容器里就只能看到新版
删除不省空间,修改让容器层变大,都是 CoW 的自然结果
理解这些行为,就能解释常见现象:
- 删文件不释放磁盘空间,是因为只是加了 .wh.* 标记,原始文件仍在 lowerdir 中
- 改一个配置文件会让容器层变大,是因为整个文件被复制进 upperdir,哪怕只改一行
- 多个容器用同一镜像却互不干扰,是因为它们共享 lowerdir,但 upperdir 各自独立
- 镜像不可变,是因为所有运行时改动都被拦截并导向 upperdir,lowerdir 始终只读











