Keepalived失效后的最后防线:硬件看门狗与STONITH物理隔离实战
被忽视的致命盲区
做高可用架构的人,十个里有九个会在简历上写"精通Keepalived+LVS"。但真正在生产环境踩过坑的都知道,软件层面的健康检查有个致命的假设前提:当前节点还能正常执行检测逻辑。当这个前提本身崩塌的时候,Keepalived连自己该做什么都判断不了。
一个典型的死亡螺旋是这样的:
内核态CPU占用100%(驱动bug/段错误)→
系统完全无响应,无法处理网络中断 →
keepalived进程还在,但已无法发送VRRP通告 →
对端节点判定本端下线,接管网段VIP →
本端应用实际还在运行(只是卡死了)→
两个节点同时持有相同VIP → 脑裂发生 →
业务数据开始出现双写、相互覆盖...
这种情况下,你配置的notify_*脚本、lvs_syncd统统没用。你需要的是:在软件不可信的时候,有一个比操作系统更底层的存在能够强制介入。
这就是今天要聊的主题——利用IPMI/STONITH实现硬件级别的节点物理隔离,把那些"假死"的机器从网络上彻底切断,或者直接给它断电重启。
为什么软件手段不够用
Keepalived的信任模型缺陷
VRRP协议本身设计得很巧妙,但它有个隐含前提:所有参与方都认为对方能正常通信才代表对方活着。这在网络层面没问题,但到了单机层面就出事了:
| 故障类型 | Keepalived表现 | 结果 |
|---|---|---|
| 网络断开 | 能检测到,立即切换 | ✓ 可控 |
| keepalived进程崩溃 | 对端超时接管 | ✓ 可控 |
| 内核卡死/panic | 无法发送通告,对端接管VIP | ⚠️ VIP可能双活 |
| CPU持续100%导致调度失常 | keepalived还在跑,但行为异常 | ❌ 不确定 |
第三种和第四种情况,正是我们最怕的"真·脑裂"。这时候VIP漂移过去了,但原节点的应用程序可能还握着数据库连接或者文件锁,继续往共享存储里写数据。两个节点都在干活,数据一致性直接完蛋。
为什么需要物理层fencing
软件fencing(比如给虚拟机发命令让它关机)在宿主机自身出问题时就鞭长莫及了。你不能让一台宕机的电脑去执行你发的指令。真正的fencing需要满足一个条件:执行者和被fence的目标相互独立,没有任何单点故障会导致两者同时失效。
IPMI恰好满足这个条件。它走的是独立的管理网络(BMC/IPMI口),不依赖宿主机的操作系统,不依赖宿主机的网卡,不依赖宿主机加载的任何驱动。只要BMC芯片本身还通电,它就能响应来自外部的命令。
STONITH是什么,为什么名字这么吓人
如果你查STONITH的资料,会发现它全称是"Shoot The Other Node In The Head"——字面意思就是爆头。这名字来源于早期分布式系统的粗暴理念:当怀疑对方可能失控时,与其尝试温柔地沟通,不如直接断电让它闭嘴。
现代语境下,STONITH已经进化成一套成熟的标准化机制,它的定义变成了:使用外部代理(fence agent)向基础设施发送控制命令,让目标节点停止运行或重新启动的过程。这里的"基础设施"可能是:
- IPMI兼容的BMC控制器(最常用)
- SSH连接到虚拟化平台的API(如virsh fence)
- SCSI Reservation reset(HBA卡级别)
- PDU电源插座控制(智能PDU)
- 云平台API(如AWS EC2 StopInstances)
每种方式对应不同的fence agent插件。Pacemaker/Corosync原生支持这些agent,而Keepalived本身没有这个能力,需要配合集群管理器使用,或者手动调用fence工具链。
IPMI基础:你以为你了解的BMC其实更强大
从远程管理口说起
服务器的IPMI口通常是一个独立的RJ45网口,旁边可能有"Management"或"BMC"标识。它有自己独立的MAC地址,能被单独分配IP地址,即使服务器OS里的网卡全部down掉,这个口通常还是活的(除非整个机器完全断电)。
通过这个独立通道,你可以:
# 安装工具
apt install ipmitool # Debian/Ubuntu
yum install OpenIPMI ipmitool # RHEL/CentOS
# 查看BMC网络配置
ipmitool lan print 1
# 示例输出:
# Set in Progress : Set Complete
# IP Address : 192.168.1.100 ← 这是BMC自己的管理IP,不是业务IP!
# MAC Address : aa:bb:cc:dd:ee:ff
# Subnet Mask : 255.255.255.0
# Default Gateway : 192.168.1.1
用IPIMI远程控制服务器电源状态
这是后续所有fencing操作的基础,你需要先确认能用这条命令控制目标机器开关机:
# 查看当前电源状态 (本地)
ipmitool power status
# 在另一台机器上远程查看 (通过BMC IP)
ipmitool -I lanplus -H <BMC_IP> -U admin -P password power status
# 如果返回 "Chassis Power is on",说明能通讯,继续往下走...
常用power子命令包括:status、on、off、cycle(等于off再on)、reset。其中 power cycle 是最常用的重置动作——它不会让被操作者有机会拦截或拒绝,直接硬重启。
连接失败的常见原因排查表
| 问题现象 | 可能原因 | 检查方法 |
|---|---|---|
| Connection timeout | BMC未开机/网线不通/防火墙阻断UDP port 623 | ping <BMC_IP> 检查连通性 |
| Unable to establish IPMI v2 / v1.5 session | Authentication mode不匹配(RO vs ADMIN) 或密码错误 | 检查BIOS里BMC的用户权限设置 |
| Get SEL Info failed, rv=20 (-1) / License required... (Dell/iDRAC) | 需要额外许可证或会话加密配置差异 |
很多企业服务器默认只开启了ReadOnly账户,需要进BIOS/BMC Web界面创建具有Admin权限的用户才能执行power操作。另外有些厂商(Dell iDRAC)的某些版本要求开启Lanplus才能用v2协议,ipmitool 默认优先尝试lanplus,如果后端不支持会回退到lan导致认证失败,这时可以加 -I lan 参数强制指定v1版本试试,或升级iDRAC固件解决兼容性问题。
实操:从零搭建基于STONITH的高可用方案
下面的演示以两台CentOS/RHEL机器为例,组成一个简单的MySQL双主复制高可用集群。我们会一步步完成完整的部署流程,确保即使操作系统完全冻结,其中一台也能把另一台踢出去。先列出关键信息以便于实际操作参考:
角色规划:
node-a → OS eth0:10.xx.xx.xx,BMC eth0-MGMT:192.yy.yy.yy,业务VIP:10.xx.xx.xxVIP
node-b → OS eth0:10.xx.xx.xa,BMC eth0-MGMT:192.yy.yy.yb,业务VIP同↑
pacemaker + corosync + pcs 作为上层集群管理器(本例的核心),因为keepalive仅做LVS转发层,这里用pcs统一管理资源更稳妥)。如仅想用keepalive+lvs+dmmqy,已有的keepalive.cfg可继续沿用,只需在最外层套一层pacemaker即可完成自动触发stonith-triggered fencing的需求,无需改动现有keepalive configmap。)
补充说明:本文以pcs/pacemaker为例展示完整的 fencing 自动触发链路。如果你当前的架构已经跑着keepalive+LVS,只需要在 pacemaker 层加入 fence设备并把 keepalive进程作为受控资源纳入pcs管理即可,现有keepalive配置文件不需要任何改动,后面的步骤会详细说明怎么整合)。
接下来的几个章节将按顺序展开完整的操作步骤,帮助你在真实环境中实现可靠的自动 fencing 功能。建议先在测试环境完整走一遍流程,确认每个环节都能正常工作后再部署到生产。如果你的环境中已经有可用的 fence设备,可以跳过相应章节直接进入下一步;如果是首次接触,建议按顺序全部完成,以建立完整的认知和排错能力。
第一步:BMC配置检查清单(在每台机器上都要做)
SSH登录或者通过KVM跳转到每台服务器的ilo/idrac/bmc web界面,完成以下配置,否则后面的步骤寸步难行。这是很多人第一次配stonith翻车的根本原因——他们以为装好软件就能工作,却忽略了硬件层面的准备工作。BMC如果没有正确配置,后续所有的remote power操作都会石沉大海,根本不可能实现自动化。以下为各厂商通用检查项目,Dell iDRAC、HPE iLO、Supermicro X10+系列等主流平台均适用,具体菜单位置因型号不同略有差异,可参考对应用户手册搜索”IPMITOOL enabled“或类似关键字定位功能入口)
用户权限设置(BIOS/BMC Web界面中操作)
大部分出厂默认只有ReadOnly账号,你需要至少创建一个Admin权限账号用于自动化脚本。本例创建admin/adminpasswd,实际生产环境请换成强密码并妥善保管!找”User Management“ → "Add User",用户名填admin,角色选Administrator,保存。用命令行验证一下能否登录成功,这是最直接的验证方式,后面配pcs时也会用到同样的用户名密码,如果这里没配对后面全是坑。具体菜单位置因型号不同,一般在Security选项卡或User Management页面下,设置用户名密码后还需要Enable该用户的Remote Access功能,否则LAN上根本无法登录。如果不确定,可以先用ipmitool user list 和 ipmitool user summary 查看当前已有哪些用户及其启用状态,一个空的user list往往是导致后续Connection Timeout的直接元凶。再确认Enabled User Channels包含LAN Channel(通常是Channel 1),否则即使用了正确的用户名密码也无法通过以太网访问)。
网络连通性确认(同网段其他机器上执行)
这一步放在两台node上都做完后再做,避免两边都没配好互相排查浪费时间。用同一网段内任意一台Linux机器(或你自己的办公电脑如果能路由到那个网段),分别ping两边的bmc ip,如果ping不通先检查线缆、光模块、中间交换机是否有portfast/vlan trunk放行等基础连通性问题,这些不在本文范围内请自行解决。能ping通后再测ipmitool能不能握手,这一步通了才算完成物理层的准备工作,后面的软件部分才有意义。如果连这一步都不通,后面的pacemaker/corosync配得再漂亮也没用,因为自动化脚本根本发不出指令)。
#!/bin/bash
NODE_A_BMC="192.yy.yy.A"
NODE_B_BMC="192.yy.yy.B"
ADMIN_USER="admin"
ADMIN_PASS="adminpasswd"
for bmc_ip in $NODE_A_BMC $NODE_B_BMC; do
echo "=== Testing BMC at $bmc_ip ==="
result=$(ipmitool -I lanplus -H $bmc_ip \
-U $ADMIN_USER \
-P $ADMIN_PASS \
power status 2>&1)
if echo "$result" | grep -q "Chassis Power"; then
echo "[OK] BMC accessible, status: $result"
else
echo "[FAIL] BMC test failed, output:"
echo "$result"
fi
done
预期结果应该是两个"[OK]",输出类似 Chassis Power is on。如果出现timeout,首先检查两端是否真的在同一大二层广播域内,很多企业的management网络是单独划的VLAN;其次确认交换机的IPv4 udp/623端口没有被ACL拦掉;如果用的是云上托管服务器则没有独立的BMC,这种场景见后文替代方案)。
BMC固件升级提醒(Dell/HPE特定,非必读但建议了解)
新版本的iLO/iDRAC固件往往修复了旧版中存在的Session Hang、Bios Boot不触发、SEL日志丢失等问题,如果你的服务器已经跑了好几年没升级过固件,建议挑个维护窗口做一次升级再去折腾后面的部分。可以去厂商官网下载对应服务编号(Service Tag)的固件ISO,用Lifecycle Controller刷入。整个过程不需要停机,刷新完成后会自动重启BMCs,不会影响正在运行业务负载的服务器。关于具体哪个版本稳定,不同型号表现不一,比如Dell R740上的iDRAC4曾经有PowerCycle卡住必须手工干预的长达数分钟的BUG,后来某个次版本才修复,遇到这类问题可以在厂家的知识库搜”iDRAC unresponsive after power cycle known issue”,结合自己的具体型号和当前固件版本综合判断要不要升级以及升到哪个版本再动手,以防踩进另一个坑).
第二步:安装集群组件并初始化(同两台Node都执行)
yum install pcs pacemaker corosync fence-agents-all resource-agents pssh net-tools wget vim-enhanced psmisc jq # RHEL/CentOS
systemctl enable pcsd --now && systemctl enable corosync --now && systemctl enable pacemaker --now
cat /etc/passwd \| grep hacluster || useradd hacluster && echo "hacluster:hacluster_password" \| chpasswd # 必须为hacluster用户设密码,后续成员认证依赖此账户,默认空密码会导致authconfig报错
firewall-cmd --permanent --add-service high-availability && firewall-cmd --reload # 如启用了firewalld,需开放ha相关端口
setenforce Permissive && sed 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config # SELinux过于严格时某些ha工具行为异常,先放宽以便快速验证,后续再精细化调优)
pcs host auth node-a node-b --username hacluster --password 'hacluster_password' # 两台之间双向认证,建立Cluster内部互信通道,是后续corosync/pcsd内部通信的基础。注意--username必须是hacluster,且两边PCS Password需一致,否则pcs cluster start时会报Authentication failure,导致cluster起不来,两边都需要确保/etc/passwd里存在hacluster且密码相同!)
上述命令需要在两台Node上分别执行完毕后,才可以开始创建cluster。否则后面启动cluster时会因为成员认证失败而hang住,导致corosync ring无法建立,整个高可用就废了,所以这步非常关键,建议反复确认无误后再继续往下。如果你是首次部署,建议在任意一台Node A上做完上面的安装配置,然后scp同步配置文件到Node B,再用pcs host auth验证双向通信,这样可以避免遗漏某一步骤。同时注意每次修改完corosync.conf或cluster.conf,都需要重新reload corosync.service使改动生效,而不是restart,后者会导致短暂的服务中断。通过pcs cluster reload corosync <nodename> 可以平滑更新而不影响业务流量.
第三步:在pacemaker层注册fence设备,让自动化成为可能(前序所有准备就绪后才可进行,否则后面 pcs cluster start 会失败)
接下来是最关键的步骤:将前面验证好的两个BMCIpmi注册为Pacemaker认识的Fence Agent,这样当Corosync心跳检测发现某个Node失联超过阈值时间,就会自动触发相应的Fence Agent向目标Node发送power reset指令,实现无人值守的自动恢复。Fence设备的定义是全局性的,一旦声明所有Node共享,所以在任意一个Member Node上执行一次即可,不需要重复注册,也不会重复发送多次指令造成混乱。所有后续的资源约束、条件表达式都将围绕这两个FENCE_DEVICE_NAMEs展开,它们构成了整个High Availability架构的最底层保险丝,没有它们前面配置的Virtual_IP、LVS、KEEPALIVED都是空中楼阁,因为在极端情况下永远不会有自动干预能力。以下为核心代码,必须逐字对照填写,所有占位符替换为你自己环境的真实值,任何一项拼写错误都会导致Automatic Fencing Failure,后果极其严重,所以强烈建议先复制粘贴到一个文本编辑器里逐一核对无误后再粘贴进Terminal批量执行:
pcs stonith create fence_node_a \
fence_ipmilan_plus \ # 注意此处必须加_plus后缀,这是支持Lanplus(v2)的版本,不加则默认走明文v1,可能被部分新版BMCs拒绝握手导致Error creating fences,具体原因同上第二步提到的加密协商兼容性问题
hostname=node_a \ # 与uname –n输出一致,此处大小写敏感,若不一致将导致stonith_agent找不到对应的peer node,引发lookup failure,请先用uname –n核实你的hostname是什么格式再填入!
ipaddr=192.yy.yy.A \ # BMC management Port 的IP地址,不要误填成业务eth0上的IP地址,两者不是同一个东西!一旦填错相当于告诉agent去远程操控一台根本不存在的假设备,当然也就永远不会生效了! 请再次核对第二步输出的结果中Set in Progress之后的实际IP地址,并复制过来,注意别漏掉小数点!)
username=admin \ # 与第一步创建的具有Admin权限的IMPI用户名保持一致,默认是空的话会自动使用匿名ADMIN账户,某些厂商出厂默认值可能导致无实际操作权限,这里明确指定更安全可靠!
passwd=adminpasswd \ # 与第一步设置的password一致,引号不要丢,也不要带多余的空格,否则解析时会截断后半段导致Auth fail,尤其注意不要在这里使用$符号开头的内容,会被shell转义替换!
action=reboot \ # 重启而非关机,保持服务的自恢复能力,关机虽然更彻底但会增加人工干预成本,重启大多数情况下足以打断死锁状态让服务恢复正常,除非遇到内核Panic级别故障重启无效时才考虑改用poweroff!
power_wait=5 \ # 命令发出后等待电源状态反馈的超时秒数,适当延长有助于过滤瞬时网络抖动导致的误判,但太长会拖慢Failover整体时间,生产环境建议根据机房电力响应速度和网络延迟综合评估,一般不低于3秒不超过15秒为宜!
pcmk_host_list="node_a"\ # 指定此Agent负责管控的目标Host列表,多个Host之间逗号分隔不加空格,注意此参数名在不同版本的pacemaker语法略有差异,新版偏好pcmk_host_list,旧版可能是host-list,两个参数名不能混用否则会被忽略! 可以用pcs stonith show fence_apc_snmp_ver_2c查看具体文档!)
pcmk_delay_max=15s\ # 为防止因网络瞬时抖动引发的同时发起冲突,在随机[0,15s]区间内引入一个人为延迟差,让Primary率先发起抢夺,有效降低”双重Fencing“导致的意外双关风险,这是一个重要的保护机制,请务必加上!
pcs stonith create fence_node_b \
fence_ipmilan_plus \
hostname=node_b \
ipaddr=192.yy.yy.B \
username=admin \
passwd=adminpasswd \
action=reboot \
power_wait=5 \
pcmk_host_list="node_b"\
pcmk_delay_max=15s
pcs property set stonith-enabled=true # 全局开启stonith,即允许cluster自动触发hardware fenced,如果不设置默认为false,此时即使上面定义好了device也不会被执行,整个SafetyNet等于白装了!!! 此参数是整个High Availability的最核心开关,也是最容易忘记的配置项之一,必须显式设置为true才能让自动化生效!!!
pcs property set stonith-action=reboot # 指定全局缺省的Stonith动作为Reboot而非PowerOff,更符合“瞬间打断死锁然后让服务自恢复”的设计初衷,除非有特殊理由一般不改!
pcs property set no-quorum-policy=suspend # 当只剩单节点仍存活时暂停资源而非试图转移,防止单点环境下误Fencing产生更大范围的服务中断,适用于纯主备模式的双机场景,多于三节点的奇数集群可以设为ignore让它继续工作;
pcs constraint location LBS_res prefers node-a score=200 #LBS_vip资源倾向于留在主节点,配合后续心跳检测,当主的心跳消失超过阈值时间(default30s),备立即提升为主承接LBS_VIP,LVS_KEEPALIVED_config无需任何改动,前提是你的KEEPALIVED已在这两台上运行并由systemd托管,具体整合见下一节!)
可以通过 pcs stonith show 和 pcs property show 来验证刚才的配置是否正确写入,尤其是stonith-enabled应该显示true而不是默认值false,这一步经常被人忽略导致后面的automatic trigger永远不会发生。然后找一个不影响业务的测试窗口,用手工触发一次真实的Fencing来验证整个链路是否能work,这个环节绝对不能跳过,是骡子是马拉出来遛遛才知道,不能等到真正出事才发现链路不通,那样就来不及了!
一个容易被忽视的参数细节:hostname vs uname的区别!
很多人在这里栽跟头,原因在于Pacemaker内部的资源调度是基于Corosync ring传递过来的node name,而这个name是从 /proc/sys/kernel/hostname,也就是 uname -n 返回的值,不是你在 /etc/hosts 里定义的别名,也不是你ssh上去看到的prompt名称。一旦两者不一致,pcmk_host_list="node_a" 就永远匹配不到任何东西,因为Corosync传递过来的身份和你写进去的不一样。建议先用以下命令核实两遍确保完全匹配,然后再写到pcs stonith create里,这样能从源头避免这类莫名其妙的Mismatch问题:
uname -n ## 返回的就是corosync使用的IDENTIFIER,例如输出可能是 node_a.localdomain 这种带域名后缀的全称,如果你之前只是写了简短的主机名,这里就会对不上!所以建议两边都统一,要么都用短名,要么都用FQDN,全局保持一致,不要混着用!
hostnamectl status ## 显示完整的主机名信息,包括Static hostname, Transient hostname, Pretty hostname,有助于理解三者之间的继承关系!
cat /etc/corosync/corosyn.conf ## 如果已经初始化过,可以在此文件中找到ring0_addr字段下方对应的bindnetaddr,理解corosync是如何绑定网络的,这对后期Troubleshooting非常重要,尤其是多网卡绑定Teaming的场景下,确认使用的是active-backup还是lacp模式,是否有bonding factor影响心跳质量!
grep nodelist /etc/corosync/corosyn.conf ## 直接提取nodes列表,与uname –n对比,快速定位有没有不一致的地方!
如何让已有的Keepalived接受pacemaker的统一指挥?
很多人问:我现在跑得好好的keepalvd,不想推翻重来怎么办?答案是:不用改keepalive配置文件一分一毫,只需要把现有的LVS+VIP当作普通资源交给pacemaker托管就行。具体来说就是把原本通过systemctl管理的keepald进程迁移给pcsd管理,加上必要的监控,让pacemakermer知道它什么时候crash了该采取什么行动。这个过程中间会有短暂的闪断,对于生产环境建议选择低峰期操作:
## 先禁用原有的systemd管理,避免两边同时抢着拉起进程,产生冲突:
systemctl disable keepaled ; systemctl stop keepaled
## 注册为pcs资源的两种风格,选一种即可:
pcs resource create LBS_keepaled systemd:keepaled op monitor interval=30s ### 最简单,自动生成start/stop/monitor三个action,适合标准rpm/deb包安装的场景;
pcs resource create LBS_keepaled lsb:keepaled clone globally-unique=false ### 如果你的分发包不是标准service name,可以用lsb类型自定义command path;
## 把virtual ip也一并托管进来,这才是最终承接流量的地址:
pcs resource create LBS_vip ocf:heartbeat:IPs addr=<VIP_ADDR>/24 cidr_netmask=24 nic=<NIC_NAME> op monitor interval=<CHECK_INTERVAL>s
## 设置位置约束,让LBS_keepaled服务和VIP尽量在一起,减少跨机房跳站点的延迟开销:
pcs constraint colocation add LBS_keepaled with LBS_vip scoreinfinity ### INFINITY表示强关联,永远不分开; 若只想倾向性关联可以用数值如200代替,表示权重差;
## 定义启动顺序,先拉起vip再做LVS health check,反之则会报nic not ready bind error:
pcs constraint order LBS_vip then LBS_keepaled kind=Mandatory symmetrical=true
## 最终效果:当某一侧的心跳连续丢失超过quorum timeout threshold(默认30S),
Pacemkerer判定该侧为Lost,将自动trigger fenced_device指向它的Agent,
后者通过IMPI下达PowerCycle指令,等待几秒让目标Server重启完成后,
Cluster Regroup动作会让剩余的健康节点接管网段的VIP,同时启动本地的LBS服务,
对外持续提供服务。整个过程全程无需人工介入,且不会产生Double-VIP BrainSplit问题!
## 可以通过如下命令查看当前资源和约束关系是否符合预期,类似一个可视化的拓扑图:
pcs constraint show verbose ; pcs resource show ; pcs resource relations LBS_keepaled ## 用于debug约束链路是否形成闭环,有没有missing link;
crm_verify-LVV ## 系统完整性校验,如果有violation warnings在此阶段就会暴露出来,比上线后发现要好得多;
crm_mon-Ansvizor #-rn ////实时监控面板,看resource migration和各种event log;
至此你已经完成了全自动Hardware Fenced HA Cluster的基本搭建!接下来要做的事情是在测试环境模拟各种Failure Scenario,观察Cluster的实际行为是否符合预期,记录每次failover的时间和recovery路径,写一份属于你们团队的RunBook,方便以后有人接手或者紧急时刻快速查阅,而不是临时抱佛脚.
生产级注意事项:那些培训材料不会告诉你的事
你真的理解quorum吗?
双机环境天然是没有quorum的——一过半才算多数,那一半是多少?答案是任何一个单独的节点都不够。所以默认策略应该是suspend,即只有一边活着的时候那边暂停,等另一边确认挂掉再接管。如果你设成ignore,那单边也会继续运行,看起来服务没中断,但如果真的是Network Partition而不是真的宕机,两边都会各自认为自己是唯一合法的master,数据就要出问题了。三节点以上才考虑改成正常的多数投票逻辑,而且要用增加QDevice/QNetd的方式人为制造出一个第三方见证者,防止平票僵局,这才是正确的做法。双机的设计哲学就是靠第三方仲裁器(Fencing Device)而不是票数来解决问题,理解这一点很重要,切不可想当然地把 quorum policy 设成 ignore 然后祈祷不要出事.
测试永远要做,但要选对方法论!
有些人喜欢直接在生产敲一堆killall命令然后观察,这种方法只能测到进程crash这一条路径,内核级freeze根本模拟不出来。你需要的是能够真正冻结OS的手段。最有效的方法是用sysrq-trigger配合magic sysrq:
echo w > /proc/sysrq-trigger ### Watchdog check,当前CPU调度队列有没有阻塞,一眼看穿,不用猜!
echo s > /proc/sysrq-trigger ### Sync all filesystems,防止数据损坏,做这步之后再干别的;
sync; echo c > /proc/sysrq-trigger ### 让kernel panic进入无限递归循环,最终触发hung_task_timeout,之后要么Watchdog Reboot,要么Bootloader timeout,是一种相对可控的内核级freeze模拟方法,比拔电源线温和多了而且可以repeat;
watchdogtool-t <DEVICE> disable && killall WATCHDOG_PROCESS_NAME & sleep [N_seconds] && ps aux\|grep WATCHDOG_ACTUALLY_STILL_RUNNING? ### 通过停掉Hardware watchdog daemon来触发watchdog计时器到期,系统会在超时后强制reset,相当于模拟了一个非常真实的hardware-level freeze,比软层面的sleep/kill更贴近真实场景,也更容易复现一些只有在真实hardware reset时才出现的边缘case比如SELinux label mismatch或者NFS stale handle。不过要注意这种test必须在非业务时段做,因为reset瞬间会造成正在进行的disk IO数据损失,虽然概率极低但不排除会遇到fsck强制检查耗时过长的问题。
无论哪种test方式,都要提前通知值班团队,并在测试前后做好记录,包括trigger前后的Cluster状态快照(crm report)和相关的日志文件(通常位于 /var/log/corosync/ 和 /var/log/pcsd/ ),方便事后复盘分析。这些历史记录对于长期维护和高可用架构迭代优化非常有价值,每次Test都应该归档保留.
日志分析和监控告警的关键指标有哪些?
光搭建起来还不够,你需要知道怎么观察它的健康状态。下面是我推荐的核心指标和对应的告警阈值,这些都是业界通用的最佳实践,结合自己的环境和RTO/RPO要求适当调整数值即可,不必生搬硬套:
#!/bin/bash
EXPECTED_NODE_COUNT=$(grep nodelist.rpm_root /etc/corosny.conf wc-l)
CURRENT_ALIVE=$(crmadmin-S_t dumb\| grep mbr\|wc-l)
if [[ $CURRENT_ALIVE-lt $[EXPECTED_NODE_COUNT] ]]; then
echo "CRITICAL: Expected ${EXPECTED_NODE_COUNT} nodes but only ${CURRENT_ALIVE} alive!" >&2
exit 2
fi
PCS_FENCE_HISTORY=$(timeout5 pcs stonith history show ALL)\
if [[ $(echo "$PCS_FENCE_HISTORY GREP Success\|wc-l)-eq0]];then
echo "WARNING:Fail count for Stoniths seems zero — has the cluster ever been tested?"
fi
journalctl-u pacemaker-fsince "-5minutes"-Noend\banner &>/dev/null\&& exit OK \|\ exit WARNIN G
tail-n500 /var/log/messages grepi-i-e"(error\|critical\|fail)"-&&\jobs-p&>/dev/null || true
if [$?-eqO]];then exit WARNING fi
DCM_TIMEOUT=$(\date+%s-d "$(pcssafely_dump dc-sbd last狮wtime fmt=%Y-%m-%dT%H:%M:%S)")
NOW=$(\date+%s)
if[$[NOW-DCM_TIMEOUT]-gt360]];then
echo "CRITICAL:DCM hasn't been updated in over6min — possible split brain risk!"
fi
exit OK
}
添加到cron,每5分钟跑一次,配合Pagerduty/Zabbix/Prometheus Alertmanager 实现夜间报警,
这样即使没人盯着也能及时发现问题.
}
}
不过要注意,上面这些Check最好放在独立的Monitor Server上跑,不要放在Cluster Member自身,因为当某个Member自身出问题时monitor也可能一起挂,起不到观测作用。建议至少有两套独立的Monitor Instance分布在不同子网,通过Consul/Eureka之类的Service Discovery汇聚结果,降低Single Point of Failure影响范围。此外可以考虑将Stonish Event实时推到SIEM平台(如Elastic Security),方便日后审计和安全事件溯源,这也是合规要求较高行业的常见需求.
}
最后一个容易忽视的点:**定期演练!**很多公司的HA Cluster建好之后三年五年都没动过,等到真正出事才发现各种corner case从来没测过,到那时候就不是技术问题了,而是运维流程和组织协调的问题。所以强烈建议至少每半年搞一次Chaos Engineering式的演练,把各种能想到的场景都过一遍,包括半夜、周末、节假日等各种时段,把Runbook的执行时间和成功率记录下来,作为SLA承诺的依据。这样既是对自己负责,也是对用户负责。