2024 年,我给一台服务器加了一块新的数据盘,要挂到 /data 给数据库用。挂载本身很顺利:分区、格式化、mount,几条命令下去,df 里清清楚楚多出了 /data,我往里写文件也一切正常。为了让这块盘开机后能自动挂上,我又往 /etc/fstab 里加了一行——这一步我做得飞快,凭着记忆敲了一行,看着"挺像那么回事",保存退出。当晚没出任何问题。可一周后,这台机器因为别的原因重启了一次,然后……它就再也没起来。SSH 连不上,我让机房同事接了显示器过去,屏幕上是一行刺眼的字:Welcome to emergency mode。一块只是用来存数据的盘,我只是想让它"开机自动挂载",怎么就把整台服务器搞到开不了机了?这件事逼着我把 Linux 的磁盘挂载、/etc/fstab、文件系统这一整套彻底理清了。本文复盘这次实战。
问题背景
环境:CentOS 7,给服务器加了一块新数据盘
事故现象:
- 手动 mount 新盘到 /data,完全正常,能读能写
- 往 /etc/fstab 加了一行让它开机自动挂载
- 一周后机器重启,卡在 emergency mode,系统起不来
现场排查(机房接显示器):
# 1. emergency mode 下,先看是谁拦住了开机
Welcome to emergency mode!
... see "systemctl status" ...
Give root password for maintenance:
# 2. 输入 root 密码进去,看启动日志
$ journalctl -xb | grep -i 'fail\|mount'
... Failed to mount /data.
... Dependency failed for Local File Systems.
... systemd[1]: Mounting /data... -> failed
# ★ 真相:开机挂载 /data 失败,把整个启动流程拖垮了
# 3. 看我当初加的那行 fstab
$ cat /etc/fstab
...
/dev/sdb1 /data ext4 defaults 0 0
# ★ 问题就在这一行
# 4. 看现在这块盘的设备名
$ lsblk
sdb 8:16 0 100G 0 disk
└─sdb1 8:17 0 100G 0 part # 没挂载
根因(后来想清楚的):
1. ★ fstab 里我写的是 /dev/sdb1。但磁盘的设备名
(sdb/sdc...)是【内核按发现顺序临时分配】的,
它【不保证每次开机都一样】。
2. 那次重启,可能因为加了别的设备 / 识别顺序变化,
这块盘的名字不再是 sdb1 -> fstab 那行指向了
一个不存在的设备 -> 挂载失败。
3. ★ 致命的是:fstab 里的条目,默认是开机的【强依赖】。
一个挂载失败,systemd 认为"本地文件系统没准备好",
直接中止正常启动,掉进 emergency mode。
4. 两个错叠加:① 用了会变的设备名,不该用;
② 没加 nofail,让一块数据盘的挂载,有权否决整个开机。
fstab 写错,代价是整台机器开不了机。
修复 1:一块新磁盘到系统能用,要走几步
# === ★ 先理清:一块裸盘到能往里写文件,中间有几道工序 ===
# 裸盘 -> [分区] -> [格式化建文件系统] -> [挂载到目录] -> 能用
# === 第一步:看看系统认到了哪些盘 ===
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 50G 0 disk
└─sda1 8:1 0 50G 0 part / # 系统盘,已挂在 /
sdb 8:16 0 100G 0 disk # ★ 新盘,还是裸的
# lsblk 一眼看清:有哪些盘、分了哪些区、各自挂在哪。
# === 第二步:给新盘分区(也可以整盘不分区直接用)===
$ fdisk /dev/sdb # 交互式分区,大于 2T 用 parted
# 或用 parted:parted /dev/sdb mklabel gpt + mkpart ...
$ lsblk # 分完应能看到 sdb1
# === 第三步:格式化 —— 在分区上【建立文件系统】===
$ mkfs.ext4 /dev/sdb1 # 建 ext4 文件系统
# 或 mkfs.xfs /dev/sdb1 # CentOS 7 默认更推荐 xfs
# ★ 格式化会清空分区数据,执行前务必确认设备名没敲错!
# === 第四步:挂载 —— 把文件系统【接到目录树的某个点】===
$ mkdir /data # 先准备好挂载点目录
$ mount /dev/sdb1 /data # 把文件系统挂到 /data
$ df -h /data # 确认挂上了
/dev/sdb1 100G ... /data
# === ★ "挂载"到底是什么意思 ===
# Linux 没有 C盘 D盘。所有东西都在【一棵目录树】上。
# 挂载,就是把一个文件系统,"接"到这棵树的某个目录上 ——
# 从此访问那个目录,就是在访问那块盘。
# 挂载点目录原有的内容,会在挂载期间被【临时遮盖】。
修复 2:mount 临时挂载 vs /etc/fstab 永久挂载
# === ★ 这次踩坑的认知起点:mount 命令的效果【不持久】 ===
# === mount 命令:只是【当下这次】把盘挂上 ===
$ mount /dev/sdb1 /data
# 它确实把盘挂好了,能读能写 —— 但这只是【运行时】状态。
# ★ 机器一重启,这次手动 mount 的效果【全部消失】,
# /data 又变回一个空目录。
# === 想让挂载"开机自动恢复",只有一个正路:写 /etc/fstab ===
$ cat /etc/fstab
# <设备> <挂载点> <类型> <选项>
UUID=xxxx-xxxx / xfs defaults 0 0
UUID=yyyy-yyyy /data ext4 defaults,nofail 0 2
# 开机时,systemd 会读这个文件,把里面每一行都挂上。
# === ★ 卸载 ===
$ umount /data # 卸载
$ umount /dev/sdb1 # 用设备名卸也行
# 卸载失败提示 "target is busy" -> 有进程正在用这个目录:
$ lsof /data # 或 fuser -m /data,看谁占着
$ cd ~ # 最常见:你自己 shell 就 cd 在里面
# === ★ 这次的根本误区 ===
# 我以为 mount 命令"挂好了"就一劳永逸 ——
# 其实 mount 管"现在",fstab 管"以后每次开机"。
# 真正危险的不是 mount,是我往 fstab 里【写错了一行】。
# === 看当前系统所有挂载情况 ===
$ mount | column -t # 列出所有已挂载的文件系统
$ findmnt # ★ 树状展示,更直观
$ findmnt /data # 单独看某个挂载点的详情
修复 3:/etc/fstab 六个字段——逐个看懂
# === ★ fstab 每一行,是空格/制表符分隔的【六个字段】 ===
# UUID=yyyy-yyyy /data ext4 defaults,nofail 0 2
# ① ② ③ ④ ⑤ ⑥
# === 字段①:设备 —— 挂【哪一块】 ===
# 可以写 /dev/sdb1,也可以写 UUID=... 或 LABEL=...
# ★ 强烈建议用 UUID(下一节专门讲为什么)。
# === 字段②:挂载点 —— 挂到目录树的【哪个目录】 ===
# 这个目录必须【已经存在】。
# === 字段③:文件系统类型 ===
# ext4 / xfs / vfat / ntfs / swap ... 不确定可写 auto 让系统自己认。
# === 字段④:挂载选项 —— ★ 坑最多、也最重要的一栏 ===
# defaults 一组常用默认选项(rw,suid,dev,exec,auto,nouser,async)
# ro / rw 只读 / 读写
# noatime 不记录文件访问时间 —— 减少写、提性能,数据盘常加
# ★ nofail 挂载失败【也不让它阻塞开机】—— 这次的救命选项!
# ★ _netdev 网络设备(NFS等),等网络就绪后再挂
# 多个选项用【逗号】连,中间不要有空格。
# === 字段⑤:dump —— 是否被 dump 工具备份 ===
# 几乎都填 0。基本是历史遗留,不用纠结。
# === 字段⑥:fsck 开机检查顺序 —— ★ 别乱填 ===
# 0 = 开机不检查这个文件系统
# 1 = ★ 只给【根文件系统 /】用
# 2 = 其他需要检查的文件系统
# 数据盘填 2,根分区填 1,swap 和不想检查的填 0。
# === ★ 一行安全的数据盘 fstab,长这样 ===
UUID=yyyy-yyyy /data ext4 defaults,nofail,noatime 0 2
# 用 UUID、带 nofail —— 这两点,正是我当初那行缺的。
修复 4:为什么用 UUID 不用 /dev/sdb1
# === ★ 这次事故的直接导火索:fstab 里写了 /dev/sdb1 ===
# === 设备名 sda/sdb/sdc 是【不稳定】的 ===
# 这些名字,是内核在开机时,【按发现磁盘的顺序】临时分配的。
# ★ 它不是磁盘的"固有属性",只是这次开机的一个临时编号。
# 什么时候会变:
# - 加了 / 拔了别的磁盘
# - 换了磁盘插槽 / 控制器
# - 内核、驱动识别顺序变化
# -> 你这次的 sdb1,下次开机可能就成了 sdc1。
# fstab 里写死 /dev/sdb1,等于把挂载赌在一个会变的名字上。
# === ★ 正解:用 UUID —— 文件系统的"身份证",终身不变 ===
# UUID 是格式化时写进文件系统的全局唯一标识,
# 盘插哪个口、设备名怎么变,UUID 都【不变】。
# === 查一块盘的 UUID ===
$ blkid /dev/sdb1
/dev/sdb1: UUID="a1b2c3d4-..." TYPE="ext4"
# 或看全部:
$ lsblk -f
NAME FSTYPE UUID MOUNTPOINT
sdb1 ext4 a1b2c3d4-...
# === fstab 里就用 UUID 写 ===
UUID=a1b2c3d4-... /data ext4 defaults,nofail 0 2
# === 也可以用 LABEL(卷标),同样稳定 ===
$ e2label /dev/sdb1 datadisk # 给 ext 文件系统打卷标
$ xfs_admin -L datadisk /dev/sdb1 # xfs 打卷标
# fstab 里:LABEL=datadisk /data ...
# === ★ 一句话纪律 ===
# fstab 里【永远】用 UUID 或 LABEL,【永远不要】用 /dev/sdX。
# 设备名是给"此刻"看的,fstab 是给"以后每次开机"用的 ——
# 用一个临时的名字,去定义一件长期的事,迟早出事。
修复 5:fstab 写错导致开机失败——怎么自救
# === ★ fstab 写错,最严重的后果就是这次:开不了机 ===
# 一个 fstab 条目挂载失败,默认会让 systemd 判定
# "本地文件系统未就绪",中止启动,进入 emergency mode。
# === emergency / rescue mode 里怎么自救 ===
# 1. 屏幕提示 "Give root password for maintenance",输 root 密码
# 2. ★ 此时根分区往往是【只读】的,先把它改成可读写:
$ mount -o remount,rw /
# 3. 改回那行有问题的 fstab(用 vi 把错误行修对或注释掉):
$ vi /etc/fstab
# 4. ★ 改完【先验证】,别急着重启:
$ mount -a
# mount -a = 按 fstab 把所有条目挂一遍。
# 没报错 -> fstab 现在是对的,可以放心重启;
# 报错 -> 哪行错继续改,直到 mount -a 干净通过。
# 5. 确认无误再 reboot。
# === ★ 真正的护身符:数据盘那行一定加 nofail ===
UUID=... /data ext4 defaults,nofail 0 2
# 加了 nofail:这个盘开机时挂不上,systemd 只是【跳过它】,
# 继续正常启动 —— 系统照样进得去,你登进去再慢慢修。
# ★ 一块数据盘的挂载,绝不该有权"否决"整台机器的开机。
# === 还可以再加 x-systemd 选项,更稳 ===
UUID=... /data ext4 nofail,x-systemd.device-timeout=10 0 2
# x-systemd.device-timeout=10 :等这个设备最多 10 秒,
# 超时就放弃,不会傻等导致开机巨慢。
# === ★ 改 fstab 的安全流程(以后每次都这么做)===
# 1. 改之前先备份:cp /etc/fstab /etc/fstab.bak
# 2. 数据盘行务必用 UUID + 带 nofail
# 3. 改完【不要直接重启】,先 mount -a 验证
# 4. mount -a 无报错,再重启 —— 这一步能挡掉 99% 的开机事故
# === 万一连 emergency mode 都进不去 ===
# 用安装盘 / LiveCD 引导进救援环境,chroot 到系统盘,
# 改 /etc/fstab 后重启。云服务器则用控制台的"救援模式"。
修复 6:磁盘挂载排查纪律
# === 这次事故暴露的认知盲区,定几条纪律 ===
# === 1. ★ mount 命令不持久,重启就没;持久要写 fstab ===
# 但 fstab 写错的代价,是整台机器开不了机。
# === 2. ★ fstab 里永远用 UUID,永远不用 /dev/sdX ===
$ blkid /dev/sdb1 # 查 UUID
# 设备名 sda/sdb 是临时编号,会变。
# === 3. ★ 数据盘那行一定加 nofail ===
UUID=... /data ext4 defaults,nofail 0 2
# 没 nofail,这块盘挂不上就能拖垮整个开机。
# === 4. ★ 改完 fstab 先 mount -a 验证,再重启 ===
$ mount -a
# 无报错才能重启 —— 这一条能挡掉绝大多数开机事故。
# === 5. 改 fstab 前先备份 ===
$ cp /etc/fstab /etc/fstab.bak
# === 6. 开机失败掉进 emergency mode 的自救 ===
$ mount -o remount,rw / # 先让根分区可写
$ vi /etc/fstab # 修错误行
$ mount -a # 验证
# === 7. umount 提示 busy,先找谁占着 ===
$ lsof /挂载点 / fuser -m /挂载点
# === 8. 排查挂载问题的命令链 ===
$ lsblk -f # ① 有哪些盘/分区、UUID、挂在哪
$ blkid # ② 查设备的 UUID 和文件系统类型
$ findmnt # ③ 当前挂载树
$ cat /etc/fstab # ④ 开机要挂的条目对不对
$ mount -a # ⑤ 验证 fstab 能否干净挂上
$ journalctl -xb | grep mount # ⑥ 开机挂载失败看这里
# 按这个顺序,磁盘挂载问题基本能定位。
命令速查
需求 命令
=============================================================
看有哪些盘/分区/挂载点 lsblk / lsblk -f
查设备 UUID 和文件系统类型 blkid /dev/sdb1
格式化分区 mkfs.ext4 /dev/sdb1
临时挂载 mount /dev/sdb1 /data
卸载 umount /data
看当前所有挂载(树状) findmnt
按 fstab 挂载所有条目 mount -a
emergency 里让根分区可写 mount -o remount,rw /
找出谁占着挂载点 lsof /data / fuser -m /data
看开机挂载失败日志 journalctl -xb | grep mount
口诀:fstab 用 UUID 不用 sdX -> 数据盘必加 nofail
改完 fstab 先 mount -a 验证 -> 无报错再重启
避坑清单
- mount 命令只是当下挂载,重启就失效,持久挂载必须写 /etc/fstab
- fstab 里要用 UUID 或 LABEL,绝不要用 /dev/sdX,设备名是临时编号会变
- 数据盘那行一定加 nofail,否则这块盘挂不上会拖垮整个开机进 emergency
- 改完 fstab 务必先 mount -a 验证无报错,再重启,别直接重启赌运气
- 改 fstab 前先 cp 备份一份,出事好回滚
- fstab 六字段:设备/挂载点/类型/选项/dump/fsck,fsck 根分区填1其他填2
- 挂载选项多个用逗号连不能带空格,nofail/noatime/_netdev 各有用途
- emergency mode 自救:mount -o remount,rw / 让根可写,再改 fstab
- umount 提示 target is busy,用 lsof 或 fuser 找出占用进程
- 挂载点目录原有内容会在挂载期间被遮盖,挂载前确认该目录是空的
总结
这次"挂个数据盘把整台服务器搞到开不了机"的事故,纠正了我一个关于"挂载"这件事的、相当致命的轻视。在这次之前,我把"给服务器挂一块盘"看成一件再简单不过的小事:mount 一下,盘就挂上了,能读能写;再往 /etc/fstab 里补一行,让它开机自动挂——在我心里,这后半步,不过是"把刚才那条 mount 命令,换个地方记下来"而已,是一件无足轻重的、抄写性质的收尾工作。正是这份轻视,让我在敲那行 fstab 时毫不上心:凭记忆写,用了 /dev/sdb1 这个"此刻看起来没错"的设备名,不加任何保险选项,看一眼"挺像样"就保存了。我完全没意识到,我刚刚埋下的,是一颗会在下一次重启时引爆的雷。复盘到根上,我才真正理解,mount 命令和 /etc/fstab,虽然都关于"挂载",却是性质完全不同的两件事——mount 管的是"现在":它把盘挂上,这个效果只活在当前这次开机的运行时里,机器一重启就烟消云散;而 fstab 管的是"以后":它是一份会被系统在【每一次开机】时都郑重读取、并逐行执行的"启动契约"。用一个只对"现在"有效的、临时的设备名 sdb1,去填写一份关乎"以后每次开机"的长期契约,这本身就是一个根本性的错配。因为磁盘的设备名——sda、sdb、sdc——根本不是磁盘的固有属性,它只是内核在每次开机时,按发现磁盘的先后顺序,临时派发的一个编号。这次它叫 sdb1,下次开机,只要识别顺序有一点变化,它就可能改叫 sdc1。我那行 fstab,等于把整台机器能否正常开机,赌在了一个注定会变的名字上。而比"用错设备名"更深、也更致命的一层认知盲区是:我从来不知道,fstab 里的一个条目,默认竟然是开机流程的一个【强依赖】。在我天真的想象里,一块数据盘挂载失败,顶多就是"那个目录暂时没法用"这么个局部的小麻烦。可 systemd 的逻辑完全不是这样:只要 fstab 里任何一个条目挂载失败,它就会判定"本地文件系统没有准备就绪",进而认为系统不具备正常启动的条件,于是干脆中止整个启动流程,把机器扔进 emergency mode。也就是说,我那块小小的、只用来存数据的盘,因为被我写进了 fstab 又没做任何防护,竟然被赋予了"一票否决整台服务器开机"的权力。想清楚这两层,正确的做法也就清晰得无可辩驳了。第一,fstab 里的设备,永远要用 UUID——那是文件系统在格式化时就被写入的、全局唯一且终身不变的"身份证",盘插在哪个口、设备名怎么变,UUID 都岿然不动。第二,也是这次的救命一课:数据盘那一行,必须加上 nofail 选项——它的含义是"这个盘万一开机挂不上,就跳过它,不要阻塞启动";加了它,即便挂载失败,系统也只是少了个 /data 目录,照样能正常开机,我登进去从容修复就是。一块数据盘的挂载成败,绝不该拥有否决整台机器开机的权力,而 nofail 就是收回这项过度权力的那把钥匙。第三,改完 fstab 之后,在重启之前,一定要先跑一遍 mount -a——它会按 fstab 把所有条目都试挂一遍,干净通过,才说明这份契约是对的、重启是安全的。这次从一个"挂个盘怎么会开不了机"的错愕出发,我最大的收获,是彻底改掉了对 /etc/fstab 的轻视——它不是一个随手抄写命令的备忘录,它是一份系统在每次开机时都会严格兑现、且默认拥有"否决开机"权力的契约。往这份契约里写东西,要像签一份正式合同一样:用稳定的标识(UUID),加上必要的免责条款(nofail),并且在生效前反复核验(mount -a)。一行随手写下的 fstab,和一台开不了机的服务器之间的距离,远比我曾经以为的,要近得多。
—— 别看了 · 2026