unionfs通过内核挂载实现逻辑叠加而非物理合并,其核心结构为lowerdir(只读镜像层)、upperdir(可写层)和merged(统一视图),文件访问按优先级穿透查找,写时复制仅在实际写入时复制单个文件并用whiteout标记删除。

UnionFS 不是把镜像层“复制合并”成一个新文件系统,而是通过内核挂载机制,在运行时动态构造出一个统一的视图。它的核心不是物理合并,而是逻辑叠加与按需穿透。
UnionFS 的挂载结构:lowerdir、upperdir 和 merged
Overlay2(Docker 默认驱动)要求三个关键目录参与 union mount:
- lowerdir:多个只读层路径,用冒号:分隔,对应镜像所有只读层(从底层层到上层层),顺序即层叠顺序
- upperdir:单个可写目录,对应容器运行时的可写层(WritableLayer)
- merged:挂载点,用户看到的最终文件系统视图,所有读写操作都面向它
执行 mount -t overlay overlay -o lowerdir=...,upperdir=...,workdir=... /merged 后,Linux 内核 OverlayFS 驱动就开始接管对 /merged 的访问请求。
文件访问如何“穿透”多层查找
每次打开、读取或检查一个路径(如 /etc/nginx/nginx.conf),OverlayFS 按固定优先级自上而下扫描:
- 先查
upperdir—— 若存在且非 whiteout,直接返回该文件(例如容器内修改过的配置) - 若 upperdir 中无此文件,再按
lowerdir中的目录顺序依次查找(从最右到最左,即从顶层镜像层往底层查) - 找到第一个匹配项即返回;若全未命中,则报 “No such file”
这个过程完全由内核完成,不涉及磁盘数据搬运,毫秒级响应。
写时复制(Copy-on-Write)的真实触发逻辑
修改一个只读层中的文件,并不会立刻复制整层。OverlayFS 只在真正需要写入时才做最小粒度复制:
- 当 open() 以
O_RDWR或O_WRONLY打开位于 lowerdir 中的文件时,OverlayFS 自动将该文件完整拷贝到upperdir中(保留原权限、时间戳等) - 后续 write()、truncate() 等操作全部作用于
upperdir中的副本 - 删除文件则在
upperdir创建 whiteout 文件(如.wh.nginx.conf),遮盖 lowerdir 同名项
这意味着:只读层始终不变,空间复用得以维持;写操作仅影响单个文件,不拖累整层。
Docker 如何组织这些目录并注入 init 层
Docker daemon 在创建容器时,会为每个容器分配独立的 upperdir 和 workdir(用于 overlay2 内部元数据),并将镜像各层解压后的目录路径拼成 lowerdir 字符串。特别的是:
- Docker 会在
lowerdir最顶端逻辑插入一个 init 层(实际是额外挂载的一个只读 init 目录) - 该 init 层含容器启动必需但不应持久化的文件:
/etc/hostname、/etc/hosts、/etc/resolv.conf - 这些文件的修改只存在于当前容器生命周期,
docker commit时会被自动忽略
这一设计让容器既具备标准 Linux 文件语义,又隔离了运行时配置与镜像本体。











