我在 dmesg 里看到这行,是凌晨一点半。

EXT4-fs (sda3): Remounting filesystem read-only

往前翻几行,还有一句更要命的:

sd 0:0:0:0: [sda] tag#9 Sense Key : Medium Error [current]
sd 0:0:0:0: [sda] tag#9 Add. Sense: Unrecovered read error - auto reallocate failed

Medium Error,Unrecovered read error。看到这我就明白了,不是系统抽风,是盘上有个扇区读不出来了。

倒回去说。一点二十,客户负责人打电话,说网站订单提交不了,一直在报错。我一边开电脑一边先扫了眼监控:负载正常,CPU 内存都在低位,磁盘空间还剩一半多,43% 的样子,怎么看都没毛病。

他发了张截图过来:

Failed to open stream: Read-only file system

我第一反应是应用把哪个目录的权限写坏了,或者谁误操作。SSH 上去随手 touch 一个文件验证——touch: cannot touch '/tmp/x': Read-only file system。连 /tmp 都写不进去。

mount | grep ' / ' 一行:

/dev/sda3 on / type ext4 (ro,relatime,data=ordered)

根分区是 ro 了。

到这一步我还在往"人"身上想。先 mount -o remount,rw / 想改回去,内核不给面子:

mount: /: cannot remount /dev/sda3 read-write, is write-protected.

write-protected,这词不常见。我翻了 /etc/fstab,没人动过;查有没有配 quota,没有;又怀疑是不是客户自己装了什么安全软件做了文件保护。折腾了二十来分钟,全是死路。

然后才想起来看 dmesg。就是开头那几行,时间戳 01:17:03,比我接到电话早了三分钟。内核在 01:17 读到坏扇区,I/O 报错,把 journal 标成 aborted,接着把整个文件系统切成只读。这是 ext4 的自我保护——宁可让所有写操作失败,也不把新数据再往一块正在坏的盘上写。

盘是 sda,一台托管在我们机房的 Dell R720,CentOS 7.9,内核 3.10。单盘,没做 RAID。

smartctl -a /dev/sda:

197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - 8
198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 8
196 Reallocated_Event_Count 0x0032 099 099 000 Old_age Always - 147

Reallocated 已经 147,另外还挂着 8 个 pending。有意思的是 smartctl -H 整体还是 PASSED。这台机器的 SMART 自检一直绿的,所以我们一直没太当回事。现在回头看,147 这个数早就该让我们抬一下头。

客户运维小哥在微信上问,要不要他先重启一下。我说千万别,先别碰机器,也别关机。坏盘上最怕乱动,你这一关,也许能起来,也许就再也起不来了。

这里有个判断。盘已经这样了,最忌讳的就是直接上去 fsck -y。一是坏道还在,fsck 大量读写只会继续折腾它;二是自动修复碰到读不出来的地方,很可能把一个本来还能抢救的目录直接标成空。我见过有人图快,fsck 一把梭,本来数据还在,修完真没了。

该做的是先把盘整个镜像出来。只读挂载不影响块设备层面的读取,用 ddrescue 从 /dev/sda 按块往外拷,碰到坏块跳过去,先把能读的全保住:

ddrescue -f -n /dev/sda /mnt/backup/sda.img /mnt/backup/sda.map

第一遍带 -n,快速抓一遍,跳过坏块;再跑第二遍,让它专门回去啃那些读不顺的地方。

盘是 2T 的,第一遍跑了差不多四个半小时。中间客户又来了两个电话,我能说的只有"还在读,没到算账的时候"。人家倒也没催到失态,就一句"你弄,我们这边有备份顶着"。我一听,心里其实更凉——他们那个所谓备份,是每天凌晨 rsync 到另一台机器,而那台机器,也不年轻了。

镜像到手,才有资格动手。用 losetup -P 把镜像挂成 loop 设备,再对里面的 sda3 分区跑 e2fsck 修 superblock 和 bitmap:

losetup -Pf /mnt/backup/sda.img
e2fsck -fy /dev/loop0p3

修完挂起来看,数据基本都在。丢了一小块:报错扇区落到的那个文件。后来确认是 /var/log 底下的一个日志,加上两个当时正在写的临时文件,拢共 1.2M 左右,业务数据没伤着。

那天收尾是上午十一点多。把数据倒到新盘、换了块盘、重装系统。那台 R720 我们劝过客户换,一直拖着,这下不用劝了。

后面我干了几件事。监控里把 SMART 的 Current_Pending_Sector 和 Reallocated_Sector_Ct 单独拎出来做告警,不再看那个糊弄人的整体 PASSED;手上单盘跑的机器列了个清单,一台台推客户加盘或者上 RAID;ddrescue 也写进了事故手册——以前真到这一步,好几个人第一反应还是 fsck。

其实三个月前那台机器的 SMART 就报过一个 pending,我们看了一眼,觉得个位数不算事,放过去了。那 8 个 pending 里随便哪一个,都够让一整个晚上泡汤。数据最后是保住了,但那四个半小时里我心里其实没底,谁也不能保证 ddrescue 一定绕得过每一个坏块。说到底,运气也占了挺大一部分。