Keepalived 系列(二):生产最佳实践与面试考点

上篇回顾

上一篇讲了 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-After header

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、参数决策树、切换时间计算
生产最佳实践与面试考点 分层检测、通知脚本、脑裂预防、面试速查
相关推荐
Ruiery3 小时前
Linux 6.6内核 PCIe 深度解析(九):复位机制 — 从 FLR 到 Secondary Bus Reset 的降级链
linux·运维·服务器
Wang's Blog3 小时前
Vibe Coding一人即团队系列59:基于PPTmaster与Claude Desktop的本地可编辑PPT自动化生成方案
运维·自动化·powerpoint
不剪发的Tony老师3 小时前
Navop:一款工具搞定数据库、SSH、SFTP、远程桌面、AI Agent
运维·数据库·ssh
学烹饪的小胡桃3 小时前
WGCLOUD支持哪些告警方式
linux·运维·服务器·网络·安全
qq7590353663 小时前
2026 docker部署Hub 监控中心管理多台服务器硬盘和内存
运维·服务器
weixin_440730504 小时前
socket简单介绍-三次握手四次断开+一个例子
运维·服务器
做运维的阿瑞4 小时前
Linux ELF 文件
linux·运维·服务器
遇见小修修4 小时前
打印机连不上电脑无法打印
大数据·运维·python·电脑
Jay Kay4 小时前
RDMA中CQ为什么用轮询?——从CQ、Doorbell到GID的深度解析
运维·服务器·网络
kruptos5 小时前
嵌入式 Linux 驱动开发:第一个内核模块怎么写?
linux·运维·驱动开发·嵌入式硬件