在理解 Docker 镜像与容器的存储实现时,UnionFS / OverlayFS 与 OCI 镜像格式 是两条必须打通的主线。本文将从 Docker 在本机的实际存储目录出发,逐步拆解:
- Docker 使用的是哪种 UnionFS
- 镜像层在 OverlayFS 中如何映射
- OCI 镜像在磁盘上到底长什么样
Docker 使用的 UnionFS 类型
Docker 默认的数据目录位于 /var/lib/docker。通过直接查看该目录,可以快速判断当前 Docker 使用的 UnionFS 实现:
$ sudo ls /var/lib/docker
buildkit containers engine-id image network overlay2 plugins runtimes swarm tmp volumes
可以看到目录中存在 overlay2,这表明当前 Docker 使用的是 OverlayFS(overlay2 driver)。
拉取镜像:从逻辑概念到物理存储
拉取一个最简单的镜像作为示例:
$ docker pull busybox:latest
latest: Pulling from library/busybox
e59838ecfec5: Pull complete
Digest: sha256:2383baad1860bbe9d8a7a843775048fd07d8afe292b94bd876df64a69aae7cb1
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest
busybox 镜像非常轻量,通常只有 一层 filesystem layer,非常适合作为理解镜像结构的起点。
需要注意的是:
所有 Docker 镜像本质上都需要符合 OCI Image Specification。
OCI 镜像的逻辑结构
一个 OCI 镜像在逻辑上由以下几部分组成:
- Manifest:描述镜像由哪些层(layers)组成
- Config:记录运行时配置(Entrypoint、Env、Cmd 等)
- Layers:真正的文件系统内容(tar 包)
下图展示了一个典型的 OCI 镜像结构
OverlayFS 的工作模型
Docker 在运行容器时,会将 OCI 镜像层映射到 OverlayFS 中。OverlayFS 的核心概念如下:
OverlayFS 架构示意图
- lowerdir:只读的镜像层(image layers)
- upperdir:容器的可写层(container diff)
- merged:联合挂载点,对容器进程可见的根文件系统
从 docker inspect 看 OverlayFS 的真实路径
通过 docker inspect,可以直接看到一个镜像或容器在 OverlayFS 中的实际映射关系:
$ docker inspect redis:latest
关键字段位于 GraphDriver:
"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/.../diff:...","MergedDir": "/var/lib/docker/overlay2/.../merged","UpperDir": "/var/lib/docker/overlay2/.../diff","WorkDir": "/var/lib/docker/overlay2/.../work"},"Name": "overlay2"
}
含义非常直接:
- LowerDir:由多个镜像层的
diff目录串联而成 - UpperDir:当前容器的写层
- MergedDir:最终挂载给容器使用的根目录
- WorkDir:OverlayFS 内部使用的工作目录
同时,RootFS.Layers 中记录的 sha256 值,与 LowerDir 中的各层一一对应。
镜像层在磁盘上的样子
随便查看一个 LowerDir 对应的目录:
$ sudo ls /var/lib/docker/overlay2/17e6bb7aa1d267a6f07b37c708827d7346ef97b50661f46b21cdc2de032fcaf1/diff
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
可以看到,这实际上就是一个 Linux 根文件系统的子集。
每一层镜像,都是一次对根文件系统的“增量修改”。
OCI 镜像的物理格式:tar + blobs
OCI 镜像本质上是一个 tar 文件格式。我们可以直接将镜像保存并解包:
$ docker save redis:latest -o redis-latest.tar
$ mkdir redis-latest
$ tar xvf redis-latest.tar -C redis-latest/
解包后的目录结构如下:
blobs/
index.json
manifest.json
oci-layout
repositories
关键文件说明
- manifest.json
描述镜像由哪些 layer 组成,结构与docker inspect中的信息高度一致 - index.json
OCI 镜像索引文件,支持多架构镜像(manifest list) - oci-layout
标识这是一个 OCI layout 格式的镜像 - repositories
Docker 历史遗留文件,记录镜像名到 image ID 的映射关系
blobs/sha256:元数据与真实数据
真正的内容位于 blobs/sha256/ 目录下:
-
*.json:镜像配置或 layer 元数据 -
*.tar:实际的文件系统 layer 内容
通过 file 命令可以快速区分:
$ for blob in blobs/sha256/*; doecho "$(basename $blob): $(file $blob | cut -d: -f2)"
done
输出结果清楚地表明:
- POSIX tar archive:文件系统层
- JSON data:配置或描述信息
小结
从这次拆解可以看到:
- Docker 使用 OverlayFS(overlay2)将多个镜像层联合成容器根文件系统
- 每个镜像层在磁盘上都是一个独立的 filesystem diff
- OCI 镜像本质是由 tar + JSON 描述文件组成的标准化格式
- Docker 的运行时视图(OverlayFS)与分发视图(OCI 镜像)是同一数据的两种表达
理解这两层模型,有助于深入分析:
- 镜像体积优化
- Copy-on-Write 行为
- 容器 I/O 与性能问题
- 容器运行时(containerd / CRI-O)的实现细节
参考
- OCI Image Specification
https://specs.opencontainers.org/image-spec/

