bigywater.tech
Docker · 24

overlay2 目录结构:lower、upper、work 与 merged

容器的文件系统到底在磁盘上是什么样?这篇用一个特权容器进入 Docker Desktop 的虚拟机,直接看容器的 overlay 挂载选项,列出每个 lower 目录里的文件,再看容器里写文件时 upperdir 的变化。

5 个 lowerdir
4 层镜像的容器,多出的一个是容器创建时加的层(hostname、hosts、resolv.conf 等)
upperdir 初始为空
容器刚启动时可写层是空的,新建与修改的文件才落进去
overlayfs 而不是 overlay2
docker info 的 Storage Driver;containerd snapshotter

存储驱动

实测 · 本机执行输出
  docker info:Storage Driver = overlayfs;DriverStatus = [[driver-type io.containerd.snapshotter.v1]]
  docker inspect 的 GraphDriver = null(没有 overlay2 的 LowerDir / UpperDir 字段)
  镜像 bwlab-c19 有 4 层(RootFS.Layers)

这个 Docker Desktop(Docker Engine 29.8.2)用的是 containerd 的 overlayfs snapshotter,docker info 里 Storage Driver 是 overlayfs,docker inspect 的 GraphDriver 是 null,目录在虚拟机里 /var/lib/desktop-containerd/…/snapshots/ 下,不是文章里常说的 /var/lib/docker/overlay2。经典 overlay2 驱动的目录结构我没有测。

挂载选项

实测 · 本机执行输出
  挂载点:/var/lib/docker/rootfs/overlayfs/<容器ID>;文件系统类型:overlay
  lowerdir 有 5 个目录(镜像 4 层);第一个:<snapshots>/2005/fs
  upperdir = <snapshots>/2006/fs
  workdir  = <snapshots>/2006/work
  lowerdir 的顺序:最上面一层在最前面。逐个看每个 lower 目录里有什么:
    lower[0] 有:dev etc
    lower[1] 有:big.bin
    lower[2] 有:two.txt
    lower[3] 有:one.txt
    lower[4] 有:bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
  lower[0] 里的文件:/etc/hostname /etc/resolv.conf /etc/hosts /etc/mtab /.dockerenv /dev/console(镜像只有 4 层,多出来的这一层是容器创建时加的)

进入虚拟机(特权容器加 nsenter -t 1)查 /proc/mounts,容器的根是一个 overlay 挂载,选项里有 lowerdir(5 个目录,最上层在前)、upperdir 和 workdir。多出来的那个 lower 里是 hostname、hosts、resolv.conf 等容器创建时生成的文件。

写入落在 upperdir

实测 · 本机执行输出
  容器启动后 upperdir 里:(空)
  容器里创建 new.txt、新目录 newdir,并往原有的 one.txt 追加一行之后,upperdir 里:new.txt newdir one.txt
  upperdir 里的 one.txt 内容:one | changed
  每个 lower 里的 one.txt:['无', '无', '无', 'one', '无']
  没改动的 big.bin 在 upperdir 里吗?False

新建的文件和目录直接出现在 upperdir;修改 lower 里已有的文件,先整个复制进 upperdir 再改(copy-up,见后面的专题),没改动的大文件不会被复制。

merged 视图

实测 · 本机执行输出
  在挂载点上 ls:big.bin bin dev etc home lib media mnt new.txt newdir one.txt opt proc root run sbin srv sys tmp two.txt usr var
  挂载点上 cat one.txt:one | changed;容器里 cat one.txt:one | changed

挂载点就是合并后的视图:所有层的文件并集,同名文件取最上层的。宿主(虚拟机)上 cat 和容器里 cat 的结果相同。

删除容器

实测 · 本机执行输出
  upperdir 还在吗?在;挂载点还在吗?1 行挂载记录
  docker rm -f 之后:upperdir 已删除;挂载记录 0 行
  容器创建时多出来的 lower[0]:已删除;镜像的层 lower[1]:在

docker rm -f 之后 upperdir 和容器创建时的那一层都被删除,挂载记录消失,镜像的层还在。需要保留的数据应该放在 volume 里。

分步演示

overlayfs 目录结构
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。进入虚拟机的方法:docker run --privileged --pid=host alpine:3.20 nsenter -t 1 -m -u -n -i(只读查看,没有修改虚拟机里的任何设置);目录路径里的 snapshot 编号和容器 ID 每次运行都会变;实验后已删除容器和镜像
    • 没有验证:经典 overlay2 驱动下的目录结构、删除文件产生的 whiteout(见后面的专题)、copy-up 的耗时、volume 与 bind mount 在 overlay 里的位置、不同内核版本的挂载选项差别
    京ICP备2026067502号-1京公网安备11010502063487号