读懂挂载树并做设计
目标
直接读取 /proc/self/mountinfo,掌握挂载树和传播方式,并用 fstab 和 systemd 单元两种形式写出绑定挂载和 tmpfs。
为什么重要
在容器时代,挂载之所以变难,是因为挂载命名空间和传播方式。在主机上挂载的卷在容器里看不到,或者容器已经删除了,挂载却还在、仍占着磁盘,这些现象全都出在这里。而这些信息不在 mount 命令的输出中,只存在于 /proc/self/mountinfo 的可选字段里。会读这个文件与不会读,差别很大。
本实验在 Ubuntu 24.04 VM 中运行。所以会用 systemctl start 把第 7 步中写的 .mount 单元真的挂载一次——你会通过亲身被拒绝,而不是死记规则,明白单元名称与路径只要有一个字符不同,systemd 就会拒绝。以前是在 Pod 中运行的,没有 mount 权限,只做到读取和设计为止。
步骤
- 创建目录
/root/mnt,从/proc/self/mountinfo的每一行中提取마운트지점 타입 옵션(占位符依次为挂载点、类型、选项)三个值,用空格分隔后保存到/root/mnt/parsed.txt。至少要有 5 行,并且必须包含/proc。 - 用
findmnt按타깃,소스,타입(列依次为目标、源、类型)的格式输出当前挂载,并保存到/root/mnt/findmnt.txt。 - 只挑出类型为
tmpfs的挂载,保存到/root/mnt/tmpfs.txt。至少要有 1 行。 - 判定每个挂载的传播方式,按
마운트지점 <shared|slave|private>(占位符为挂载点)的格式保存到/root/mnt/propagation.txt。至少要有 5 行。 - 在
/root/mnt/bind.fstab中,用一行写出下面的绑定挂载行。源/srv/data,目标/var/www/data,类型none,选项中包含bind和ro,dump 为0,pass 为0。 - 在
/root/mnt/tmpfs.fstab中,用一行写出下面的 tmpfs 行。挂载点/var/cache/app,类型tmpfs,选项中必须同时包含size=256M、mode=1777、noexec、nosuid、nodev,dump 为0,pass 为0。 - 把与第 6 步相同的内容,也写成 systemd 单元。文件名必须是对挂载点转义后的准确名称,并放在
/root/mnt/之下。必须有[Unit]、[Mount]、[Install]三个节,而且[Mount]中必须有What、Where、Type、Options四个键。 - 把
/root/mnt/design.txt写成下面这 4 行。MOUNTS=<1번 파일의 줄 수>/TMPFS=<3번 파일의 줄 수>/BIND_TARGET=/var/www/data/UNIT=<7번에서 만든 파일 이름(경로 제외)>(第 1 项、第 2 项和第 4 项的占位符依次为第 1 步文件的行数、第 3 步文件的行数、第 7 步中所创建文件的名称,不含路径)
参考
- mountinfo 以一个连字符(
-)为界,前后的字段数量不同。挂载点是第 5 个字段,类型是连字符之后的第一个字段。 - 用
findmnt -n -o TARGET,SOURCE,FSTYPE这样的形式,可以只提取想要的列。 - 单元名称可以用
systemd-escape --path --suffix=mount /var/cache/app得到。如果没有这个工具,请直接套用规则(去掉开头的斜杠,其余斜杠改为连字符)。 - 常见错误 1:在第 1 步中,可选字段的数量每行都不同,导致 awk 的固定列号错位。请先找到连字符的位置。
- 常见错误 2:在第 7 步随意起单元名称。如果名称与路径不同,systemd 会拒绝这个单元。
解析 mountinfo
创建目录 /root/mnt,从 /proc/self/mountinfo 的每一行中提取 마운트지점 타입 옵션(占位符依次为挂载点、类型、选项)三个值,用空格分隔后保存到 /root/mnt/parsed.txt。至少要有 5 行,并且必须包含 /proc。
字段分隔符是空格,中间有数量可变的可选字段。一个连字符就是它的边界。
用 findmnt 查看
用 findmnt 按 타깃,소스,타입(列依次为目标、源、类型)的格式输出当前挂载,并保存到 /root/mnt/findmnt.txt。
findmnt 是读取 /proc 的工具,所以无需特权就能工作。有指定输出格式的选项。
只挑出 tmpfs 挂载
只挑出类型为 tmpfs 的挂载,保存到 /root/mnt/tmpfs.txt。至少要有 1 行。
请只过滤出类型为 tmpfs 的。留意一下有 size 选项的和没有的。
制作传播方式表
判定每个挂载的传播方式,按 마운트지점 <shared|slave|private>(占位符为挂载点)的格式保存到 /root/mnt/propagation.txt。至少要有 5 行。
如果可选字段中有 shared: 或 master:,分别是 shared 和 slave。什么都没有就是 private。
绑定挂载的 fstab 行
在 /root/mnt/bind.fstab 中,用一行写出下面的绑定挂载行。源 /srv/data,目标 /var/www/data,类型 none,选项中包含 bind 和 ro,dump 为 0,pass 为 0。
绑定挂载的类型字段是 none,选项中要有 bind。也可以同时指定只读。
tmpfs 的 fstab 行
在 /root/mnt/tmpfs.fstab 中,用一行写出下面的 tmpfs 行。挂载点 /var/cache/app,类型 tmpfs,选项中必须同时包含 size=256M、mode=1777、noexec、nosuid、nodev,dump 为 0,pass 为 0。
设备的位置写 tmpfs。必须指定大小,并同时加入三个安全选项。
.mount 单元文件
把与第 6 步相同的内容,也写成 systemd 单元。文件名必须是对挂载点转义后的准确名称,并放在 /root/mnt/ 之下。必须有 [Unit]、[Mount]、[Install] 三个节,而且 [Mount] 中必须有 What、Where、Type、Options 四个键。
单元名称必须是对挂载点路径转义后的结果。用 systemd-escape 可以得到准确的名称。
挂载设计文档
把 /root/mnt/design.txt 写成下面这 4 行。
MOUNTS=<1번 파일의 줄 수> / TMPFS=<3번 파일의 줄 수> / BIND_TARGET=/var/www/data / UNIT=<7번에서 만든 파일 이름(경로 제외)>(第 1 项、第 2 项和第 4 项的占位符依次为第 1 步文件的行数、第 3 步文件的行数、第 7 步中所创建文件的名称,不含路径)
值从前面步骤中创建的文件里取即可。数量按行数统计就足够了。