1. 当服务器突然卡死:从NMI watchdog错误说起
那天下午3点,机房警报突然响起。我冲到服务器前,屏幕上赫然显示着刺眼的红色错误:"NMI watchdog: BUG: soft lockup - CPU#2 stuck for 23s!"。这台承载着核心业务的服务器就像被冻住一样,所有SSH连接全部断开,监控图表上的CPU使用率曲线变成了一条直线。
这种场景对运维人员来说就像噩梦——系统没有完全死机,但核心功能已经瘫痪。soft lockup的本质是CPU被内核代码"绑架"了,就像交通要道被一辆抛锚的卡车完全堵死。与完全死机的hard lockup不同,soft lockup时系统还能勉强响应部分中断,但这种"半死不活"的状态往往更让人抓狂。
我立即连接了服务器的IPMI控制台,发现除了这个错误外,系统还在不断吐出网络丢包的警告。这给了我第一个线索:网络中断可能是触发条件。通过IPMI导出完整的内核日志后,我注意到错误发生前有大量virtio_net驱动的异常记录,这提示我需要重点检查虚拟化网络驱动。
2. 解剖soft lockup:内核的"心脏监护仪"
2.1 NMI watchdog的工作原理
Linux内核的NMI watchdog机制就像给CPU安装的心脏监护仪。它由两个关键部分组成:
- hrtimer高精度定时器:每4秒(默认值)触发一次中断,唤醒watchdog线程
- NMI不可屏蔽中断:当常规中断无法响应时,NMI是最后的救命稻草
用医院来类比的话,hrtimer就像是定期体检,而NMI则是当病人昏迷时使用的电击除颤。当watchdog线程超过20秒(2×watchdog_thresh)得不到执行,就会触发soft lockup报警。
# 查看当前watchdog配置 cat /proc/sys/kernel/watchdog_thresh2.2 从dmesg日志中找线索
关键的错误日志是这样的:
[Mon Dec 30 18:39:04 2019] NMI watchdog: BUG: soft lockup - CPU#1 stuck for 31s! [sshd:5071] [Mon Dec 30 18:39:04 2019] RIP: 0010:[<ffffffff91f904dc>] iowrite16+0x1c/0x40 [Mon Dec 30 18:39:04 2019] Call Trace: [Mon Dec 30 18:39:04 2019] [<ffffffffc03f1584>] start_xmit+0x264/0x500 [virtio_net]这里有几个关键信息:
- 卡死的CPU是1号核心
- 最后执行的函数是iowrite16
- 调用栈显示问题出在virtio_net驱动的数据发送流程
3. 深度诊断:像侦探一样分析锁死现场
3.1 绘制问题时间线
通过梳理日志,我重建了故障时间线:
| 时间 | 事件 |
|---|---|
| 18:38:33 | 交换机端口开始出现CRC错误 |
| 18:38:47 | 虚拟机网络吞吐量突然下降90% |
| 18:39:04 | 首次出现soft lockup警告 |
| 18:39:41 | RCU调度器检测到CPU stall |
这个时间线清楚地显示:网络中断先于CPU锁死,说明可能是网络问题触发了内核bug。
3.2 复现与压力测试
为了验证猜想,我搭建了测试环境:
# 模拟网络丢包 tc qdisc add dev eth0 root netem loss 30% # 启动网络压力测试 iperf -c 192.168.1.100 -t 600 -P 8经过3小时测试,成功复现了soft lockup。有趣的是,问题只在使用virtio_net驱动时出现,切换到vmxnet3驱动后一切正常。
4. 解决方案:从临时缓解到彻底修复
4.1 紧急处置方案
当生产环境出现锁死时,可以临时调整这些参数:
# 延长watchdog检测窗口 echo 30 > /proc/sys/kernel/watchdog_thresh # 允许自动panic重启 echo 1 > /proc/sys/kernel/softlockup_panic但要注意,这只是"止痛药",不是根治方案。我曾见过一个案例,调整阈值后系统虽然不报错了,但性能下降了40%。
4.2 根本解决之道
根据我们的分析,最终采取了以下措施:
- 升级virtio-net驱动:从内核4.9升级到4.19
- 调整网络队列:减少virtio-net的发送队列长度
ethtool -G eth0 tx 256 rx 256 - 优化交换机配置:调整端口流控阈值
实施后连续监控两周,再未出现锁死情况。这个案例让我深刻体会到:soft lockup从来不是根本问题,而是更深层次bug的症状。
5. 经验总结:预防优于抢救
经过这次事件,我们建立了新的监控策略:
- 提前预警机制:
# 监控watchdog事件 grep -c "soft lockup" /var/log/kern.log - 压力测试规范:所有新服务器上线前必须通过72小时混合负载测试
- 内核参数调优清单:针对不同工作负载预设优化配置
最让我意外的是,事后查阅内核邮件列表发现,我们遇到的居然是已知bug——virtio-net驱动在特定网络拥塞场景下会出现自旋锁问题。这提醒我们:定期更新内核不仅能获得新特性,更是稳定性保障。
在技术这条路上,每个故障都是最好的老师。那次深夜抢修后,我养成了每周浏览内核更新日志的习惯。毕竟,预防故障远比解决故障更有价值——对系统如此,对人生亦是如此。