Loading

Overlay 联合文件系统挂载实验


一、实验目标与准备

上一小节我们学习了 Overlay 三层结构(lowerdirupperdirmerged)的原理,本节通过动手实验来验证这些理解。实验思路是:创建多个目录,然后用 mount 命令将它们进行联合挂载,模拟容器的文件系统。

实验规划

我们需要创建以下目录:

目录 角色 说明
lower1lower2lower3 传给 lowerdir 作为镜像层,内容只读
upperdir 传给 upperdir 容器层,初始为空,记录改动
merged 展现层 初始为空,联合挂载后的合并结果
work 工作目录 存放 Overlay 修改文件过程中产生的临时文件,改完后会自动清理

关于 work 目录的补充:Overlay 文件系统在修改文件的过程中可能会产生一些临时文件,work 目录就是用来临时存放这些文件的,修改完成后会自动清理。这个目录也是挂载时必须指定的。


二、创建目录并写入测试文件

首先创建所有需要的目录:

[root@test04 ~]# mkdir -p /test/lower1
[root@test04 ~]# mkdir -p /test/lower2
[root@test04 ~]# mkdir -p /test/lower3
[root@test04 ~]# mkdir -p /test/upperdir
[root@test04 ~]# mkdir -p /test/work
[root@test04 ~]# mkdir /test/merged

然后往 lower1lower2lower3 三个目录里各放一个文件,文件名和目录名对应,方便实验时区分来源:

[root@test04 ~]# echo 111 > /test/lower1/1.txt
[root@test04 ~]# echo 222 > /test/lower2/2.txt
[root@test04 ~]# echo 333 > /test/lower3/3.txt

此时 upperdirmergedwork 目录里什么都没有,这是预期的——upperdir 只有发生改动才会有内容,merged 是联合挂载后的展现结果,work 存放临时文件。


三、执行联合挂载

使用 mount 命令进行联合挂载:

[root@test04 ~]# mount -t overlay overlay -o lowerdir=/test/lower1:/test/lower2:/test/lower3,upperdir=/test/upperdir,workdir=/test/work /test/merged

命令解析

  • -t overlay:指定文件系统类型为 overlay
  • 第二个 overlay:设备名(对 overlay 类型来说是固定写法)
  • -o:指定挂载参数
    • lowerdir=/test/lower1:/test/lower2:/test/lower3lowerdir 层可以指定多个目录,用冒号分隔。只要被指定为 lowerdir,目录里的内容就是只读的
    • upperdir=/test/upperdir:指定可写的容器层目录
    • workdir=/test/work:指定工作目录
  • /test/merged:最终联合合并后挂载到的目标位置(展现层)

挂载完成后,用 df 查看挂载情况:

[root@test04 ~]# df
文件系统          1K-块    已用     可用 已用% 挂载点
devtmpfs        1003228       0  1003228    0% /dev
tmpfs           1013948       0  1013948    0% /dev/shm
tmpfs           1013948    9604  1004344    1% /run
tmpfs           1013948       0  1013948    0% /sys/fs/cgroup
/dev/sda3      19936256 3939388 15996868   20% /
/dev/sda1        201380  112084    89296   56% /boot
tmpfs            202792       0   202792    0% /run/user/0
overlay        19936256 3939388 15996868   20% /test/merged

可以看到最后一行,/test/merged 挂载点的文件系统类型就是 overlay

查看 merged 目录的内容:

[root@test04 ~]# ls /test/merged/
1.txt  2.txt  3.txt

三个 lowerdir 目录里的文件全部合并展现在了 merged 层中。此时用图来表示:

┌──────────────────────────────────────┐
│  merged(展现层)                      │
│  可见: 1.txt, 2.txt, 3.txt            │
├──────────────────────────────────────┤
│  upperdir(容器层) → (空)            │
├──────────────────────────────────────┤
│  lowerdir(镜像层,只读)               │
│  lower1: 1.txt   lower2: 2.txt       │
│  lower3: 3.txt                        │
└──────────────────────────────────────┘

upperdir 没有任何文件,没有遮挡,所以 merged 层直接看到 lowerdir 里的全部内容。


四、验证修改操作

为了更清晰地观察变化,开三个终端窗口分别进入 mergedupperdirlower 目录。

merged 层修改 1.txt 的内容:

[root@test04 ~]# cd /test/merged/
[root@test04 merged]# ls
1.txt  2.txt  3.txt
[root@test04 merged]# vim 1.txt    # 将内容改为 1116666666666

修改完成后,检查 upperdir 目录——按照之前的理论,改动结果应该出现在 upperdir 里:

[root@test04 ~]# cd /test/upperdir/
[root@test04 upperdir]# ls
1.txt
[root@test04 upperdir]# cat 1.txt
1116666666666

确实如此,upperdir 中出现了同名的 1.txt,内容就是修改后的新版本。

再检查 lower1 里的原始 1.txt 有没有被改动:

[root@test04 ~]# cd /test/lower1/
[root@test04 lower1]# ls
1.txt
[root@test04 lower1]# cat 1.txt
111

lowerdir 是只读的,原始文件内容仍然是 111,没有被改动。验证通过:修改操作只影响 upperdir,不影响 lowerdir


五、验证删除操作

merged 层删除 2.txt

[root@test04 merged]# rm -rf 2.txt
[root@test04 merged]# ls
1.txt  3.txt

展现层中 2.txt 确实消失了。接下来检查 upperdir 里发生了什么:

[root@test04 upperdir]# ls
1.txt  2.txt
[root@test04 upperdir]# ll
总用量 4
-rw-r--r-- 1 root root   14 5月  25 11:45 1.txt
c--------- 1 root root 0, 0 5月  25 11:48 2.txt

注意看 2.txt 这一行:文件权限最前面的标识是 c,表示这是一个字符设备文件(character device),而不是普通文件(普通文件的标识是 -)。这就是上一节说的 whiteout 文件——Overlay 通过在 upperdir 中创建一个特殊的字符设备文件来标记"该文件已被删除"。

lowerdir 里的原始 2.txt 还在不在?

[root@test04 lower1]# cd /test/lower2/
[root@test04 lower2]# ls
2.txt
[root@test04 lower2]# cat 2.txt
222

文件仍然完好无损。验证通过:删除操作不会真正删除 lowerdir 中的文件,只是在 upperdir 中创建 whiteout 标记。


六、验证新增操作

merged 层新增一个文件:

[root@test04 merged]# echo 4444 > 4.txt

检查 upperdir

[root@test04 upperdir]# ls
1.txt  2.txt  4.txt
[root@test04 upperdir]# cat 4.txt
4444

新增的 4.txt 直接出现在了 upperdir 中。验证通过:新增文件写入 upperdir

此时整个 Overlay 的状态如图所示:

┌──────────────────────────────────────────────────┐
│  merged(展现层)                                  │
│  可见: 1.txt(已修改), 3.txt, 4.txt                 │
├──────────────────────────────────────────────────┤
│  upperdir(容器层)                                │
│  1.txt(修改后)  2.txt(whiteout标记)  4.txt(新增)   │
├──────────────────────────────────────────────────┤
│  lowerdir(镜像层,只读)                           │
│  lower1: 1.txt(111)  lower2: 2.txt(222)           │
│  lower3: 3.txt(333)                               │
└──────────────────────────────────────────────────┘

七、容器内的文件系统类型是 overlay,不是 XFS/ext4

通过刚才的挂载实验,大家应该注意到了:在 df 输出和 mount 命令中,文件系统类型指定的是 overlay。这引出一个非常重要的知识点——容器内挂载的文件系统类型不是传统的 ext4 或 XFS,而是 overlay 文件系统。

这意味着容器内发生的所有读写操作,都是在跟 overlay 文件系统打交道。Overlay 在这里的角色叫做 Storage Driver(存储驱动),它直接关系到容器的读写行为。

Overlay 与宿主机文件系统的层级关系

虽然容器内使用的是 overlay 文件系统,但归根结底 overlay 是寄生在宿主机之上的。宿主机底层用的文件系统可能是 XFS 或 ext4,overlay 相当于在宿主机文件系统之上的更上一层

容器读写 → overlay 文件系统 → 宿主机底层文件系统(XFS / ext4)

Linux 5.4 内核对 Overlay 的改动

在 Linux 内核 5.4 中,为了完善 overlay 文件系统,内核单独为 overlay 实现了自己的读写接口。在此之前,overlay 会直接把读写请求转发给宿主机底层的 XFS 或 ext4 文件系统;5.4 之后,容器发起的读写请求直接调用 overlay 本身的读写接口,不再直接调用后端的宿主机文件系统。

但这个改动也带来了一个问题:overlay 自己的读写接口目前只实现了同步 IO,没有实现异步 IO

IO 类型 行为 特点
异步 IO 数据先写到内存(page cache / buffer cache)中缓存,攒一波后定期刷盘 减少 IO 次数,性能更好
同步 IO 数据写到内存后立马往硬盘刷 IO 次数多,性能相对较差

所以如果在 5.4 内核上测试 overlay 的异步 IO 性能,可能会发现很慢,这是因为 overlay 自身尚未实现异步 IO 导致的,需要注意这个版本差异。


八、本节总结与下一步

通过本节的联合挂载实验,我们用实际操作验证了 Overlay 三层结构的工作原理:

  • 修改文件upperdir 中产生同名文件(新内容),lowerdir 不变
  • 删除文件upperdir 中产生 whiteout 字符设备文件标记删除,lowerdir 中原文件不变
  • 新增文件 → 直接写入 upperdirlowerdir 不变
  • merged本身不存放文件,只是展现 upperdir + lowerdir 的合并结果
  • 容器内的文件系统类型是 overlay,不是传统的 XFS 或 ext4
  • Linux 5.4 内核为 overlay 实现了独立的读写接口,但目前仅支持同步 IO

有了这些理解,后面学习 Docker 启动容器时,就能清楚知道:Docker 引擎在创建好容器的名称空间之后,容器的 rootfs 和写目录是如何通过 overlay 联合挂载关联进去的——原理就是我们刚才实验的这种方式。下一节将直接去看 Docker 容器中的 overlay 联合挂载效果。

posted @ 2026-06-19 09:55  知杏  阅读(15)  评论(0)    收藏  举报