从 Docker OverlayFS 到 OCI 镜像格式

在理解 Docker 镜像与容器的存储实现时,UnionFS / OverlayFSOCI 镜像格式 是两条必须打通的主线。本文将从 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 镜像结构
Pasted image 20260120094756


OverlayFS 的工作模型

Docker 在运行容器时,会将 OCI 镜像层映射到 OverlayFS 中。OverlayFS 的核心概念如下:

OverlayFS 架构示意图
Pasted image 20260120095803

  • 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/*; do
    echo "$(basename $blob): $(file $blob | cut -d: -f2)"
done

输出结果清楚地表明:

  • POSIX tar archive:文件系统层
  • JSON data:配置或描述信息

小结

从这次拆解可以看到:

  1. Docker 使用 OverlayFS(overlay2)将多个镜像层联合成容器根文件系统
  2. 每个镜像层在磁盘上都是一个独立的 filesystem diff
  3. OCI 镜像本质是由 tar + JSON 描述文件组成的标准化格式
  4. Docker 的运行时视图(OverlayFS)与分发视图(OCI 镜像)是同一数据的两种表达

理解这两层模型,有助于深入分析:

  • 镜像体积优化
  • Copy-on-Write 行为
  • 容器 I/O 与性能问题
  • 容器运行时(containerd / CRI-O)的实现细节

参考

posted @ 2026-01-20 23:59  uran0sh  阅读(0)  评论(0)    收藏  举报