Overlay 联合文件系统挂载实验
一、实验目标与准备
上一小节我们学习了 Overlay 三层结构(lowerdir、upperdir、merged)的原理,本节通过动手实验来验证这些理解。实验思路是:创建多个目录,然后用 mount 命令将它们进行联合挂载,模拟容器的文件系统。
实验规划
我们需要创建以下目录:
| 目录 | 角色 | 说明 |
|---|---|---|
lower1、lower2、lower3 |
传给 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
然后往 lower1、lower2、lower3 三个目录里各放一个文件,文件名和目录名对应,方便实验时区分来源:
[root@test04 ~]# echo 111 > /test/lower1/1.txt
[root@test04 ~]# echo 222 > /test/lower2/2.txt
[root@test04 ~]# echo 333 > /test/lower3/3.txt
此时 upperdir、merged、work 目录里什么都没有,这是预期的——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/lower3:lowerdir层可以指定多个目录,用冒号分隔。只要被指定为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 里的全部内容。
四、验证修改操作
为了更清晰地观察变化,开三个终端窗口分别进入 merged、upperdir、lower 目录。
在 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中原文件不变 - 新增文件 → 直接写入
upperdir,lowerdir不变 merged层本身不存放文件,只是展现upperdir+lowerdir的合并结果- 容器内的文件系统类型是 overlay,不是传统的 XFS 或 ext4
- Linux 5.4 内核为 overlay 实现了独立的读写接口,但目前仅支持同步 IO
有了这些理解,后面学习 Docker 启动容器时,就能清楚知道:Docker 引擎在创建好容器的名称空间之后,容器的 rootfs 和写目录是如何通过 overlay 联合挂载关联进去的——原理就是我们刚才实验的这种方式。下一节将直接去看 Docker 容器中的 overlay 联合挂载效果。

浙公网安备 33010602011771号