跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Shell / Bash

Docker 镜像原理与分层存储机制详解

综述由AI生成Docker 镜像基于联合文件系统(UnionFS)实现分层存储,包含只读层与可写层。通过写时复制(CoW)机制确保镜像不可变性与存储效率。实战演示了使用 docker history 和 inspect 命令分析镜像层结构,以及手动配置 overlay 文件系统验证 merged、upper、lower 目录的文件读写与删除逻辑。

战神发布于 2026/3/15更新于 2026/6/1137 浏览
Docker 镜像原理与分层存储机制详解

Docker 是操作系统层的虚拟化,所以 Docker 镜像的本质是在模拟操作系统。

联合文件系统(UnionFS)

联合文件系统(UnionFS) 是 Docker 镜像实现分层存储的核心技术,它通过将多个只读层(Image Layers)和一个可写层(Container Layer)叠加,形成一个虚拟的、统一的文件系统。

核心概念

分层存储:

  • Docker 镜像由多个只读层组成,每一层对应一个构建步骤(如 RUN、COPY 指令)。
  • 这些层通过 UnionFS 叠加在一起,形成一个逻辑上的完整文件系统。

只读层与可写层:

  • 只读层:镜像的每一层都是只读的,确保了镜像的一致性和不可变性。
  • 可写层:当容器启动时,Docker 会在镜像的只读层之上添加一个可写的容器层,用于记录容器的文件修改。

共享与复用:

  • 多个容器可以共享同一个镜像的只读层,节省存储空间。
  • 不同容器之间的可写层相互隔离,互不影响。

工作原理

文件系统的叠加:

  • UnionFS 将多个只读层和一个可写层按照顺序叠加,形成一个统一的视图。
  • 当访问文件时,UnionFS 会从顶层开始查找,直到找到目标文件为止。

写时复制(Copy-on-Write, CoW):

  • 如果容器需要修改只读层中的文件,UnionFS 会先在可写层中创建一个该文件的副本,然后对副本进行修改。
  • 这种机制避免了直接修改只读层,确保了镜像的不可变性。

透明性:

  • 对于用户来说,UnionFS 提供的文件系统视图是透明的,用户无需关心文件的实际存储位置。

实现方式

常见实现:

  • AUFS:早期 Docker 使用的 UnionFS 实现,但已逐渐被其他实现取代。
  • OverlayFS:现代 Linux 内核中广泛使用的 UnionFS 实现,性能更优。
  • Btrfs、ZFS:其他支持 UnionFS 的文件系统,但使用较少。

OverlayFS 的分层结构:

  • LowerDir:镜像的只读层,由多个目录组成。
  • UpperDir:容器的可写层。
  • WorkDir:用于存储 OverlayFS 的临时数据。
  • MergedDir:最终呈现给容器的统一文件系统视图。

在 Docker 中的应用场景

镜像构建:

  • Docker 通过 UnionFS 将多个构建步骤的层叠加,形成一个完整的镜像。
  • 每个层都可以被其他镜像共享,减少了存储空间的占用。

容器运行:

  • 容器启动时,Docker 会在镜像的只读层之上添加一个可写层,用于记录容器的文件修改。
  • 这种设计使得容器可以快速启动,并且多个容器可以共享同一个镜像。

镜像分发:

  • 由于镜像的层是共享的,Docker 在分发镜像时只需要传输新增的层,大大减少了网络带宽的占用。

UnionFS 的优势

存储效率:

  • 通过共享层,减少了存储空间的占用。
  • 写时复制机制避免了重复的数据复制。

性能优化:

  • OverlayFS 等现代 UnionFS 实现具有较高的性能,能够满足容器化应用的需求。

灵活性:

  • 支持动态添加和删除层,方便镜像的管理和更新。

Docker 分层存储机制

Docker 分层存储机制是 Docker 镜像构建与运行的核心技术,通过将镜像和容器的数据存储在多个独立的层中,实现了高效、灵活的镜像管理和容器运行。

基本概念

镜像层(Image Layer)

  • Docker 镜像由多个只读层组成,每一层对应一个构建步骤(如 RUN、COPY、ADD 指令)。
  • 这些层通过 联合文件系统(UnionFS) 技术叠加,形成一个逻辑上的完整文件系统。
  • 每个层只存储与前一层相比的增量变化,避免重复数据存储。

容器层(Container Layer)

  • 当容器启动时,Docker 会在镜像的只读层之上添加一个可写的容器层。
  • 容器运行时的所有修改(如文件创建、修改、删除)都记录在容器层中,不会影响镜像层。

工作原理

镜像构建

  • 构建镜像时,每执行一条 Dockerfile 指令,都会生成一个新的镜像层。
  • 例如,执行 RUN apt-get update && apt-get install -y nginx 会在基础镜像层之上新增一层,记录安装的 Nginx 软件包。

容器运行

  • 容器启动时,Docker 会将镜像的只读层与容器层联合挂载,形成一个可读写的文件系统视图。
  • 容器层是临时的,当容器停止或删除时,容器层的修改会丢失(除非通过卷或绑定挂载持久化数据)。

写时复制(Copy-on-Write, CoW)

  • 当容器需要修改只读层中的文件时,Docker 会将该文件复制到容器层,然后在容器层中进行修改。
  • 这种机制保证了镜像层的不可变性,同时提高了容器启动速度。

分层存储的优势

镜像复用与共享

  • 多个容器可以共享同一个镜像的只读层,减少存储空间占用。
  • 例如,多个基于同一基础镜像构建的应用镜像可以共享基础镜像层。

高效构建与部署

  • 镜像构建时,Docker 会利用缓存机制。如果某一层的内容没有变化,Docker 会直接使用缓存的层,而不需要重新构建。
  • 镜像的分层存储使得镜像的传输和存储更加高效。

版本控制与回滚

  • 每一层的变化都可以被追踪,开发者可以轻松地回滚到之前的版本,或者在不同版本之间切换。

快速启动

  • 由于容器启动时只需添加一个轻量级的可写层,而不是重新创建整个文件系统,因此容器启动速度非常快。

实现细节

层标识符(Layer ID)

  • 每个镜像层都有一个唯一的标识符,用于在不同的镜像之间共享。

存储驱动(Storage Driver)

  • Docker 支持多种存储驱动,如 AUFS、OverlayFS、Device Mapper 等,这些驱动都实现了分层存储的机制。
  • 例如,OverlayFS 是现代 Linux 系统中广泛使用的存储驱动,性能更优。

层的内容

  • 每一层包含了文件系统的变化,例如添加、删除或修改的文件。这些变化以增量方式存储,只有发生变化的部分会被存储。

镜像分层存储实战

先拉取镜像

docker pull nginx:1.21.1

通过 docker image history 查看如下

root@VM-8-12-ubuntu:/data/ahri# docker history nginx:1.21.1
IMAGE         CREATED       CREATED BY                                      SIZE      COMMENT
822b7ec2aaf2  3 years ago   /bin/sh -c #(nop) CMD ["nginx", "-g", "daemon…" 0B        
<missing>     3 years ago   /bin/sh -c #(nop) STOPSIGNAL SIGQUIT            0B        
<missing>     3 years ago   /bin/sh -c #(nop) EXPOSE 80                     0B        
<missing>     3 years ago   /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr…"  0B        
<missing>     3 years ago   /bin/sh -c #(nop) COPY file:09a214a3e07c919a…  4.61kB    
<missing>     3 years ago   /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…  1.04kB    
<missing>     3 years ago   /bin/sh -c #(nop) COPY file:0b866ff31ef5b0…    1.96kB    
<missing>     3 years ago   /bin/sh -c #(nop) COPY file:65504f71f5855ca0…  1.2kB     
<missing>     3 years ago   /bin/sh -c set -x && addgroup --system -…       63.9MB    
<missing>     3 years ago   /bin/sh -c #(nop) ENV PKG_RELEASE=1~buster      0B        
<missing>     3 years ago   /bin/sh -c #(nop) ENV NJS_VERSION=0.6.1         0B        
<missing>     3 years ago   /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.1      0B        
<missing>     3 years ago   /bin/sh -c #(nop) LABEL maintainer=NGINX Do…    0B        
<missing>     3 years ago   /bin/sh -c #(nop) CMD ["bash"]                  0B        
<missing>     3 years ago   /bin/sh -c #(nop) ADD file:4ff85d9f6aa246746…  69.3MB

可以看到 dockerfile 和做出来的镜像是对应的,而且不是每一层都占用空间的。

我们再通过 inspect 命令查看该镜像的存储位置。

root@VM-8-12-ubuntu:/data/ahri# docker image inspect nginx:1.21.1
[
  {
    "GraphDriver": {
      "Data": {
        "LowerDir": "/data/var/lib/docker/overlay2/ec2f5f43a9a6f4e7063fb6ef633103b1bca417f34488a4a48736758f9eb6019f/diff:/data/var/lib/docker/overlay2/e368569b4eb5117dabbb84864913883ecf8f50130097619ce128a7bcdf141092/diff:...",
        "MergedDir": "/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged",
        "UpperDir": "/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/diff",
        "WorkDir": "/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/work"
      },
      "Name": "overlay2"
    },
    "RootFS": { "Type": "layers", "Layers": [...] },
    "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" }
  }
]

可以看到 GraphDriver 也就是我们的存储驱动,是 overlay2 的存储驱动;nginx 的 overlay2 的四个目录也都显示出来了。我们知道 Docker 的默认目录是 /var/lib/docker,之所以在 /data/var/lib/docker 下面是因为我们规划了磁盘,调整了默认的存储目录。

可以看到 lowerdir,upperdir,都位于 /data/var/lib/docker/overlay2 下面,因为我们调整过默认存储位置所以对比默认的 /var/lib/docker 多了 /data。我们进入到

cd /data/var/lib/docker/overlay2

该目录下,查找我们的 nginx,看下文件的怎么存储的

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# tree -P nginx -f | grep "/sbin/nginx"
./0b85066e21c3238a8d0214b82399b41b6fedfbebe48ba88ab45d0947089685/diff/usr/sbin/nginx
./db3ef3edafc0f2fe808eb68b6f53aec88b9c2e6fd525bb7e7cf9bbfec464992b/diff/usr/sbin/nginx
...

-P nginx 选项表示只显示匹配模式 nginx 的文件或目录名。从当前目录开始,递归地列出所有匹配 nginx 的文件或目录(通过 tree -P nginx -f)。然后,通过 grep 进一步过滤,只显示路径中包含 /sbin/nginx 的行。

搜索后可以看到我们找到了多个 nginx 文件,因为我们本地有多个 nginx 镜像所以搜到了多个 nginx 文件,通过 lowerdir 的值我们可以确定有一个是和我们 nginx:1.12.1 的匹配上的。

同样的方式我们通过 Dockerfile 发现,nginx 还存储了个 docker-entrypoint.sh,我们搜索这个文件,我们发现这个文件也被放到了 diff 目录下面,和我们的 lowerdir 中一个 layer 是对应的。

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# tree -P "docker-entrypoint.sh" -f | grep "docker-entrypoint.sh"
./2b130b497b862c6acff53f31ebdbc5ecf772345c2b9ce4326f620bcef741507d/diff/usr/local/bin/docker-entrypoint.sh
./4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1/diff/docker-entrypoint.sh
...

接下来我们进入 diff 的上一级目录查看

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# cd 4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# ls committed diff link lower work

可以看到 link 文件,里面是每一个 diff 目录的短名称,或者说软链接

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# cat ./link
J6U3CHDJ5RRYP6ONH2VJLP34P2

通过遍历 l 目录我们会发现,整个 Docker 的镜像的 diff 目录都被做了对应的软链接,或者说起了个短名称。

每一个 diff 是一个层级的内容,层级的关系是存放到了 lower 文件中,里面存放着父级的层级。

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# cd 4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# cat lower
l/HUIS7U7MEMOI47VSVORK45VAB4:l/BDTHLGWDJQYNWQQP4AH2NAT3BE

最后我们查看下 mergeddir,发现 merged dir 是不存在的,当我们启动为容器的时候才是有有效的。

root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# ll b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged
ls: cannot access 'b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged': No such file or directory
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# docker run -d --name mylayer nginx:1.21.1 f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# docker inspect mylayer
[
  {
    "Id": "f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea",
    "Created": "2025-04-15T12:33:55.683157888Z",
    "Path": "/docker-entrypoint.sh",
    "Args": ["nginx", "-g", "daemon off;"],
    "State": { "Status": "running", "Running": true, ... },
    "Image": "sha256:822b7ec2aaf2...",
    "GraphDriver": { "Data": { "LowerDir": "...", "MergedDir": "...", "UpperDir": "...", "WorkDir": "..." }, "Name": "overlay2" },
    ...
  }
]

我们通过镜像实际存储位置可以看到镜像在存储的时候,通过分层来实现,并通过 link 和 lower 完成层与层之间链接关系配置,diff 存放了我们的内容,并且没有什么加密。

overlay 文件系统工作实战

我们首先创建一个目录用来挂载我们的文件系统

mkdir -p /data/myworkdir/fs

创建文件系统的工作目录

root@VM-8-12-ubuntu:/data# cd /data/myworkdir/fs
root@VM-8-12-ubuntu:/data/myworkdir/fs# ls
root@VM-8-12-ubuntu:/data/myworkdir/fs# mkdir upper lower merged work

准备一些文件

root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "in lower" > lower/in_lower.txt
root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "in upper" > upper/in_upper.txt
root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "In both. from lower" > lower/in_both.txt
root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "In both. from upper" > upper/in_both.txt

挂载 overlay 目录

mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged

通过 df -h 可以看到我们完成了挂载

root@VM-8-12-ubuntu:/data/myworkdir/fs# df -h
Filesystem Size Used Avail Use% Mounted on
... 
overlay 50G 29G 19G 61% /data/myworkdir/fs/merged

此时看下目录结构,然后发现 merged 目录自动生成了 3 个文件,可以看到 both, lower, upper 都在。merged 目录其实就是用户看到的目录,用户的实际文件操作在这里进行。

root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a .
.
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
├── upper
│   ├── in_both.txt
│   └── in_upper.txt
└── work
    └── work
5 directories, 8 files

merged 目录下编辑一下 in_lower.txt,upper 目录下就会马上出现一个 in_lower.txt,而且内容就是编辑后的。而 lower 目录下的 in_lower.txt 内容不变

root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# vi in_lower.txt
root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# cat in_lower.txt
in lower! after edit!
root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# cd ..
root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a .
...
root@VM-8-12-ubuntu:/data/myworkdir/fs# cat upper/in_lower.txt
in lower! after edit!

如果我们删除 in_lower.txt,lower 目录里的 in_lower.txt 文件不会有变化,只是在 upper/ 目录中增加了一个特殊文件来告诉 OverlayFS,in_lower.txt 这个文件不能出现在 merged/ 里了,类似 AuFS 的 whiteout

root@VM-8-12-ubuntu:/data/myworkdir/fs# rm -f merged/in_lower.txt
root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a .
...
root@VM-8-12-ubuntu:/data/myworkdir/fs# ll upper/
total 16
drwxr-xr-x 2 root root 4096 Apr 15 20:49 ./
drwxr-xr-x 6 root root 4096 Apr 15 20:39 ../
-rw-r--r-- 1 root root 20 Apr 15 20:40 in_both.txt
c--------- 1 root root 0, 0 Apr 15 20:49 in_lower.txt
-rw-r--r-- 1 root root 9 Apr 15 20:39 in_upper.txt

注意到 upper 下 in_lower.txt 的文件类型没有 -,而是 c 不是 - 或者 d。

可以看到这种文件系统对于底层来说不影响,共享比较容易,但是如果编辑,删除频繁的话,性能还是比较差的。要不停的拷贝或者标记。

目录

  1. 联合文件系统(UnionFS)
  2. 核心概念
  3. 工作原理
  4. 实现方式
  5. 在 Docker 中的应用场景
  6. UnionFS 的优势
  7. Docker 分层存储机制
  8. 基本概念
  9. 工作原理
  10. 分层存储的优势
  11. 实现细节
  12. 镜像分层存储实战
  13. overlay 文件系统工作实战
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 网络安全从业人员必备认证证书指南
  • Go Map 进化史:从桶链式哈希表到 Swiss Table 源码剖析
  • Linux 高级 IO:五种 IO 模型与非阻塞实现
  • 舔狗转正!我花了三天狂测 Megick Studio,彻底跪了!还当什么舔狗啊!
  • AI 与大模型的核心差异深度解析
  • LLM 大模型实践指南:从入门到应用开发路线
  • 卷积神经网络(CNN)原理、数学表达与工程实践详解
  • SpringBoot 项目从零搭建指南
  • 《SCUM》自建服务器与虚拟局域网联机教程
  • 降低论文 AIGC 疑似度的方法与工具
  • AI-Render:在 Blender 中集成 Stable Diffusion 进行图像渲染
  • 自学网络安全:学习误区、路线规划与资源推荐
  • 前端权限管理实现方案与最佳实践
  • DeepSeek-R1 大模型基于 MS-Swift 框架部署、推理与微调实践
  • AIGC 与艺术创作:机遇与变革
  • 基于 AI 大模型的培训题库自动生成方案与实践
  • AI 大模型对会计工作与财务软件的影响分析
  • LLaMA 2/3、Qwen 与 DeepSeek 开源大模型技术对比分析
  • Whisper-medium.en 企业级英文语音转写实战指南
  • 网络安全学习资源与常用安全论坛汇总

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online