裸金属BMC失联36小时:一根IPMI网线引发的“静默”事故与自动化治理实录
前奏:双十一大促后的寂静杀手
去年11月,我们一批轻云互联的裸金属物理机刚刚经历完一轮大促流量洪峰,正当我以为可以喘口气时,监控平台的一条告警像针一样扎进眼球:node-23(杭州C区)BMC ping不通。第一反应是机房的带外交换机又抽风了,但登上跳板机敲下 ping 10.10.9.23 时,延迟正常,TTL正常。BMC IP活着,但管理面死活连不上。
此时,业务网卡、内网联通性全部正常,CPU负载低于1.0。这台机器就像一头不吭声的老黄牛,表面温顺,内核可能已经在暴雷。直觉告诉我,这不是网络问题,是带外管理平面的协议栈出了问题。
第一线排查:IPMI协议的“薛定谔状态”
我直接祭出 ipmitool 进行带外探测,命令如下:
# 从跳板机执行
ipmitool -I lanplus -H 10.10.9.23 -U admin -P '****' mc info
输出卡死,直到超时。再次换用 RMCP 纯UDP协议探测:
# 抓包检查 RMCP 端口(623)
tcpdump -i eth0 -nn -vvv port 623 or port 5900 or port 443
诡异的一幕出现了:RMCP的Discover请求有去无回,但SOL(Serial-over-LAN)的SSH端口(2200)却能建立TCP握手。这说明 BMC的Web管理进程或IPMI会话管理进程已经僵死,但网络协议栈还在低位运行。这种故障最恶心,你无法远程重启BMC,因为IPMI本来就是用来干这事的,现在等于钥匙锁在保险箱里。
暗雷探查:进得去“后门”,却拧不开“门锁”
既然 SOL 端口活着,我用 ipmitool sol activate 尝试挂载串口控制台:
ipmitool -I lanplus -H 10.10.9.23 -U admin -P '****' sol activate
结果进入了BMC的Linux子系统Shell(很多厂商BMC底层是OpenBMC或定制Linux)。这真是绝佳的观测窗口。执行 top 查看进程,发现 phosphor-host-ipmid 这个守护进程的CPU占用率高达98%,且处于 D 状态(不可中断睡眠)。
这是一次典型的 内核IO锁死。罪魁祸首找到了:BMC的IPMID进程在尝试读取某些硬件传感器(可能是电源功耗或风扇转速)时,被内核的I2C总线驱动卡死,进了死锁。
根治手术:给BMC的“心脏”做支架
通过SOL进入的Linux Shell权限极高(root)。我通过挂载BMC的根文件系统查看日志,核心报错如下:
# 在BMC shell内操作
cat /var/log/messages | grep -i "i2c" | tail -20
[ 1687.259319] i2c_i801 SMBus controller down
[ 1687.259458] platform ebr-sensors-bb: Failed to read from sensor (addr 0x4d)
[ 1687.259512] kernel: INFO: task kipmi0 blocked for more than 120 seconds.
这里又牵扯出一个极容易被忽略的物理机特性:裸金属的BMC监控芯片与CPU的SMBus直连。如果主板上有某些PCIe设备(比如NVIDIA A100 GPU或NVMe SSD转接卡)占用了SMBus地址冲突,会导致BMC在某一瞬时状态切换时发生I2C死锁。
幸运的是,这个BMC的Linux系统支持强制加载内核模块来复位总线。我执行了:
# 在BMC Shell,强制重置SMBus控制器
rmmod i2c_i801
modprobe i2c_i801
# 重启IPMI守护进程
systemctl restart phosphor-host-ipmid
# 验证传感器读取
sensors
瞬间,IPMI响应恢复正常。远程 ipmitool mc info 秒回。
自动化加固:故障必须在发生前被“掐死”
这次事故虽未造成业务停机,但暴露了一个深层隐患:物理机的带外管理设备是独立于宿主机的“定时炸弹”,它同样会死机、会卡死,且比宿主机更隐蔽。
我们不可能每天手动去SOL激活排查。于是我写了一套自动化探针,部署在所有物理机的宿主机侧,用于监控BMC的健康状态并自动处理这种“哑火”故障。
探针脚本:不只是ping,而是“预检心跳”
#!/bin/bash
# /usr/local/bin/bmc_health_check.sh
# 用于检测BMC的IPMI进程是否僵死,并自动通过SOL通道进行复位
BMC_IP="10.10.9.23"
BMC_USER="admin"
BMC_PASS='your_password'
SOL_PORT="2200"
# 1. 检测IPMI标准端口是否响应
if ! ipmitool -I lanplus -H $BMC_IP -U $BMC_USER -P $BMC_PASS mc info &>/dev/null; then
# 2. 检测SOL端口(SSH)是否还活着
if nc -z -w5 $BMC_IP $SOL_PORT &>/dev/null; then
# 3. 通过SOL执行“软复位”
# 此处使用 expect 脚本自动化,避免手动交互
expect <<EOF
set timeout 10
spawn ssh -p $SOL_PORT root@$BMC_IP
expect "password:"
send "$BMC_PASS\r"
expect "# "
send "rmmod i2c_i801 && modprobe i2c_i801 && systemctl restart phosphor-host-ipmid\r"
expect "# "
send "exit\r"
EOF
sleep 5
# 4. 再次检测IPMI是否恢复
if ipmitool -I lanplus -H $BMC_IP -U $BMC_USER -P $BMC_PASS mc info &>/dev/null; then
echo "$(date) [RECOVERED] BMC IPMI stuck, SOL recovery successful." >> /var/log/bmc_auto_recovery.log
else
echo "$(date) [CRITICAL] BMC SOL recovery failed, manual intervention needed." >> /var/log/bmc_auto_recovery.log
fi
else
echo "$(date) [CRITICAL] BMC and SOL both dead, network-level issue." >> /var/log/bmc_auto_recovery.log
fi
fi
物理机专属的“D状态”检测逻辑
上述脚本只能解决IPMI进程僵死。但我们要连BMC的Linux内核死锁也要预防。更聪明的做法是直接监控BMC的内存和进程状态,但很多BMC并不开放SSH到宿主机。退而求其次,我在宿主机上监控 /sys/class/i2c-dev/ 的busy状态,如果SMBus被锁,对CPU上某些设备的 sensors 命令读取也会阻塞。
这里要特别强调一个冷门参数:让BMC的传感器轮询频率降低,能大幅降低I2C总线冲突概率。通过IPMI设置传感器事件触发阈值:
# 将指定传感器的扫描周期从默认的0.5Hz降为0.2Hz
ipmitool sensor set "GPU_TEMP" 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.25
# 若主板支持,则关闭对无意义的PCIe电源插槽的监控
ipmitool raw 0x30 0x49 0x00 0x01
这两个命令因地制宜,因主板型号而异,但思路是:减少BMC的I2C读操作频度,避免高频轮询撞上PCIe设备的高峰数据传输,造成锁冲突。
运维视角的降维打击:把BMC视作“第一公民”
大多数人在云服务器上没有这种困扰,因为虚拟化层屏蔽了这些硬件泥沼。但裸金属物理机的魅力和痛点都在这里,你需要同时伺候好两个CPU:一个是运算的x86/ARM核,另一个是管理板的PowerPC/ARM核。轻云互联后来给我们提供了一个隐藏功能,允许在控制台上直接配置BMC的IPMI参数和固件版本回滚,这确实规避了不少远程手搓SOL的尴尬。
最后附上一条排查此类问题的黄金法则:任何关于物理机的监控告警,永远优先排查带外管理面,再排查业务面。因为带外常常是独立于业务内核的“套娃”,一旦它出现问题,你所有自动化运维工具都会瞬间变成瞎子。以上,就是一台物理机BMC的“心梗”急救实录。