物理主机数据盘挂载:先验收路径,再启动服务

物理主机数据盘挂载:先验收路径,再启动服务

数据盘挂载失败后,应用不一定立即报错。原本用于挂载的 /srv/data 仍是系统盘上的普通目录,服务可能继续写入,直到系统盘空间耗尽。修复挂载后,这批文件又被挂载层遮住,形成两份难以辨认的数据。上线验收应检查路径背后的文件系统,而不是只检查目录是否存在。

一、先确认数据盘身份与操作边界

本文以 Ubuntu Server 24.04 LTS、已有 ext4 文件系统的数据分区为例,不执行分区或格式化。命令通过 SSH 在主机上运行;CloudSFP 的连接凭据付款后在控制台查看,多个地址时使用第一个地址。

标配两块 1TB NVMe 各自直连,没有硬件 RAID。设备编号不等于系统盘或数据盘身份,也不能从编号推断冗余关系。若已有软 RAID 或其他存储层,挂载对象应是上层文件系统,不能绕过它挂载成员盘。

lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINTS
sudo blkid
findmnt -T /
findmnt -T /srv

结合容量、文件系统类型、现有挂载点与安装记录辨认目标分区,记录其 UUID。UUID 比设备枚举名稳定,但并非天然唯一:克隆文件系统可能复制 UUID。发现重复时先处理身份冲突,不直接写入启动配置。

以下改动应在数据盘投入业务前完成。已有业务的机器必须先停止写入、确认备份与恢复路径;不得为了测试直接卸载正在使用的文件系统。

二、先试挂载,再登记 fstab

创建空挂载点,检查其中是否已有文件。隐藏文件也要检查;若目录非空,先查明来源,不删除、不覆盖。

sudo mkdir -p /srv/data
sudo find /srv/data -mindepth 1 -maxdepth 1 -print
read -r -p 'Filesystem UUID: ' DATA_UUID
sudo blkid -U "$DATA_UUID"
sudo mount -t ext4 -o ro "UUID=$DATA_UUID" /srv/data
findmnt -M /srv/data -o TARGET,SOURCE,FSTYPE,OPTIONS,UUID

只有 blkid 输出与盘身份记录吻合,才继续执行挂载。首次只读挂载用于检查内容,并非修复损坏文件系统的手段。这里的 findmnt -M 检查精确挂载点;findmnt -T 则查找承载路径的文件系统,数据盘未挂载时可能返回根文件系统,不能仅凭它退出成功判定挂载正常。

确认来源和内容后,卸载只读挂载。备份 /etc/fstab,再编辑它;不要重复追加同一挂载点。示例中的 UUID 必须替换为刚才核对的实际值:

UUID=<已核对的文件系统UUID> /srv/data ext4 defaults,nofail,x-systemd.device-timeout=15s 0 2

这行配置允许缺盘时系统继续启动,设备等待上限是本例选择的 15 秒,不是交付或恢复承诺。若业务要求缺盘就阻断系统启动,应另行设计启动策略,不照搬 nofail。

sudo umount /srv/data
sudo cp -a /etc/fstab /etc/fstab.before-data-mount
sudoedit /etc/fstab
sudo findmnt --verify --verbose
sudo systemctl daemon-reload
sudo mount /srv/data
findmnt -M /srv/data -o TARGET,SOURCE,FSTYPE,OPTIONS,UUID

校验失败就先修正配置,不继续挂载。只挂载目标路径,避免用一次全盘挂载掩盖其他配置问题。语法检查通过也不代表设备一定可用,实际挂载结果仍需验收。

三、把服务启动与挂载绑定

nofail 解决的是系统启动行为,不负责应用安全。数据目录对应的服务必须声明挂载依赖。下面的 app.service 代表实际存在的业务单元,应替换成真实名称,并确认该单元确实启动写入进程。

执行 sudo systemctl edit app.service,添加:

[Unit]
RequiresMountsFor=/srv/data
AssertPathIsMountPoint=/srv/data

RequiresMountsFor 拉起访问此路径所需的挂载,并建立启动顺序;断言用于拒绝“目录存在但不是挂载点”的状态。两者不能识别错误的磁盘,也不能替代应用的数据一致性检查,所以 UUID 来源核对仍是独立步骤。

sudo systemctl daemon-reload
sudo systemctl cat app.service
sudo systemctl show app.service -p Requires -p After

检查覆盖配置是否生效,确认应用的实际数据目录位于 /srv/data 下。若启动脚本另行重定向路径,依赖正确也可能写错位置。定时任务、手工脚本与其他服务不会自动继承这些约束,必须分别检查。

挂载成功后再建立业务子目录,并按实际服务账户配置属主与权限。不要给整个数据盘开放所有账户写入权限,也不要把凭据写进挂载配置。

四、在正式写入前演练缺盘与恢复

演练应在没有业务数据、没有活跃写入的阶段进行。先停止应用,再停止数据挂载单元。/srv/data 对应的单元名可用 systemd-escape --path --suffix=mount /srv/data 核对,不能凭复杂路径手工猜测。

sudo systemctl stop app.service
sudo systemctl stop srv-data.mount
sudo systemctl mask --runtime srv-data.mount
sudo systemctl start app.service
sudo systemctl status app.service srv-data.mount --no-pager

临时屏蔽挂载单元,是为了模拟挂载不可用,避免启动应用时自动重新挂载。预期结果是启动失败,业务进程没有运行。若卸载提示忙,应查明占用者;不使用强制或延迟卸载绕过检查。

此时检查底层 /srv/data 是否出现新文件。测试结束后先解除屏蔽,再恢复挂载:

sudo systemctl unmask --runtime srv-data.mount
sudo systemctl start srv-data.mount
findmnt -M /srv/data -o TARGET,SOURCE,FSTYPE,OPTIONS,UUID
sudo systemctl reset-failed app.service
sudo systemctl start app.service

只有来源匹配、应用健康检查通过,才算恢复完成。验收结束时必须确认临时屏蔽已解除。启动依赖主要保护启动阶段,不是运行中磁盘故障检测器;后续还需要应用错误处理与监控。

五、留下可复核的上线记录

记录应包括盘身份、UUID、挂载点、文件系统类型、fstab 改动、服务覆盖配置和缺盘演练结果。重启验收应安排在业务投入前,确认挂载先完成、服务后启动;不能以当前一次手工挂载成功代替启动验证。

若挂载后发现系统盘空间异常,先核对是否存在被遮住的旧文件。应停止相关写入并安排检查,不能在线卸载后直接删除,更不能把底层目录与数据盘目录盲目合并。

最终通过条件是:来源正确、路径确实挂载、服务依赖生效、缺盘时没有误写、恢复后健康检查通过。挂载配置不提供冗余,也不提供备份;这两项仍需单独设计与验收。

常见问题

数据盘挂载为什么不直接使用设备名?

设备枚举顺序可能变化,应使用核对过且唯一的文件系统 UUID。克隆文件系统可能产生重复 UUID,因此仍需用 lsblk、blkid 与 findmnt 对照来源。

fstab 写了 nofail,应用就不会误写系统盘吗?

不会。nofail 只允许系统在该挂载失败时继续启动。应用还需配置 RequiresMountsFor 与 AssertPathIsMountPoint,并核对挂载来源,才能阻止数据盘缺失时写入底层目录。

AMD EPYC 7543P 独享物理主机$441.4/月起