一、引言
Keepalived是实现高可用集群的核心组件,基于VRRP(Virtual Router Redundancy Protocol)协议工作。脑裂是Keepalived集群中最严重的问题之一,当集群节点间通信中断时,多个节点可能同时成为Master,导致服务异常和数据不一致。
本文将深入剖析脑裂问题的原因,提供系统性的解决方案,并分享生产环境中的最佳实践。
二、SCQA分析框架
情境(Situation)
- Keepalived广泛用于构建高可用集群
- VRRP协议依赖节点间心跳通信
- 生产环境中网络故障难以避免
冲突(Complication)
- 节点间网络中断可能导致脑裂
- 脑裂会引发双主状态和数据不一致
- 需要可靠的检测和恢复机制
问题(Question)
- 脑裂是如何产生的?
- 如何预防脑裂发生?
- 脑裂发生后如何处理?
- 生产环境有哪些最佳实践?
答案(Answer)
- 脑裂由网络中断、心跳超时、配置错误等原因导致
- 通过网络冗余、配置优化、仲裁机制预防脑裂
- 使用监控告警及时发现脑裂并自动处理
- 推荐三节点集群和专用心跳网络
三、脑裂问题深度解析
3.1 什么是脑裂?
脑裂(Brain Split) 是指高可用集群中,由于节点间通信中断,导致多个节点同时认为自己是Master节点的现象。
正常状态 vs 脑裂状态:
┌─────────────────────────────────────────────────────────────────┐
│ Keepalived状态对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 正常状态: │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Virtual Router ID: 51 │ │
│ │ Virtual IP: 192.168.1.200 │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────┐ VRRP心跳 ┌─────────┐ │ │
│ │ │ Master │ ─────────────────→ │ Backup │ │ │
│ │ │(192.168.1.10)│←─────────────────│(192.168.1.11)│ │ │
│ │ │ Priority: 100│ │Priority: 90 │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ └──────────────┬───────────────┘ │ │
│ │ ▼ │ │
│ │ VIP: 192.168.1.200 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 脑裂状态: │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ NETWORK PARTITION │ │
│ ├──────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────┐ 网络中断 ┌─────────┐ │ │
│ │ │ Master │ ─────────X──────── │ Backup │ │ │
│ │ │(192.168.1.10)│ │(192.168.1.11)│ │ │
│ │ │ Priority: 100│ │Priority: 90 │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ VIP: 192.168.1.200 VIP: 192.168.1.200 │ │
│ │ ←─────────────── 双主状态 ────────────────→ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 脑裂的危害
| 危害类型 | 说明 | 影响 |
|---|---|---|
| 双主状态 | 两个节点同时拥有VIP | 流量分发异常 |
| 数据不一致 | 同时写入两个节点 | 数据冲突、丢失 |
| 服务不可用 | 客户端连接混乱 | 业务中断 |
| 资源争用 | 共享资源访问冲突 | 数据损坏 |
3.3 脑裂产生的原因
| 原因分类 | 具体原因 | 场景示例 |
|---|---|---|
| 网络问题 | 网络中断 | 交换机故障、网线断开、光纤断裂 |
| 网络配置 | 防火墙拦截VRRP包 | iptables规则阻止VRRP协议 |
| 心跳超时 | advert_int设置过小 | 网络延迟导致心跳超时 |
| 节点负载 | CPU/内存耗尽 | 节点假死无法响应心跳 |
| 配置错误 | VRRP配置不一致 | priority、auth_pass配置不同 |
| 进程问题 | keepalived进程挂起 | 进程阻塞无法发送心跳 |
四、预防脑裂的解决方案
4.1 网络层面优化
1. 双网卡Bonding
# /etc/network/interfaces 配置示例
auto bond0
iface bond0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
bond-mode 1 # 主动-备份模式
bond-miimon 100 # 链路检测间隔100ms
bond-slaves eth0 eth1 # 绑定的物理网卡
bond-primary eth0 # 主网卡
auto eth0
iface eth0 inet manual
bond-master bond0
auto eth1
iface eth1 inet manual
bond-master bond0
2. 专用心跳网络
# 配置独立的心跳网络接口
# eth0: 业务网络 (192.168.1.0/24)
# eth1: 心跳网络 (10.0.0.0/24)
# keepalived.conf中指定心跳接口
vrrp_instance VI_1 {
interface eth1 # 使用心跳网络
mcast_src_ip 10.0.0.10 # 心跳源IP
}
3. 防火墙规则
# 允许VRRP协议
iptables -A INPUT -p vrrp -s 192.168.1.0/24 -j ACCEPT
iptables -A OUTPUT -p vrrp -d 192.168.1.0/24 -j ACCEPT
# 保存规则
iptables-save > /etc/iptables/rules.v4
4.2 配置层面优化
1. 合理设置心跳参数
# keepalived.conf
global_defs {
router_id LVS_DEVEL
vrrp_version 3 # 使用VRRPv3
}
vrrp_instance VI_1 {
state BACKUP # 所有节点都设为BACKUP
interface eth0
virtual_router_id 51 # 同一组必须相同
priority 100 # 优先级,数值越高越优先
advert_int 1 # 心跳间隔1秒
garp_master_delay 1 # 发送免费ARP延迟
garp_master_refresh 5 # 免费ARP刷新间隔
authentication {
auth_type PASS # 认证类型
auth_pass 1111 # 认证密码,同一组必须相同
}
virtual_ipaddress {
192.168.1.200/24 dev eth0 label eth0:vip
}
}
2. 非抢占模式配置
# 非抢占模式(推荐用于稳定场景)
vrrp_instance VI_1 {
state BACKUP
priority 100
nopreempt # 禁止抢占
preempt_delay 300 # 如果启用抢占,延迟300秒
}
3. 监控脚本配置
# 定义监控脚本
vrrp_script chk_http {
script "/etc/keepalived/check_http.sh"
interval 2 # 检查间隔2秒
weight -20 # 失败时优先级减20
fall 3 # 连续3次失败认为服务不可用
rise 2 # 连续2次成功认为服务恢复
}
vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh"
interval 3
weight -30
}
# 在实例中引用脚本
vrrp_instance VI_1 {
track_script {
chk_http
chk_mysql
}
}
监控脚本示例:
#!/bin/bash
# /etc/keepalived/check_http.sh
URL="http://localhost/health"
TIMEOUT=5
if curl -s --max-time $TIMEOUT $URL | grep -q "OK"; then
exit 0 # 健康检查通过
else
exit 1 # 健康检查失败
fi
4.3 资源层面优化
1. 第三方仲裁机制
# 使用共享存储作为仲裁
# 节点启动前检查锁文件
# 仲裁脚本
#!/bin/bash
LOCK_FILE="/mnt/shared/keepalived.lock"
if [ -f "$LOCK_FILE" ]; then
# 锁文件存在,检查是否有效
LOCK_PID=$(cat "$LOCK_FILE")
if kill -0 "$LOCK_PID" 2>/dev/null; then
echo "Another node is active"
exit 1
else
# 锁文件无效,删除并获取锁
rm -f "$LOCK_FILE"
fi
fi
# 获取锁
echo $$ > "$LOCK_FILE"
exit 0
2. 使用分布式一致性服务
# 使用etcd作为分布式锁
etcdctl lock keepalived-lock --ttl=30
# 或使用Consul
consul lock -name=keepalived-lock /bin/keepalived-start
3. 三节点集群配置
# 三节点集群可以避免脑裂
# 需要多数派同意才能成为Master
# 节点1配置
vrrp_instance VI_1 {
state BACKUP
priority 100
}
# 节点2配置
vrrp_instance VI_1 {
state BACKUP
priority 90
}
# 节点3配置
vrrp_instance VI_1 {
state BACKUP
priority 80
}
五、脑裂检测与处理
5.1 脑裂检测脚本
#!/bin/bash
# /etc/keepalived/brain_split_detect.sh
VIP="192.168.1.200"
PEER_IPS=("192.168.1.10" "192.168.1.11")
CHECK_INTERVAL=5
MAX_RETRY=3
# 检查本地是否拥有VIP
has_vip() {
ip addr show | grep -q "$VIP"
return $?
}
# 检查peer是否拥有VIP
peer_has_vip() {
local peer_ip=$1
ssh -o ConnectTimeout=2 "$peer_ip" "ip addr show | grep -q '$VIP'"
return $?
}
# 检测脑裂
detect_brain_split() {
local local_has_vip=0
local peer_has_vip_count=0
if has_vip; then
local_has_vip=1
fi
for peer in "${PEER_IPS[@]}"; do
if peer_has_vip "$peer"; then
((peer_has_vip_count++))
fi
done
# 如果本地和peer都有VIP,说明脑裂
if [ $local_has_vip -eq 1 ] && [ $peer_has_vip_count -gt 0 ]; then
return 0 # 脑裂检测到
fi
return 1 # 正常
}
# 处理脑裂
handle_brain_split() {
logger "CRITICAL: Brain split detected! Local and peer both have VIP $VIP"
# 策略1: 优先级低的节点主动放弃
local priority=$(grep -i priority /etc/keepalived/keepalived.conf | awk '{print $2}')
if [ "$priority" -lt 100 ]; then
logger "Priority is lower, stopping keepalived"
systemctl stop keepalived
exit 0
fi
# 策略2: 通过仲裁决定
# 可以调用仲裁脚本
}
# 主循环
while true; do
if detect_brain_split; then
handle_brain_split
fi
sleep $CHECK_INTERVAL
done
5.2 通知脚本配置
# keepalived.conf
vrrp_instance VI_1 {
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
notify_stop "/etc/keepalived/notify.sh stop"
}
通知脚本示例:
#!/bin/bash
# /etc/keepalived/notify.sh
STATE=$1
VIP="192.168.1.200"
LOG_FILE="/var/log/keepalived_notify.log"
EMAIL_TO="admin@example.com"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
send_alert() {
local subject="Keepalived Alert: $STATE"
local body="VIP $VIP state changed to $STATE on $(hostname)"
echo "$body" | mail -s "$subject" "$EMAIL_TO"
}
case $STATE in
master)
log "Changed to MASTER state"
send_alert
# 启动服务
systemctl start nginx
;;
backup)
log "Changed to BACKUP state"
# 停止服务
systemctl stop nginx
;;
fault)
log "Changed to FAULT state"
send_alert
systemctl stop nginx
;;
stop)
log "Keepalived stopped"
systemctl stop nginx
;;
esac
5.3 Prometheus监控配置
keepalived-exporter配置:
scrape_configs:
- job_name: 'keepalived'
static_configs:
- targets: ['node1:9165', 'node2:9165']
告警规则:
groups:
- name: keepalived-alerts
rules:
- alert: KeepalivedBrainSplit
expr: keepalived_vrrp_instance_state == 1 and count(keepalived_vrrp_instance_state == 1) > 1
for: 1m
labels:
severity: critical
annotations:
summary: "Keepalived脑裂检测"
description: "检测到多个节点同时为Master状态"
- alert: KeepalivedInstanceDown
expr: keepalived_vrrp_instance_state == 0
for: 5m
labels:
severity: warning
annotations:
summary: "Keepalived实例状态异常"
description: "Instance 状态为FAULT"
- alert: KeepalivedNoMaster
expr: count(keepalived_vrrp_instance_state == 1) == 0
for: 30s
labels:
severity: critical
annotations:
summary: "无Master节点"
description: "所有Keepalived节点都不是Master状态"
六、生产环境最佳实践
6.1 架构设计建议
推荐架构:
┌─────────────────────────────────────────────────────────────────┐
│ 高可用集群架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Load Balancer │ │
│ └────────┬────────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ (Priority │ │ (Priority │ │ (Priority │ │
│ │ 100) │ │ 90) │ │ 80) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └───────┬───────┴───────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ 业务网络 心跳网络 │
│ (192.168.1.0/24) (10.0.0.0/24) │
│ │
└─────────────────────────────────────────────────────────────────┘
6.2 配置检查清单
| 检查项 | 说明 | 配置要求 |
|---|---|---|
| VRRP ID | 同一组必须相同 | virtual_router_id一致 |
| 认证密码 | 同一组必须相同 | auth_pass一致 |
| 心跳间隔 | 避免过短 | advert_int ≥ 1秒 |
| 非抢占模式 | 生产环境推荐 | nopreempt |
| 监控脚本 | 检查关键服务 | track_script |
| 日志配置 | 便于排查问题 | logfile配置 |
| 防火墙 | 允许VRRP协议 | iptables放行 |
6.3 应急响应流程
脑裂应急响应流程:
1. 收到告警通知
2. 确认脑裂状态(检查各节点VIP)
3. 分析原因(网络/配置/资源)
4. 执行恢复操作:
- 如果是网络问题:修复网络连接
- 如果是配置问题:同步配置并重启
- 如果是资源问题:释放资源或扩容
5. 验证恢复(确认单Master状态)
6. 记录问题和解决方案
7. 复盘优化(预防再次发生)
七、常见问题排查
7.1 问题诊断表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 双主状态 | 网络中断 | ping对方节点 | 检查网络连接 |
| VIP不漂移 | 优先级配置错误 | 检查priority | 调整优先级 |
| 抢占频繁 | 无nopreempt | 检查配置 | 添加nopreempt |
| 脚本不执行 | 权限问题 | 检查脚本权限 | chmod +x script.sh |
| 日志无输出 | 日志路径错误 | 检查logfile配置 | 修正路径 |
| 认证失败 | auth_pass不一致 | 对比配置文件 | 统一密码 |
7.2 排查命令速查
# 查看Keepalived状态
systemctl status keepalived
# 查看日志
tail -f /var/log/keepalived.log
journalctl -u keepalived -f
# 检查VIP状态
ip addr show | grep inet
# 检查VRRP进程
ps aux | grep keepalived
# 检查网络连通性
ping <peer-ip>
tcpdump -i eth0 vrrp
# 验证配置文件
keepalived -t -f /etc/keepalived/keepalived.conf
八、总结
核心要点
- 脑裂原因:网络中断、心跳超时、配置错误、节点故障
- 预防措施:
- 网络层面:双网卡Bonding、专用心跳网络
- 配置层面:合理心跳间隔、非抢占模式、监控脚本
- 资源层面:第三方仲裁、三节点集群
- 检测处理:脑裂检测脚本、通知脚本、Prometheus告警
- 最佳实践:三节点集群、专用心跳网络、nopreempt模式
最佳实践清单
- ✅ 使用三节点集群避免脑裂
- ✅ 配置专用心跳网络
- ✅ 启用nopreempt非抢占模式
- ✅ 配置监控脚本检查关键服务
- ✅ 建立完善的监控告警体系
- ✅ 定期演练故障恢复流程
本文对应的面试题:Keepalived脑裂怎么解决?
附录:完整配置示例
keepalived.conf完整配置:
global_defs {
router_id LVS_DEVEL
logfile /var/log/keepalived.log
logfacility local0
vrrp_version 3
}
vrrp_script chk_http {
script "/etc/keepalived/check_http.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
nopreempt
preempt_delay 300
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.200/24 dev eth0 label eth0:vip
}
track_script {
chk_http
}
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
}
文档信息
- 本文作者:soveran zhong
- 本文链接:https://blog.clockwingsoar.cn/2026/06/30/keepalived-brain-split-solutions/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)