上篇回顾
上一篇讲了 VRRP 协议原理、参数决策树和切换时间计算。这一篇直接上生产级最佳实践:健康检查脚本怎么写才靠谱、通知脚本怎么做、脑裂怎么防,以及面试高频考点速查。
第一部分:别只用 pgrep --- 生产级健康检查脚本
为什么 pgrep 不够?
pgrep -x haproxy 只能检查"进程是否存在"
但进程活着 ≠ 服务可用
故障场景:
HAProxy 进程死锁了 → 端口没了
pgrep 返回:✓ 活着
实际:服务不可用
完整的检测应该覆盖
系统层(服务器健康):
① 网关 ping → 外层流量能到达本机
② 网卡状态 → 网卡 UP,有 IP 地址
③ 默认路由 → 知道往哪发流量
④ 系统负载 → CPU/内存/磁盘没被压垮
应用层(HAProxy 健康):
⑤ 进程存活 pgrep
⑥ 端口监听 ss -tlnp
⑦ 内部状态 socat → show info
⑧ 后端可用 stats 页面 → 至少一个 UP
分层检测方案
为什么分层
检测太少 → 无法发现故障
检测太多 → 影响业务性能
分层检测 → 平衡全面性和性能
检测策略
快速层(每 2 秒执行):
进程 + 端口 + 网关 ping
成本:约 15ms
快速层 FAIL → 立刻返回,不跑下面层
中速层(每 10 秒执行):
CPU 负载 + socat 内部状态
成本:约 5ms
慢速层(每 30 秒执行):
curl stats + 磁盘 + 内存
成本:约 10ms
完整脚本
bash
#!/bin/bash
# /etc/keepalived/check_layered.sh
# 分层检测脚本 --- 生产级 HAProxy 健康检查
# ============ 快速层 ============
fast_check() {
# ① 进程检查
pgrep -x haproxy > /dev/null 2>&1 || return 1
# ② 端口检查(检查实际监听端口)
ss -tlnp | grep -q ":8080.*haproxy" || return 1
# ③ 网关 ping(确认网络可达)
GATEWAY=$(ip route | grep "^default" | awk '{print $3}')
ping -c 1 -W 1 -q $GATEWAY > /dev/null 2>&1 || return 1
return 0
}
# ============ 中速层 ============
medium_check() {
# ④ CPU 负载检查
local load=$(cat /proc/loadavg | awk '{print $1}')
local cores=$(nproc)
[ "$(echo "$load > $cores * 0.8" | bc)" -eq 1 ] && return 1
# ⑤ HAProxy 内部状态检查
echo "show info" | socat /var/run/haproxy.sock - > /dev/null 2>&1 || return 1
return 0
}
# ============ 慢速层 ============
slow_check() {
# ⑥ 磁盘检查
[ "$(df / | tail -1 | awk '{print $5}' | tr -d '%')" -gt 90 ] && return 1
# ⑦ 内存检查
[ "$(free -m | grep Mem | awk '{print $7}')" -lt 200 ] && return 1
return 0
}
# ============ 主流程 ============
fast_check || exit 1
[ $(( $(date +%s) % 10 )) -eq 0 ] && medium_check
[ $(( $(date +%s) % 30 )) -eq 0 ] && slow_check
exit 0
对应的 Keepalived 配置
keepalived
vrrp_script chk_haproxy_prod {
script "/etc/keepalived/check_layered.sh"
interval 2
weight -30
fall 2
rise 2
timeout 5
}
vrrp_instance VI_HAPROXY {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 12345678
}
virtual_ipaddress {
192.168.1.200/24 dev eth0
}
track_script {
chk_haproxy_prod
}
}
切换判定逻辑
系统层异常 → exit 1 → 触发切换
↑ 无论 HAProxy 是否正常,服务器都不行了
系统层正常 + 应用层严重异常 → exit 1 → 触发切换
↑ 服务器健康,但 HAProxy 挂了
系统层正常 + 应用层轻微异常 → exit 0 → 不触发切换(观察)
↑ 比如后端全部 DOWN,但 HAProxy 本身正常
第二部分:通知脚本
通知脚本的作用
Keepalived 切换角色时,自动执行脚本:
MASTER 切换 → 执行 notify_master
BACKUP 切换 → 执行 notify_backup
FAULT 切换 → 执行 notify_fault
用于:发告警、启动服务、更新 DNS、记录事件
完整的通知脚本
bash
#!/bin/bash
# /etc/keepalived/notify.sh
ROLE=$1
DATE=$(date '+%Y-%m-%d %H:%M:%S')
HOST=$(hostname)
VIP="192.168.1.200"
LOG="/var/log/keepalived-notify.log"
send_alert() {
local level=$1
local message=$2
echo "[$DATE] [$level] $message" >> $LOG
# 生产环境:企业微信/钉钉 webhook
# curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" \
# -H "Content-Type: application/json" \
# -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"[$level] $message\"}}"
}
do_master() {
send_alert "INFO" "★ 本机成为 MASTER,持有 VIP $VIP"
# 1. 确保 HAProxy 在运行
if ! pgrep -x haproxy > /dev/null 2>&1; then
systemctl start haproxy 2>&1
sleep 2
fi
# 2. 发送 gratuitous ARP 通知交换机更新 MAC 表
arping -c 3 -A -I eth0 $VIP 2>/dev/null || true
# 3. 更新 DNS(可选)
# nsupdate -k /etc/named/Kupdate.key << EOF
# server dns-server 53
# update add app.example.com 300 A $VIP
# send
# EOF
}
do_backup() {
send_alert "WARN" "● 本机成为 BACKUP,释放 VIP $VIP"
# 1. 确认 VIP 已释放
if ip addr show eth0 | grep -q "$VIP"; then
ip addr del $VIP/24 dev eth0 2>/dev/null || true
fi
# 2. 记录日志
echo "[$DATE] Keepalived 切换,请关注" >> $LOG
}
do_fault() {
send_alert "CRITICAL" "✗ 本机进入 FAULT 状态"
# 1. 尝试重启 Keepalived
systemctl restart keepalived 2>&1
# 2. 记录故障信息
echo "===== 故障信息 =====" >> $LOG
ip addr show eth0 >> $LOG 2>&1
ip route >> $LOG 2>&1
echo "=====================" >> $LOG
}
case $ROLE in
MASTER) do_master ;;
BACKUP) do_backup ;;
FAULT) do_fault ;;
esac
exit 0
在 Keepalived 配置中引用
keepalived
vrrp_instance VI_HAPROXY {
...
notify_master "/etc/keepalived/notify.sh MASTER"
notify_backup "/etc/keepalived/notify.sh BACKUP"
notify_fault "/etc/keepalived/notify.sh FAULT"
}
第三部分:脑裂预防与处理
什么是脑裂?
两台 Keepalived 都成了 MASTER,都持有 VIP
→ 交换机 MAC 表在两个端口间反复横跳
→ 部分流量到 A,部分到 B
→ 对应用来说:一会儿通一会儿不通
常见原因
① 心跳线物理断开
② VRRP 组播被防火墙拦截
③ 网络拥堵导致 VRRP 包丢失
脑裂防护三件套
1. 单播 VRRP(替代组播,更可靠)
keepalived
vrrp_instance VI_HAPROXY {
...
# 替代组播,直接向对端发送 VRRP 包
unicast_peer {
192.168.1.2 # 对端服务器 IP
}
}
为什么单播更可靠:组播依赖 IGMP 协议和交换机配置,可能被网络设备过滤或丢弃;单播是点到点直接通信,不受交换机组播配置影响。
2. 防火墙放行 VRRP
bash
# 如果使用组播方式,需要在防火墙放行
iptables -A INPUT -p vrrp -j ACCEPT
iptables -A OUTPUT -p vrrp -j ACCEPT
# 持久化规则
# CentOS/RHEL: service iptables save
# 或直接写入 /etc/sysconfig/iptables
3. 自检脚本(最后一道防线)
bash
# 在 notify_backup 中增加脑裂自检
notify_backup() {
if ip addr show eth0 | grep -q "$VIP"; then
# 脑裂!强制释放 VIP
ip addr del $VIP/24 dev eth0
logger "CRITICAL: 脑裂检测,已强制释放 VIP"
# 发送告警
curl -X POST "https://qyapi.weixin.qq.com/..." \
-d '{"msgtype":"text","text":{"content":"脑裂检测到,已强制释放 VIP"}}'
fi
}
主备配置模板
keepalived
# 服务器 A(MASTER)--- 192.168.1.1
vrrp_instance VI_HAPROXY {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
nopreempt
unicast_peer {
192.168.1.2
}
virtual_ipaddress {
192.168.1.200/24 dev eth0
}
track_script {
chk_haproxy
}
notify_master "/etc/keepalived/notify.sh MASTER"
notify_backup "/etc/keepalived/notify.sh BACKUP"
}
# 服务器 B(BACKUP)--- 192.168.1.2
vrrp_instance VI_HAPROXY {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
nopreempt
unicast_peer {
192.168.1.1
}
virtual_ipaddress {
192.168.1.200/24 dev eth0
}
track_script {
chk_haproxy
}
notify_master "/etc/keepalived/notify.sh MASTER"
notify_backup "/etc/keepalived/notify.sh BACKUP"
}
# 两台服务器配置差别只有:
# state(初始角色)
# priority(优先级)
# unicast_peer(对端 IP)
# 其他参数完全一致
第四部分:面试高频考点速查
以下是面试中关于 HAProxy + Keepalived 的高频问题,每个问题附带"一句话回答"和"展开要点"。
Q1:脑裂(Split-Brain)怎么办?
一句话:单播 VRRP + 自检脚本 + 备用心跳。
展开要点:
- 原因:心跳断、防火墙拦截、网络拥堵
- 检测:notify 脚本中检查"角色是 BACKUP 但持有 VIP"→ 强制释放
- 预防:
unicast_peer替代组播、防火墙放行 VRRP
Q2:会话保持怎么实现?
一句话 :HAProxy 用 stick-table 做会话保持,支持源 IP、Cookie、URL 参数三种 key。
展开要点:
- 三种方式:
balance source(简单)、stick-table(推荐)、Cookie Insert(最精确) - 多台 HAProxy 需要配置
peers同步 stick-table - 超时时间根据业务 session 有效期配置
haproxy
backend web-sticky
stick-table type ip size 100k expire 30m
stick on src
server web1 127.0.0.1:9001 check
server web2 127.0.0.1:9002 check
Q3:限流/熔断怎么做?
一句话 :HAProxy 三层限流 --- maxconn 熔断保护 + rate-limit 速率控制 + stick-table 精确到每个用户。
展开要点:
- 第1层:
maxconn全局并发限制(熔断) - 第2层:
rate-limit sessions连接速率限制(防 CC) - 第3层:
stick-table store http_req_rate+http-request deny单 IP 限流 - 返回 429 +
Retry-Afterheader
Q4:HAProxy 热重载原理?
一句话:SO_REUSEPORT 两进程共享端口,新进程接管新连接,旧进程处理完存量连接后退出。
展开要点:
- 新进程 bind 同一端口(SO_REUSEPORT)
- 内核将新连接分配给两个进程
- 新进程发 SIGUSR1 给旧进程
- 旧进程关闭监听 socket,处理完已有连接后退出
Q5:Nginx vs HAProxy 怎么选?
一句话:Nginx 强在 HTTP 内容处理,HAProxy 强在 TCP 协议处理,配合使用效果最佳。
展开要点:
| 能力 | HAProxy | Nginx |
|---|---|---|
| L4 TCP 代理 | ✅ 原生支持 | ❌ stream 模块 |
| 健康检查 | ✅ 内置 MySQL/Redis 等 | ❌ 需第三方 |
| HTTP 内容处理 | ❌ 有限 | ✅ rewrite/SSI/auth |
| 配置复杂度 | 简单(两层) | 中等(三层) |
Q6:健康检查怎么设计?
一句话:分层检测 --- 系统层(网关/网卡/负载)+ 应用层(进程/端口/状态),分层执行,系统层异常优先切换。
展开要点:
- 快速层:每 2 秒,进程 + 端口 + 网关 ping(~15ms)
- 中速层:每 10 秒,CPU + socat 状态(~5ms)
- 慢速层:每 30 秒,磁盘 + 内存(~10ms)
- 系统层异常 → 直接切换(服务器不健康,VIP 切过来也没用)
Q7:Keepalived 切换需要多久?
一句话:切换时间 = 健康检查延迟(fall × interval)+ VRRP 超时(advert_int × 3)。
展开要点:
- 标准配置:fall 2 × interval 2 = 4s + advert_int 1 × 3 = 3s ≈ 7s
- 快速配置:fall 1 × interval 1 = 1s + advert_int 0.3 × 3 = 0.9s ≈ 2s
- 快速配置的代价:更容易误判
Q8:多中间件统一入口怎么设计?
一句话:一个 HAProxy 暴露多个端口,每个中间件一个 frontend,Keepalived 提供 VIP。
展开要点:
- 客户端只认
VIP:端口,不关心后端 IP 变化 - 每个中间件独立 frontend,配置隔离
- 健康检查方式不同(MySQL→TCP、Redis→PING/PONG、ES→HTTP)
速查表
| 问题 | 一句话回答 |
|---|---|
| 脑裂怎么办? | 单播 VRRP + 自检脚本 + 备用心跳 |
| 会话保持? | stick-table,支持 IP/Cookie/URL 参数 |
| 限流? | maxconn + rate-limit + stick-table 三层 |
| 热重载? | SO_REUSEPORT 两进程共存 |
| Nginx vs HAProxy? | Nginx 强 HTTP,HAProxy 强 TCP |
| 健康检查? | 系统层 + 应用层,分层执行 |
| 切换时间? | fall × interval + advert_int × 3 |
| 多中间件? | 同 IP 多端口,每个中间件一个 frontend |
本期总结
- 健康检查脚本:分层检测(快速/中速/慢速),覆盖系统层和应用层,防止误判
- 通知脚本:notify_master/backup/fault 自动执行,启动 HAProxy、发 ARP、更新 DNS、发告警
- 脑裂预防:单播 VRRP + 防火墙放行 + 自检脚本 = 三件套
- 面试考点:8 个高频问题覆盖了集群方案的核心考点,理解原理比背答案更重要
全系列回顾
| 篇号 | 标题 | 核心内容 |
|---|---|---|
| 一 | HAProxy 入门与核心概念 | F5 映射、配置、算法、热重载 |
| 二 | 健康检查与 SSL 卸载 | TCP/HTTP/协议检查、SSL 三模式 |
| 三 | ACL 与高级路由策略 | ACL、会话保持、三层限流 |
| 四 | 多中间件统一 VIP 入口 | MySQL/Redis/Kafka/ES/etcd/Mongo |
| 五 | Keepalived VRRP 与参数精讲 | VRRP、参数决策树、切换时间计算 |
| 六 | 生产最佳实践与面试考点 | 分层检测、通知脚本、脑裂预防、面试速查 |