4. 存储排查:磁盘 I/O 异常、文件系统损坏时的数据采集方法
镜像加载失败,很多时候不是镜像本身的问题,而是底层存储扛不住了。我遇到过好几次,业务方急得跳脚,说镜像拉不下来,结果我一查,磁盘 I/O 已经飙到 100%,或者文件系统报只读错误。这时候你慌也没用,得按套路把数据先捞出来。
说白了,存储层面的问题分两类:性能问题(I/O 打满)和结构问题(文件系统坏了)。采集方法完全不同,咱们分开讲。
4.1 磁盘 I/O 异常:先看指标,再抓现场
磁盘 I/O 异常,最直观的表现就是 docker pull 卡住不动,或者进度条走几秒就停了。你敲 docker pull 没反应,第一反应不是重启,而是看系统指标。
4.1.1 核心指标采集清单
我个人习惯,先跑三个命令,把 I/O 的底裤扒干净:
| 命令 | 采集指标 | 异常阈值(参考) |
|---|---|---|
iostat -x 1 5 |
%util、await、svctm、r/s、w/s | %util > 90%,await > 30ms |
sar -d 1 5 |
tps、rd_sec/s、wr_sec/s | tps 突降或突升 5 倍以上 |
pidstat -d 1 5 |
进程级读写速率 | 单个进程 I/O 占总量 80% 以上 |
iostat -x 1 5 看整体,如果 %util 超过 90%,再用 pidstat -d 揪出是哪个进程在搞事。别一上来就查进程,浪费时间。
4.1.2 抓取 I/O 高负载时的堆栈
光看指标不够,你得知道 I/O 到底卡在哪个环节。我常用的三板斧:
- 看 D 状态进程:
ps aux | grep " D",如果大量进程处于 uninterruptible sleep,说明磁盘响应已经崩了。 - 抓内核栈:
cat /proc/<PID>/stack,能看到进程卡在哪个内核函数上。比如卡在blk_queue_bio说明是块层排队,卡在wait_on_page_bit说明是页缓存回写。 - 记录 I/O 等待时间:
echo 1 > /proc/sys/vm/block_dump,然后看内核日志,能抓到每个进程的 I/O 操作详情。不过这个命令生产环境慎用,会刷爆日志。
4.2 文件系统损坏:从报错到取证
文件系统损坏比 I/O 异常更棘手。镜像加载失败时,你可能会看到这样的错误:
failed to register layer: error while creating filesystem:
input/output error
或者
unexpected filesystem error: structure needs cleaning
看到这类错误,别急着修,先采集证据。
4.2.1 文件系统状态快照
我一般按这个顺序采集:
- 挂载状态:
mount -l看有没有分区被 remount 成 ro(只读)。如果发现ro标志,说明内核已经检测到损坏并自动保护了。 - 超级块信息:
dumpe2fs -h /dev/sdX1(ext4)或xfs_info /dev/sdX1(XFS),记录文件系统的原始参数。 - 日志区域:
dmesg | grep -i "ext4\|xfs\|btrfs\|i/o error",把内核报错全部捞出来。 - 坏块信息:
badblocks -v /dev/sdX1,不过这个命令很慢,我一般只跑前 1% 的区域做抽样。
4.2.2 只读挂载下的数据抢救
如果文件系统已经变成只读,千万别强制 remount 成读写。我见过有人直接 mount -o remount,rw,结果把最后一点数据也搞坏了。
正确的做法是:
- 用
dd做全盘镜像:dd if=/dev/sdX1 of=/backup/sdX1.img bs=4M conv=noerror,sync。加上noerror参数,遇到坏块会跳过,不会卡死。 - 在镜像文件上做只读分析:
mount -o loop,ro /backup/sdX1.img /mnt/analyze。 - 从只读挂载点里把 Docker 数据目录拷出来:
cp -a /mnt/analyze/var/lib/docker /backup/docker_data。
conv=noerror,sync。noerror 跳过坏块,sync 用 null 填充坏块位置,保证镜像文件大小和原盘一致。这样后续分析时,文件系统的偏移量不会乱。
4.3 实战:从 I/O 异常到根因定位
讲个真实案例。有一次线上容器平台大面积镜像拉取失败,我上去一看,iostat -x 显示 sda 的 %util 一直在 95% 以上,await 高达 200ms。但奇怪的是,读写速率并不高,r/s 才 200 左右。
这说明什么?说明磁盘本身响应慢,而不是负载高。我查了 /proc/diskstats,发现 IOPS 很低但等待时间很长,典型的磁盘硬件故障特征。
进一步用 smartctl -a /dev/sda 采集 SMART 信息,发现 Reallocated_Sector_Ct 已经飙到 500 多,Current_Pending_Sector 也有 20 多个。实锤了——磁盘坏道导致 I/O 超时重试,镜像层文件读写卡死。
整个数据采集链路是这样的:
docker pull 卡死
→ iostat 发现 %util 高、await 高
→ pidstat 定位到 dockerd 进程 I/O 异常
→ /proc/diskstats 确认磁盘响应慢
→ smartctl 采集磁盘健康数据
→ 结论:磁盘坏道,需要换盘
4.4 知识图谱:存储排查全景
下面这张图,是我自己总结的存储排查数据采集流程。每次遇到存储问题,我就按这个框架走,基本不会漏东西。
4.5 避坑指南:我踩过的几个坑
- 别在 I/O 高负载时跑 fsck:我曾经在磁盘 I/O 打满的时候跑
fsck -y,结果把文件系统搞得更乱了。正确的做法是先采集数据,等 I/O 降下来再修复。 - SMART 数据要采两次:有些磁盘故障是间歇性的,一次 SMART 采集可能显示正常。我习惯隔 5 分钟再采一次,对比
Raw_Read_Error_Rate和Reallocated_Sector_Ct的变化。 - dd 镜像别直接放故障盘:这个听起来像废话,但我真见过有人把镜像文件写到同一个故障磁盘上。结果读到坏块时,镜像文件也写坏了。
- 内核日志要带时间戳:
dmesg -T可以把时间戳转成可读格式,方便和 I/O 指标做时间轴对齐。不然你根本不知道报错是几分钟前还是几小时前发生的。
tar czf storage_evidence_$(date +%Y%m%d_%H%M%S).tar.gz 把 iostat 输出、dmesg 日志、SMART 信息、dd 镜像元数据打包。这样后续复盘或者给存储厂商提工单时,证据链是完整的。
嗯,存储排查这块,说白了就是「先保数据,再找原因」。只要把现场数据完整地捞出来,后面不管是换盘、修文件系统还是找厂商索赔,你手里都有硬证据。别急着修,先采集,这是运维的老规矩。