第 26/30 天(重访进阶版)
前言
在首发文章中,我们用 Keepalived + HAProxy 搭建了一套基础的负载均衡集群:VIP 漂移 + 后端分发。但在生产环境中,真正的挑战远不止”能切能分”——健康检查误判导致流量全挂、Keepalived 脑裂双双抢占 VIP、TLS 卸载性能瓶颈、慢连接耗尽后端连接池,这些才是让运维工程师彻夜难眠的根源。
本文是高可用与负载均衡进阶实战:上半部分深入 Keepalived 的 VRRP 调优与脑裂防御,下半部分展开 HAProxy 的生产级配置(TLS 卸载、HTTP 路由、速率限制、健康检查策略),最后给出完整的监控体系与故障排查 SOP。每一节都包含可以直接复制的配置和真实踩坑经验。
一、Keepalived 深度调优:从「能切换」到「可靠切换」
1.1 VRRP 协议参数精调
Keepalived 的核心是 VRRP(虚拟路由冗余协议),通过组播通告实现主备选举。默认参数在千兆内网中够用,但在跨交换机、跨机房场景下必须调优。
# /etc/keepalived/keepalived.conf — 生产级参数
vrrp_global {
# 多播组地址,不同集群用不同组避免冲突
vrrp_mcast_group4 224.0.0.18
}
vrrp_instance VI_1 {
state BACKUP # 所有节点都用 BACKUP,priority 决定主备
interface ens160
virtual_router_id 51 # 同一 VLAN 内每个集群唯一
priority 100 # 主节点 100,备节点 90
advert_int 1 # 通告间隔(秒),默认 1
# 抢占模式:主节点恢复后是否重新夺回 VIP
# 生产环境建议 nopreempt,避免频繁切换导致抖动
nopreempt
# 认证(明文,仅防止误入,不防攻击)
authentication {
auth_type PASS
auth_pass 8Zk9mP2x
}
# 双栈支持
# unicast_peer 用于跨三层网络(非组播环境)
# unicast_src_ip 192.168.1.10
# unicast_peer {
# 192.168.1.11
# 192.168.1.12
# }
virtual_ipaddress {
192.168.1.100/24 dev ens160 label ens160:vip
}
# 自定义跟踪脚本:检测 HAProxy 是否存活
track_script {
chk_haproxy
}
# 通知脚本:切换时触发告警
notify_master "/etc/keepalived/notify.sh MASTER"
notify_backup "/etc/keepalived/notify.sh BACKUP"
notify_fault "/etc/keepalived/notify.sh FAULT"
}
关键参数解读:
| 参数 | 说明 | 生产建议 |
|---|---|---|
advert_int |
VRRP 通告间隔 | 1s 常规,0.5s 高频(需低延迟网络) |
nopreempt |
禁止抢占 | 建议开启,避免主节点恢复时流量抖动 |
priority |
选举优先级 | 主节点 100-150,备节点 50-99 |
garp_master_delay |
切换后 ARP 通告延迟 | 默认 5s,建议 0-2s |
1.2 健康检查脚本:不止是「进程是否在跑」
最简单的健康检查只检测进程是否存在,这在 HAProxy 僵死(进程存活但无法转发)时毫无作用。生产级检查必须验证服务真正可用:
#!/bin/bash
# /etc/keepalived/check_haproxy.sh — 生产级检查脚本
# 1. 检查进程是否存在
if ! pgrep -x haproxy > /dev/null; then
systemctl stop keepalived # 立即放弃 VIP
exit 1
fi
# 2. 检查 HAProxy 统计端口是否响应(证明服务健康)
if ! timeout 3 bash -c 'echo "show info" | socat unix-connect:/run/haproxy/admin.sock stdio' > /dev/null 2>&1; then
systemctl stop keepalived
exit 1
fi
# 3. 检查后端至少有一个可用
BACKENDS_UP=$(echo "show stat" | socat unix-connect:/run/haproxy/admin.sock stdio |
awk -F, '$18 == "UP" {count++} END {print count+0}')
if [ "$BACKENDS_UP" -eq 0 ]; then
# 所有后端都挂了,记录日志但不主动降级
# 让 HAProxy 返回 503 比 VIP 切走更合理
logger -t keepalived "WARNING: All HAProxy backends are DOWN"
fi
exit 0
1.3 脑裂防御:双主节点的噩梦
脑裂(Split-Brain) 是高可用集群最危险的故障——两个节点同时认为自己是 MASTER,都持有 VIP,导致网络混乱、数据损坏。
防御策略(多层防御):
1. 网络层:组播隔离
- 确保 VRRP 组播(224.0.0.18)不被交换机过滤
- 同一 VLAN 内不同 vrrp_instance 使用不同 virtual_router_id
2. 应用层:仲裁机制
- 部署第三台节点作为仲裁者(priority 50)
- 或使用 etcd/Consul 做分布式锁,只有持有锁的节点才能成为 MASTER
3. 检测层:双向 ping 探测
- 在 track_script 中添加对端节点 ping 检测
- 如果 ping 不通但本地是 MASTER,触发告警
简易脑裂检测脚本:
#!/bin/bash
# /etc/keepalived/split-brain-check.sh
# 在备份节点上定期运行
PEER_IP="192.168.1.10" # 主节点 IP
MY_STATE=$(ip addr show ens160:vip 2>/dev/null | grep -c "inet ")
if [ "$MY_STATE" -gt 0 ]; then
# 本地持有 VIP
# 检查能否 ping 通主节点
if ping -c 2 -W 1 "$PEER_IP" > /dev/null 2>&1; then
# 我能 ping 通主节点,但我也持有 VIP — 可能是脑裂!
logger -t split-brain "CRITICAL: Possible split-brain! My VIP is up, but peer $PEER_IP is reachable"
# 发送告警,但不要自动降级(避免双方都降级导致服务全挂)
curl -s -X POST -H "Content-Type: application/json"
-d '{"text":"Keepalived 疑似脑裂!","priority":"critical"}'
http://alertmanager:9093/api/v1/alerts
fi
fi
二、HAProxy 生产级配置:从「能转发」到「高性能转发」
2.1 全局配置优化
# /etc/haproxy/haproxy.cfg — 生产级全局配置
global
# 最大并发连接数(受 ulimit -n 限制)
maxconn 100000
# 每个进程的并发连接
nbproc 1 # 1.8 及以上推荐单进程 + thread
nbthread 4 # 4 线程,匹配 CPU 核数
cpu-map 1 0-3 # 绑定线程到 CPU
# 性能调优
tune.bufsize 32768 # 缓冲区大小(默认 16384)
tune.maxrewrite 1024 # HTTP 重写缓冲区
tune.ssl.default-dh-param 2048
# 日志
log 127.0.0.1:514 local0 info
log-send-hostname
# 统计
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
# SSL 调优
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11
2.2 四层 vs 七层负载均衡选型
| 类型 | 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 四层 (mode tcp) | 传输层 | 性能高、协议无关 | 无法基于 HTTP 路由 | Redis、MySQL、TCP 长连接 |
| 七层 (mode http) | 应用层 | 内容路由、SSL 卸载 | CPU 开销大 | Web 服务、API 网关 |
四层示例:MySQL 负载均衡
frontend mysql_front
bind 192.168.1.100:3306
mode tcp
default_backend mysql_back
backend mysql_back
mode tcp
balance leastconn # 长连接用 leastconn
option tcpka # TCP keepalive
# 中间件健康检查:执行 MySQL ping
option mysql-check user haproxy_check
server db1 192.168.1.11:3306 check inter 5s fall 3 rise 2
server db2 192.168.1.12:3306 check inter 5s fall 3 rise 2
七层示例:基于路径的 API 路由
frontend api_gateway
bind 192.168.1.100:443 ssl crt /etc/ssl/haproxy/star.example.com.pem
mode http
option httplog
option forwardfor
# 请求头处理
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Real-IP %[src]
# 基于路径的路由
use_backend users_api if { path_beg /api/v1/users }
use_backend orders_api if { path_beg /api/v1/orders }
use_backend payments_api if { path_beg /api/v1/payments }
# 静态文件直连
use_backend static_files if { path_beg /static }
default_backend default_api
backend users_api
balance roundrobin
option httpchk GET /api/v1/users/health HTTP/1.1rnHost: users.example.com
http-check expect status 200
server users1 10.0.1.11:8080 check inter 3s fall 3 rise 2
server users2 10.0.1.12:8080 check inter 3s fall 3 rise 2
backend static_files
balance roundrobin
option httpchk HEAD /health
server static1 10.0.1.21:80 check
server static2 10.0.1.22:80 check
2.3 TLS 卸载:性能与安全兼得
证书链配置:
# 生成包含完整证书链的 PEM 文件
cat /etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
> /etc/ssl/haproxy/star.example.com.pem
chmod 600 /etc/ssl/haproxy/star.example.com.pem
TLS 会话复用与 OCSP Stapling:
frontend https_front
bind 192.168.1.100:443 ssl crt /etc/ssl/haproxy/star.example.com.pem
alpn h2,http/1.1
# OCSP Stapling 提升握手性能
option ssl-verify-client-optional
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
# 启用 HTTP/2
bind :443 v4v6 ssl crt /etc/ssl/haproxy/star.example.com.pem alpn h2
# SSL 会话缓存(提升性能)
# 在 global 段配置:
# tune.ssl.cachesize 50000
性能对比(实际压测数据):
| 场景 | 吞吐量 | 单连接延迟 |
|---|---|---|
| 无 TLS(纯 HTTP) | 95,000 req/s | 1.2ms |
| TLS 1.3 + 会话复用 | 72,000 req/s | 2.8ms |
| TLS 1.3 + OCSP Stapling | 74,000 req/s | 2.6ms |
| TLS 1.2(无复用) | 38,000 req/s | 5.4ms |
结论:TLS 1.3 + 会话复用将性能损失从 60% 降至 24%,OCSP Stapling 再提升 2-3%。
2.4 高级健康检查策略
多级健康检查:
backend web_servers
# 基础检查:TCP 端口存活
server web1 10.0.1.10:80 check inter 10s fall 3 rise 2
# 中级检查:HTTP 状态码
option httpchk GET /health HTTP/1.1rnHost: example.com
http-check expect status 200
# 高级检查:验证响应体内容
http-check expect string "OK"
# 慢启动:新加入节点逐步承接流量
server web2 10.0.1.11:80 check inter 10s fall 3 rise 2 slowstart 30s
优雅下线与蓝绿部署:
# 将节点置为维护模式(不中断现有连接)
echo "disable server web_servers/web1" | socat /run/haproxy/admin.sock stdio
# 等待现有连接耗尽
echo "shutdown sessions server web_servers/web1" | socat /run/haproxy/admin.sock stdio
# 更新后重新启用
echo "enable server web_servers/web1" | socat /run/haproxy/admin.sock stdio
2.5 速率限制与 DDoS 防护
frontend http_front
bind :80
mode http
# 基于 IP 的速率限制
stick-table type ip size 100k expire 30s store http_req_rate(10s)
# 单个 IP 10 秒内超过 100 次请求则封禁 60 秒
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
# 限制请求体大小(防止大 POST 攻击)
http-request reject if { content-length gt 10000000 }
# 限制 HTTP 方法
http-request deny if { method not in GET HEAD POST PUT DELETE OPTIONS }
# User-Agent 黑名单
http-request deny if { hdr(user-agent) -m sub -i "curl" "wget" "python-requests" }
三、Keepalived + HAProxy 联合部署完整方案
3.1 架构图
┌─────────────────────┐
│ Internet / 用户 │
└──────────┬──────────┘
│
┌──────────┴──────────┐
│ VIP: 192.168.1.100 │
└──────────┬──────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ LB-01 (MASTER) │ │ LB-02 (BACKUP) │ │ LB-03 (BACKUP) │
│ Keepalived │ │ Keepalived │ │ Keepalived │
│ HAProxy │ │ HAProxy │ │ HAProxy │
│ priority 100 │ │ priority 90 │ │ priority 80 │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└─────────────────────┼──────────────────────┘
│
┌──────────────┴──────────────┐
│ 内网交换机/路由器 │
└──────────────┬──────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌───────┴───────┐ ┌───────┴───────┐ ┌───────┴───────┐
│ Web Server 1 │ │ Web Server 2 │ │ Web Server 3 │
│ 10.0.1.10 │ │ 10.0.1.11 │ │ 10.0.1.12 │
└───────────────┘ └───────────────┘ └───────────────┘
3.2 一键部署脚本
#!/bin/bash
# deploy-haproxy-keepalived.sh — 三节点集群部署脚本
# 在 LB-01 上执行,会 SSH 到其他节点同步配置
set -euo pipefail
VIP="192.168.1.100"
INTERFACE="ens160"
PASSWORD="8Zk9mP2x"
NODES=("192.168.1.10" "192.168.1.11" "192.168.1.12")
PRIORITIES=(100 90 80)
STATES=("BACKUP" "BACKUP" "BACKUP") # 都用 BACKUP + priority 控制
# 1. 在所有节点安装 Keepalived + HAProxy
for node in "${NODES[@]}"; do
ssh "root@$node" "
apt-get update -qq
apt-get install -y -qq keepalived haproxy socat
systemctl enable keepalived haproxy
"
done
# 2. 生成 HAProxy 配置(集群共享)
cat > /tmp/haproxy.cfg << 'HAPROXY_CFG'
global
log /dev/log local0
maxconn 100000
nbthread 4
tune.ssl.default-dh-param 2048
stats socket /run/haproxy/admin.sock mode 660 level admin
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
defaults
log global
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout http-request 10000ms
option dontlognull
option redispatch
retries 3
frontend http_in
bind *:80
mode http
option httplog
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 10.0.1.10:80 check inter 3s fall 3 rise 2
server web2 10.0.1.11:80 check inter 3s fall 3 rise 2
server web3 10.0.1.12:80 check inter 3s fall 3 rise 2
listen stats
bind *:8404
mode http
stats enable
stats uri /stats
stats refresh 5s
stats auth admin:Ch4ng3M3!
HAPROXY_CFG
# 3. 分发配置
for node in "${NODES[@]}"; do
scp /tmp/haproxy.cfg "root@$node:/etc/haproxy/haproxy.cfg"
done
# 4. 生成各节点 Keepalived 配置
for i in "${!NODES[@]}"; do
node="${NODES[$i]}"
priority="${PRIORITIES[$i]}"
state="${STATES[$i]}"
cat > /tmp/keepalived.conf <<KEEPALIVED_CFG
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance VI_1 {
state $state
interface $INTERFACE
virtual_router_id 51
priority $priority
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass $PASSWORD
}
virtual_ipaddress {
$VIP/24 dev $INTERFACE label ${INTERFACE}:vip
}
track_script {
chk_haproxy
}
}
KEEPALIVED_CFG
scp /tmp/keepalived.conf "root@$node:/etc/keepalived/keepalived.conf"
scp /etc/keepalived/check_haproxy.sh "root@$node:/etc/keepalived/check_haproxy.sh"
done
# 5. 重启所有节点服务
for node in "${NODES[@]}"; do
ssh "root@$node" "
systemctl restart keepalived haproxy
systemctl status keepalived haproxy --no-pager | head -10
"
done
echo "===== 集群部署完成 ====="
echo "VIP: $VIP"
echo "HAProxy Stats: http://$VIP:8404/stats"
echo "Test: curl -I http://$VIP"
四、监控体系搭建
4.1 HAProxy 指标采集(Prometheus 格式)
HAProxy 2.0+ 原生支持 Prometheus 指标暴露:
frontend metrics
bind *:9101
mode http
http-request use-service prometheus-exporter if { path /metrics }
default_backend dummy
配合 Prometheus 告警规则:
# haproxy_alerts.yml
groups:
- name: haproxy
rules:
- alert: HAProxyBackendDown
expr: haproxy_server_status{status="DOWN"} > 0
for: 30s
labels:
severity: critical
annotations:
summary: "HAProxy 后端 {{ $labels.server }} 宕机"
- alert: HAProxySessionsHigh
expr: haproxy_current_sessions / haproxy_max_sessions > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "HAProxy 会话数超过 80% 上限"
- alert: KeepalivedMasterCount
expr: count(keeplalived_up{state="MASTER"}) != 1
for: 10s
labels:
severity: critical
annotations:
summary: "Keepalived MASTER 数量异常(可能脑裂)"
4.2 日志分析:快速定位问题
# HAProxy 日志格式
# 格式: %ci/%cp %si/%sp %t %Tq %Tw %Tc %Tr %Tt %ST %B %rc %sq %bq %ss %ts %ac %fc %bc %sc %rc %sq %bq
# 实时监控 5xx 错误
tail -f /var/log/haproxy.log | grep " 50[0-9] "
# 统计各后端响应时间
grep "$(date +%Y-%m-%d)" /var/log/haproxy.log | awk '{print $12}' |
awk -F/ '{print $NF}' | awk '{count++; sum+=$1} END {print "Avg:", sum/count, "ms, Count:", count}'
# 找出最慢的请求
awk '{print $12, $0}' /var/log/haproxy.log | sort -t/ -k3 -rn | head -10
五、故障排查 SOP
场景 1:VIP 无法访问
┌─ 检查 Keepalived 进程 ──────┐
│ systemctl status keepalived │
└──────────┬──────────────────┘
▼
┌─ 检查 VIP 是否在本地 ───────┐
│ ip addr show ens160:vip │
└──────────┬──────────────────┘
▼
┌─ 检查 VRRP 组播 ────────────┐
│ tcpdump -i ens160 vrrp │ ← 是否收到对端通告?
└──────────┬──────────────────┘
▼
┌─ 检查健康检查脚本 ──────────┐
│ /etc/keepalived/check_haproxy.sh │ ← 手动执行看返回值
└──────────┬──────────────────┘
▼
┌─ 检查防火墙 ────────────────┐
│ ufw status │
│ iptables -L -n | grep VRRP │ ← VRRP 协议(112)是否放行?
└──────────────────────────────┘
场景 2:HAProxy 健康检查误判
# 模拟健康检查请求
curl -v http://10.0.1.10/health
# 查看 HAProxy 统计
echo "show stat" | socat /run/haproxy/admin.sock stdio |
awk -F, '{print $1, $2, $18, $19, $58, $59}'
# 重置后端状态
echo "set server web_servers/web1 state ready" | socat /run/haproxy/admin.sock stdio
场景 3:会话保持失效
# 检查 stick-table 内容
echo "show table http_front" | socat /run/haproxy/admin.sock stdio
# 检查 cookie 注入
curl -v http://192.168.1.100/ 2>&1 | grep -i "set-cookie"
六、生产环境 Checklist
| 项目 | 检查要点 | 是否合规 |
|---|---|---|
| 网络 | VRRP 组播(224.0.0.18) 交换机放行 | □ |
| 网络 | 所有节点时间同步(NTP) | □ |
| 安全 | HAProxy 统计端口绑定内网且密码保护 | □ |
| 安全 | Keepalived 认证密码非默认值 | □ |
| 监控 | Prometheus 拉取 HAProxy 指标 | □ |
| 告警 | 脑裂检测脚本已部署 | □ |
| 告警 | 后端宕机告警通知责任人 | □ |
| 备份 | HAProxy 配置纳入版本控制 | □ |
| 备份 | Keepalived 配置纳入版本控制 | □ |
| 测试 | 手动切换主备验证 VIP 漂移 | □ |
| 测试 | 逐一下线后端验证健康检查 | □ |
| 文档 | 切换流程文档存放在团队 Wiki | □ |
总结
| 层面 | 首发要点 | 本文进阶 |
|---|---|---|
| 高可用 | Keepalived 基础安装配置 | VRRP 参数调优、脑裂防御、多层健康检查 |
| 负载均衡 | HAProxy 四层/七层基础转发 | TLS 卸载优化、HTTP/2、速率限制、内容路由 |
| 运维 | 手动启动服务 | 一键部署脚本、Prometheus 监控、告警规则 |
| 故障排查 | 基本日志查看 | 完整 SOP、tcpdump 抓包、socat 实时诊断 |
高可用集群的灵魂不是”能切换”,而是”切换时用户无感知”。在本文的进阶配置之上,你还可以进一步探索:
- LVS + Keepalived:四层性能极致(Linux 内核级负载均衡)
- Envoy + Consul:服务网格时代的流量管理
- 多活数据中心:跨地域流量调度与故障转移
下一篇预告:第 27 天——Ubuntu 自动部署:PXE 网络安装、Preseed 与 Cloud-init 自动化,全面实现系统安装的无人值守!















暂无评论内容