磁盘只读(read-only filesystem)是线上最容易被误判为“服务挂了”的故障:进程还在、端口还通,但任何写操作都报 Read-only file system。日志写不进、上传失败、数据库卡死,表现千奇百怪。本文从如何确认只读、到用 dmesg 与 fsck 定位根因,再到安全修复与长效防护,给出一套可直接照做的排查流程,帮你把“假死”快速救活。
一、先确认是不是真的只读
不要急着重启。第一步是把“应用层报错”和“文件系统真的只读”区分开。最可靠的办法是看挂载选项,再实际写一个文件验证。
# 查看根分区挂载选项,留意有没有 ro
mount | grep ' on / '
# 实际写测试
touch /tmp/ro_test 2>/tmp/ro_err && echo "可写" || (echo "只读"; cat /tmp/ro_err)
如果 mount 输出里根或某分区带 ro 选项,或者 touch 明确报 Read-only file system,就确认是文件系统只读。下面这张表帮你把现象和结论对上号。
| 现象 | 说明 |
|---|---|
| mount 含 ro 选项 | 文件系统被以只读方式挂载或自动降级 |
| touch 报 Read-only file system | 确认该挂载点不可写 |
| 应用写日志失败但读正常 | 典型只读表现,进程未崩溃 |
| df 显示容量正常 | 与“磁盘满”不同,要先排除空间问题 |
二、看内核日志:为什么变成只读
Linux 文件系统(EXT4/XFS 等)在检测到底层 I/O 错误时,会主动进入只读保护模式,目的是“宁可停写也不继续损坏数据”。所以只读几乎总是“果”,真正的“因”在日志里。配合 Web 服务器日志分析实战 的思路,先抓内核环形缓冲:
# 带时间戳查看内核日志,过滤关键错误
dmesg -T | grep -iE 'read-only|error|EXT4|XFS|I/O error|sda|nvme'
# 或查系统日志
journalctl -k -p err -b | tail -50
常见根因:云盘瞬时抖动、磁盘坏块、RAID 降级、虚拟机热迁移、内存故障触发内核 BUG。看到 EXT4-fs error 或 I/O error 基本就能锁定是底层存储问题,而不是应用 bug。
三、临时恢复:重新挂载为读写
如果业务需要立刻恢复,可尝试在线重新挂载为读写:
# 谨慎:仅用于紧急恢复
mount -o remount,rw /
# 指定分区
mount -o remount,rw /data
注意:若底层确实有未修复的错误,强制 rw 可能扩大数据损坏范围。这一步只能当作“先恢复服务”,之后必须做 fsck 彻底修复。如果 remount 仍然失败,说明错误严重,需要进入单用户或救援模式处理。
四、根因分类与对症处理
1. 文件系统错误:用 fsck 修复
文件系统元数据损坏是最常见原因。修复前必须先卸载(或在救援模式),绝对不能对挂载中的分区跑 fsck。
# EXT4:卸载后检查并自动修复
umount /dev/sda1
fsck -y /dev/sda1
# XFS:用 xfs_repair(不能在线,且先尝试 xfs_repair -n 预览)
xfs_repair -n /dev/sda1
xfs_repair /dev/sda1
2. 底层存储 / 云盘故障
云服务器优先去控制台看云盘状态、IOPS/吞吐是否异常,必要时换盘或工单。物理机用 SMART 看磁盘健康:
smartctl -a /dev/sda | grep -iE 'Reallocated|Pending|Uncorrectable|Overall'
3. 内核 / 内存触发的保护性只读
若 dmesg 出现 hung task、BUG 或大量内存纠错,可能是内核或硬件问题。升级内核、排查内存条、必要时迁移实例。
4. 容器场景里的“只读”
容器自身可能是只读层导致,不一定是宿主机故障。例如 Docker Compose 一键编排 里给容器加了 --read-only,或 overlay 所在宿主机磁盘只读会传导到容器。K8s 里 securityContext.readOnlyRootFilesystem: true 也会让写入失败——这类要在编排层面处理,而不是去修宿主机。
五、fsck 实战步骤
不同文件系统的检查命令不同,下面一张表对照清楚:
| 文件系统 | 检查/修复命令 | 能否在线 |
|---|---|---|
| EXT2/3/4 | fsck -y /dev/sdXN | 否(需卸载) |
| XFS | xfs_repair /dev/sdXN | 否(先 xfs_repair -n 预览) |
| Btrfs | btrfs check /dev/sdXN | 否(建议离线) |
# 标准流程
# 1) 备份重要数据 / 打快照
# 2) 卸载目标分区(根分区需进救援模式)
umount /dev/sda1
# 3) 自动修复(交互确认选 y)
fsck -y /dev/sda1
# 4) 重新挂载验证
mount /dev/sda1 /data && touch /data/ro_test && echo "恢复可写"
小提示:LVM 或云盘环境下设备名可能是 /dev/vdb1、/dev/nvme0n1p1,用 lsblk 和 blkid 确认,别修错盘。类似“假死”现象也可以参考 文件描述符耗尽排查,两者表现相似但根因完全不同。
六、容器与云原生里的只读根
把容器根文件系统设为只读,是现代“不可变基础设施”的推荐做法,能减少攻击面、避免容器把日志/临时文件写爆宿主机。正确姿势是把需要写的地方用卷挂出来:
# Docker:根只读,临时目录与日志挂 volume
docker run --read-only -v app-tmp:/tmp -v app-log:/var/log myapp
# Kubernetes:只读根 + emptyDir 给可写目录
securityContext:
readOnlyRootFilesystem: true
volumes:
- name: tmp
emptyDir: {}
七、如何避免再次只读
修复只是止血,长效防护才是关键:
- 监控先行:对
dmesg的 I/O error、磁盘 SMART、云盘健康做告警,别等业务报障才发现。 - 配置稳健:
/etc/fstab用 UUID 挂载,了解errors=remount-ro的默认行为。 - 架构隔离:日志、数据、临时文件分盘,避免根分区被写满或抖动传导。
- 定期备份:云盘快照 + 应用级备份,修复失败时也能回滚。
性能与稳定性基线可参考 Linux 性能排查实战 与 Linux 内核参数调优,把磁盘、内存、网络监控一起做扎实。
八、落地 Checklist
| 步骤 | 动作 | 命令/要点 |
|---|---|---|
| 1 确认 | 看挂载选项 + 写测试 | mount | grep ro;touch 测试 |
| 2 取证 | 抓内核日志 | dmesg -T | grep -iE ‘error|EXT4|XFS’ |
| 3 判断 | 区分文件错误/存储/容器 | 看错误类型与场景 |
| 4 应急 | 谨慎 remount rw | mount -o remount,rw / |
| 5 修复 | 离线 fsck | umount 后 fsck -y |
| 6 防护 | 监控+备份+隔离 | 告警、快照、分盘 |
九、根分区只读、进不了系统怎么办
当根分区(/)本身只读且无法 remount,机器往往已经卡死或起不来。这种情况不能在线 fsck,必须离线修复:云服务器用厂商的“救援模式”把系统盘作为数据盘挂到另一台实例,再用 fsck 修复;物理机或本地虚拟机则从 Live CD / ISO 启动进入救援环境。核心原则不变——fsck 永远只修未挂载的分区。修完把系统盘挂回原实例重启即可。这类操作建议沉淀进应急预案,关键时刻能省下大量救火时间。
# 救援模式下:把系统盘挂载为数据盘后修复
mkdir -p /mnt/rescue
mount /dev/sdb1 /mnt/rescue
fsck -y /dev/sdb1
umount /mnt/rescue
磁盘只读并不可怕,可怕的是盲目重启或强制写导致二次损坏。记住这条主线:先确认、再取证、后修复、重防护,绝大多数只读故障都能在不丢数据的前提下恢复。




