被迫当了一天半“自宅服务器运维员”,差点数据火葬场,在这里给大家避个坑吧
我实体服务器放家里,系统是原生 Debian 11
存储环境是 4*2T HDD 组的软件 RAID5,容量大概 5T 左右,文件系统是 btrfs,这里/dev/md0p1
就是 RAID 分区,挂载在/storage
上。系统盘也就是/
目录和/boot
目录存放在 128G SSD 上
RAID 区上主要存储各种文件,比如电影照片游戏等、以及自己的各种工程项目,当然服务器上同时还跑着 Docker 和 MySQL,它们的存储路径也放在/storage
下,也就是存在 RAID 区里
问题的导火索
昨天下午收拾东西的时候撞了下网线,没过一会就收到了 uptimebot 的告警邮件,原来是服务器断网了(这里监控的是使用 frp 穿透的 https 服务),赶紧用手机连上 ssh 检查,但内网没问题外网却连不通,寻思着服务器也连续运行了 90 多天该重启了,无脑sudo reboot
了一下,这下好,直接闯祸了(事后才知道是重启后 grub 默认加载了6.0.0-0.deb11.2-rt-amd64
kernel
发现问题
我 qq 机器人的服务端和数据库是分离的,服务端基于 Aridane+Mirai 搭建,数据库用 MySQL,由于这玩意业务逻辑比较复杂,自己优化做的也很差,所以放在云服务器上跑,因为自己撺出来这实体机性能也明白,但数据库服务还是使用这台实体机,之间使用 VPN 组网进行连接(因为用家宽没有公网 IP
在重启服务器后,突然发现 qq 消息不停刷,原来是机器人给我连续抛出几十个异常上报,内容都是关于 MySQL 连接失败的
这下我就纳闷了,于是又火速连上 ssh 检查 VPN 的状态,设备之间能互相 ping 通(云服务器是10.50.9.1
家里这台服务器是10.50.9.10
),那么接着按照惯例就该检查 MySQL 状态了。我已经设置为开机自启,但sudo systemctl status mysql
查看,发现服务没有启起来,如果稍微懂那么一丢丢 Linux,那么你第一反应就是赶紧 start 它。但事实上,start 后它就寄了!
一般 start MySQL 服务也就几秒完毕,但这次我等了半分钟也没启动完毕,紧接着我在 ssh 终端上看到一条 broadcast 消息,作为浅尝过 Linux 的小白,肯定知道终端出现 broadcast 消息一定大事不妙。很好,CPU 直接 stuck 了,按 Ctrl-C 虽然能回到 shell 环境,但 htop 查看 CPU 其中一个核心占用 100% 却看不到任何占用它的进程,当然 SSH HTTP SMB FTP VPN VNC FRP 这些服务无一例外没活着的,端口正常开放,连上后没响应
尝试解决问题
我的天,正常关机也不能实现了,因为这个时候 systemd 也卡住了,走不了流程,只好强行拔电,插电开机继续找问题
我当时一度认为 MySQL 的 binlog 过大把进程卡死了,因为这个服务器上的数据库不仅给 qq 机器人用,还给爬虫、网盘服务用,数据的吞吐量很大。就尝试删除 MySQL 的 binlog,没想到一删直接踩雷,ssh 连接给我断了、硬盘刚才还轰隆轰隆,顿时变得安静起来了、硬盘灯全部熄灭、当然 ping 不通了。它,这次没有报 stuck,而是直接宕机了!
太怪了,为啥删几个文件就轻松死机,有点不对劲(内心 OS:快进到数据火葬场)赶紧让老子先备份一下数据库,整个 MySQL 存储文件夹端走得了,已经用systemctl disable mysql
关闭 MySQL 开机自启了,这样至少不会开机既死机,可以苟一会
尝试把 Mysql 文件转移到 SSD 去,但涛声依旧,没拷过去几个文件,咔的一下系统又宕了
rsync -ravh /storage/DataBase/mysql /Mysql
越来越离谱了,难道是只要访问/storage/DataBase/mysql
这个路径就会触发宕机?于是我排除一下,扫描/storage/File
下的文件(这个路径下主要存项目、学习资料、游戏,大概有1T左右),因为du
会遍历整个目录树,所以正好能用来检查读取文件的稳定性
du -hd1
没想到啊,仍涛声依旧,不碰 MySQL 存储目录还是会宕机,证明不是 MySQL 的问题。如果,读写某些特定路径会死机,那么照常理讲,最大的可能应该是文件系统损坏。如果真是 btrfs 损坏那基本上没人能恢复得了,再加之我是在 RAID 之上建立的分区,如果损坏那么就像火化后再浇一盆冷水(整个人慌了起来,万一数据火葬场就哭得没眼泪了
一波三折
首先我怀疑是 RAID 的问题,就手动触发一下 RAID 的一致性检查机制吧,因为该服务器使用 RAID5,有一定的容错和自我修复能力
echo check > /sys/block/md0/md/sync_action
这一等就是一下午.......与此同时,在检查进行的时候顺便测试测试,用 SMB 传出去 40GB 的文件,全程跑到 100M/s 既没掉速也没有宕机,这更让我相信是文件系统损坏导致的
涛声依旧.......整个文件系统如同雷池般,读写某些文件可以正常进行,某些文件碰都不敢碰,无论读写都会触发宕机,有时候是 cpu stuck,有时候 ssh 直接断开 ping 不通,而这些文件可没啥明显的异常啊喂
要是问图形界面怎么样,哈哈哈哈哈只能说太聪明了,宕机期间 VNC 是连进不去的,显示器上只有鼠标是可以运动的,剩下一切都定格了
想用 SMB 试图提前抢救出重要文件,可传输还没开始就卡住了,进度条那里一直提示“正在检查文件数量”,果然故障扩散了,我还是太“聪明”了(⑨
最后急到跳脚,甚至还跑了一遍 btrfs 的检测
# 检查bttrfs文件系统错误
sudo btrfs check --repair /dev/md0p1
# 检查bttrfs文件系统校验和
sudo btrfs scrub start /dev/md0p1
结果是没有错误! 我突然有种科学家遇到“智子”的感觉,还得是大史那句话“邪乎到家必有鬼”
这时已是深夜,我准备再搏最后一把,把/storage
卸载掉,然后在系统里随便操作操作,测试稳定性。很好,sudo apt update
更新一下软件源索引轻松宕机,但愿这只是从侧面反映出存储系统没损坏,而不是故障继续扩散了,两害取其轻,怀着疑惑睡觉了
急急国王
变换思路
第二天早上起来,二话不说就把服务器扒了,首先怀疑是 CPU 供电不稳导致的宕机,毕竟软件 RAID 寻址需要普通盘数倍的 CPU 算力,用 BIOS 自带的监控测各路电压、也用万用表测电源输出电压,甚至还捞起来一个老电脑的电源代换它
当然,结果心里已经有数了,都是无用功,轻松死机
在这一次又一次的反复调试中,其中一次开机进入 recovery mode 检查时,发现读写文件触发 cpu stuck 后就会有这样的报错信息刷屏,可以看到底下有一个完整的线程 Callstack Trace,问题百分百指向 btrfs
我就寻思 btrfs 既然是是内核态驱动,那么会不会是内核不兼容导致的,可以尝试降级一个版本,用uname -r
查看,发现内核竟居然是6.0.0-0.deb11.2-rt-amd64
,赶紧重启后在 grub 里选Adavanced options for Debian GNU/Linux
选项,再选择5.19.0-0.deb11.2-amd64
引导启动
问题解决
在换内核启动后,宕机问题成功解决,赶忙用 rsync 把 MySQL 目录也全部拷出,挂上一块移动硬盘到服务器上备份重要数据
有惊无险,后面测试一波发现各项服务和数据其实并无大碍
当然一开始 MySQL 莫名其妙停止运行的问题也迎刃而解
救活了!整个人心都放下了
我寻思造成这种故障的祸根一定是 apt 吧(笑
最后当然是迁移了 MySQL 目录和 Docker 目录,放到 SSD 中(ext4 格式
接着配置默认启动项,彻底铲除由于升级造成的故障
sudo vi /etc/default/grub
sudo update-grub
设置GRUB_DEFAULT
为Advanced options for Debian GNU/Linux>Debian GNU/Linux, with Linux 5.19.0-0.deb11.2-amd64
既默认启动5.19.0-0.deb11.2-amd64
内核
事故分析
在服务器正常运行的 90 天内,装软件时 apt 或许已经把 6.0.0 内核当成依赖安装了,但不重启就不会加载新版内核。而搬东西把网线撞掉再插上,有可能造成 FRP 服务断连或者 ARP 表缓存异常,导致外网无法连接、以及收到掉线告警邮件。重启后 grub 将系统里存在的最高版本内核,也就是6.0.0-0.deb11.2-rt-amd64
作为默认启动项引导,启动后由于内核的 bug 导致 btrfs 驱动程序工作不稳定,进而造成 MySQL 服务无法启动,读文件的时候同样会触发这个 bug,导致宕机或者 cpu stuck
经验教训
- 生产环境服务器不要随便升级内核
- MySQL、Docker 等 IO 密集型服务尽量别用 RAID 存储,尤其是软 RAID,最好使用 SSD 缓存盘,这些服务中 RAID 只做定期热备份