Keepalived + HAProxy 高可用架构
大约 16 分钟约 4851 字
Keepalived + HAProxy 高可用架构
简介
Keepalived + HAProxy 是业界广泛采用的开源高可用负载均衡方案。Keepalived 基于 VRRP(Virtual Router Redundancy Protocol)协议实现 IP 漂移和故障自动切换,HAProxy 则提供高性能的四层/七层负载均衡能力。两者结合可以构建出生产级别的高可用架构,实现服务的零中断交付。
在传统的单点负载均衡架构中,一旦 Nginx 或 HAProxy 所在节点宕机,整个后端服务将不可用。通过引入 Keepalived,可以在主节点故障时自动将虚拟 IP 漂移到备节点,从而保证服务的持续可用性。该方案广泛应用于 Web 服务、API 网关、数据库代理等核心业务场景。
核心特点
| 特点 | 说明 |
|---|---|
| 高可用性 | VRRP 协议实现秒级故障切换 |
| 负载均衡 | 支持四层(TCP)和七层(HTTP)负载均衡 |
| 健康检查 | 多种检测机制,自动剔除故障后端 |
| 会话保持 | 支持 Cookie、IP Hash 等会话保持策略 |
| SSL 卸载 | HAProxy 可在前端终止 SSL,减轻后端压力 |
| 双主架构 | 两个节点互为主备,充分利用服务器资源 |
| 统计面板 | 内置实时监控面板,便于运维观察 |
| 高性能 | 单机可达数十万并发连接 |
环境准备
系统要求
| 项目 | 最低要求 | 推荐配置 |
|---|---|---|
| 操作系统 | CentOS 7+ | CentOS 7.9 |
| 内存 | 1GB | 4GB+ |
| CPU | 2 核 | 4 核+ |
| 网络 | 千兆网卡 | 万兆网卡 |
节点规划
| 节点 | IP 地址 | 角色 |
|---|---|---|
| node1 | 192.168.1.10 | 主节点(MASTER) |
| node2 | 192.168.1.11 | 备节点(BACKUP) |
| VIP-1 | 192.168.1.100 | 对外虚拟 IP |
| VIP-2 | 192.168.1.101 | 双主模式第二 VIP |
端口规划
| 端口 | 说明 |
|---|---|
| 80 | HTTP 负载均衡 |
| 443 | HTTPS 负载均衡 |
| 3306 | MySQL 代理(可选) |
| 6379 | Redis 代理(可选) |
| 1080 | HAProxy 统计面板 |
| 8443 | HAProxy 统计面板(安全) |
Keepalived 原理详解
VRRP 协议原理
VRRP(虚拟路由冗余协议)是一种容错协议,它将多台路由器组成一个虚拟路由器组,对外提供一个虚拟 IP 地址。组内的路由器通过优先级选举出 Master 节点,拥有虚拟 IP 的 Master 节点负责转发数据包。当 Master 故障时,Backup 节点自动接管虚拟 IP。
+------------------+
| 客户端请求 |
+--------+---------+
|
VIP: 192.168.1.100
|
+------------+------------+
| |
+------+------+ +------+------+
| MASTER | | BACKUP |
| 优先级: 100 | VRRP | 优先级: 90 |
| 192.168.1.10| 组播通信 | 192.168.1.11|
+------+------+ +------+------+
| |
+------+------+ +------+------+
| 后端服务器 | | 后端服务器 |
+-------------+ +-------------+VRRP 选举机制
1. 所有节点启动后声明自己的优先级
2. 优先级最高的节点成为 MASTER
3. MASTER 定期发送 VRRP 通告(组播地址 224.0.0.18)
4. BACKUP 节点监听通告,若在指定时间内未收到,则发起选举
5. 新选举的 MASTER 绑定虚拟 IP,发送免费 ARP 更新 MAC 表安装部署
安装 Keepalived
# 两台节点均执行
yum install -y keepalived
# 查看版本
keepalived -v
# 备份默认配置
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak安装 HAProxy
# 两台节点均执行
yum install -y haproxy
# 查看版本
haproxy -v
# 备份默认配置
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
# 创建配置目录
mkdir -p /etc/haproxy/errors
mkdir -p /var/lib/haproxyKeepalived 配置
主节点配置(node1)
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id LVS_NODE1
script_user root
enable_script_security
}
# HAProxy 健康检查脚本
vrrp_script check_haproxy {
script "/etc/keepalived/scripts/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass MySecret123
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip1
}
track_script {
check_haproxy
}
track_interface {
eth0
}
notify_master "/etc/keepalived/scripts/notify.sh master"
notify_backup "/etc/keepalived/scripts/notify.sh backup"
notify_fault "/etc/keepalived/scripts/notify.sh fault"
}
EOF备节点配置(node2)
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id LVS_NODE2
script_user root
enable_script_security
}
vrrp_script check_haproxy {
script "/etc/keepalived/scripts/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass MySecret123
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip1
}
track_script {
check_haproxy
}
track_interface {
eth0
}
notify_master "/etc/keepalived/scripts/notify.sh master"
notify_backup "/etc/keepalived/scripts/notify.sh backup"
notify_fault "/etc/keepalived/scripts/notify.sh fault"
}
EOF健康检查脚本
# 创建脚本目录
mkdir -p /etc/keepalived/scripts
# HAProxy 检查脚本
cat > /etc/keepalived/scripts/check_haproxy.sh << 'SCRIPT'
#!/bin/bash
# 检查 HAProxy 进程是否存活
if ! pidof haproxy > /dev/null 2>&1; then
echo "HAProxy process not found" >> /var/log/keepalived-check.log
exit 1
fi
# 检查 HAProxy 是否正常响应
HAPROXY_STATS_URL="http://127.0.0.1:1080/stats"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 ${HAPROXY_STATS_URL} 2>/dev/null)
if [ "${HTTP_CODE}" != "200" ] && [ "${HTTP_CODE}" != "401" ]; then
echo "HAProxy stats check failed: HTTP ${HTTP_CODE}" >> /var/log/keepalived-check.log
exit 1
fi
exit 0
SCRIPT
chmod +x /etc/keepalived/scripts/check_haproxy.sh状态切换通知脚本
cat > /etc/keepalived/scripts/notify.sh << 'SCRIPT'
#!/bin/bash
STATE=$1
STATE_FILE="/var/run/keepalived.state"
LOG_FILE="/var/log/keepalived-notify.log"
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
echo "${TIMESTAMP} - State change to: ${STATE}" >> ${LOG_FILE}
echo "${STATE}" > ${STATE_FILE}
case ${STATE} in
master)
# 切换为主节点时的操作
logger -t keepalived "Transition to MASTER state"
# 可选:发送告警通知
# curl -X POST "https://webhook.example.com/alert" \
# -d "message=Keepalived切换为MASTER节点"
;;
backup)
# 切换为备节点时的操作
logger -t keepalived "Transition to BACKUP state"
;;
fault)
# 进入故障状态时的操作
logger -t keepalived "Transition to FAULT state"
;;
esac
SCRIPT
chmod +x /etc/keepalived/scripts/notify.shHAProxy 配置
基础负载均衡配置
cat > /etc/haproxy/haproxy.cfg << 'EOF'
#-------------------- 全局配置 --------------------
global
log 127.0.0.1 local2 info
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 65535
user haproxy
group haproxy
daemon
# 性能优化
stats socket /var/lib/haproxy/stats mode 600 level admin
stats timeout 2m
# SSL 优化
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2
#-------------------- 默认配置 --------------------
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 5s
maxconn 30000
#-------------------- 统计面板 --------------------
listen stats
bind *:1080
mode http
stats enable
stats uri /stats
stats realm HAProxy\ Statistics
stats auth admin:StrongPassword123
stats refresh 30s
stats show-legends
stats show-node
stats admin if LOCALHOST
#-------------------- HTTP 负载均衡 --------------------
frontend http_front
bind *:80
mode http
# 记录客户端真实 IP
option forwardfor
http-request set-header X-Forwarded-Proto http
# ACL 规则
acl is_api path_beg /api/
acl is_static path_end .css .js .png .jpg .gif .ico .svg .woff2
# 路由分发
use_backend api_servers if is_api
use_backend static_servers if is_static
default_backend web_servers
frontend https_front
bind *:443 ssl crt /etc/haproxy/ssl/server.pem
mode http
option forwardfor
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Port 443
# HTTP/2 支持
# bind *:443 ssl crt /etc/haproxy/ssl/server.pem alpn h2,http/1.1
default_backend web_servers
#-------------------- 后端服务器 --------------------
backend web_servers
mode http
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ web.example.com
server web1 192.168.1.20:8080 check inter 3s fall 3 rise 2 weight 100
server web2 192.168.1.21:8080 check inter 3s fall 3 rise 2 weight 100
server web3 192.168.1.22:8080 check inter 3s fall 3 rise 2 weight 50 backup
backend api_servers
mode http
balance leastconn
# 会话保持:基于 Cookie
cookie SERVERID insert indirect nocache maxlife 12h
option httpchk GET /api/health HTTP/1.1\r\nHost:\ api.example.com
server api1 192.168.1.30:8080 cookie s1 check inter 3s fall 3 rise 2
server api2 192.168.1.31:8080 cookie s2 check inter 3s fall 3 rise 2
server api3 192.168.1.32:8080 cookie s3 check inter 3s fall 3 rise 2
backend static_servers
mode http
balance roundrobin
# 静态资源缓存头
http-response set-header Cache-Control "public, max-age=86400" if is_static
server static1 192.168.1.40:80 check inter 5s fall 3 rise 2
server static2 192.168.1.41:80 check inter 5s fall 3 rise 2
EOFTCP 四层负载均衡配置
# 追加 TCP 代理配置
cat >> /etc/haproxy/haproxy.cfg << 'EOF'
#-------------------- MySQL 代理 --------------------
listen mysql_proxy
bind *:3306
mode tcp
balance leastconn
option mysql-check user haproxy_check
server mysql1 192.168.1.50:3306 check inter 5s fall 3 rise 2 weight 100
server mysql2 192.168.1.51:3306 check inter 5s fall 3 rise 2 weight 100
#-------------------- Redis 代理 --------------------
listen redis_proxy
bind *:6379
mode tcp
balance roundrobin
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
server redis1 192.168.1.60:6379 check inter 3s fall 3 rise 2
server redis2 192.168.1.61:6379 check inter 3s fall 3 rise 2
#-------------------- RabbitMQ 代理 --------------------
listen rabbitmq_proxy
bind *:5672
mode tcp
balance roundrobin
server rmq1 192.168.1.70:5672 check inter 5s fall 3 rise 2
server rmq2 192.168.1.71:5672 check inter 5s fall 3 rise 2
EOF双主架构配置
双主模式下,两个节点互为主备,各持有一个 VIP,通过 DNS 轮询将流量分发到两个 VIP,充分利用服务器资源。
节点 1 双主配置
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id LVS_NODE1
script_user root
enable_script_security
}
vrrp_script check_haproxy {
script "/etc/keepalived/scripts/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
# 实例 1:node1 为主
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass VIP1Secret
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip1
}
track_script {
check_haproxy
}
}
# 实例 2:node1 为备
vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 52
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass VIP2Secret
}
virtual_ipaddress {
192.168.1.101/24 dev eth0 label eth0:vip2
}
track_script {
check_haproxy
}
}
EOF节点 2 双主配置
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id LVS_NODE2
script_user root
enable_script_security
}
vrrp_script check_haproxy {
script "/etc/keepalived/scripts/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
# 实例 1:node2 为备
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass VIP1Secret
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip1
}
track_script {
check_haproxy
}
}
# 实例 2:node2 为主
vrrp_instance VI_2 {
state MASTER
interface eth0
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass VIP2Secret
}
virtual_ipaddress {
192.168.1.101/24 dev eth0 label eth0:vip2
}
track_script {
check_haproxy
}
}
EOFSSL 终端配置
证书准备
# 创建 SSL 目录
mkdir -p /etc/haproxy/ssl
# 合并证书链(证书 + 私钥)
cat /path/to/fullchain.pem /path/to/privkey.pem > /etc/haproxy/ssl/server.pem
# 设置安全权限
chmod 600 /etc/haproxy/ssl/server.pem
chown haproxy:haproxy /etc/haproxy/ssl/server.pem
# 证书更新脚本
cat > /etc/haproxy/scripts/reload_ssl.sh << 'SCRIPT'
#!/bin/bash
CERT_DIR="/etc/haproxy/ssl"
CERT_FILE="${CERT_DIR}/server.pem"
# 合并新证书
cat /path/to/new/fullchain.pem /path/to/new/privkey.pem > ${CERT_FILE}.tmp
mv ${CERT_FILE}.tmp ${CERT_FILE}
chmod 600 ${CERT_FILE}
# 优雅重载 HAProxy
haproxy -f /etc/haproxy/haproxy.cfg -sf $(cat /var/run/haproxy.pid)
echo "SSL certificate reloaded at $(date)" >> /var/log/haproxy-ssl.log
SCRIPT
chmod +x /etc/haproxy/scripts/reload_ssl.shSSL 前端配置
# HTTPS 前端与 HTTP 自动跳转
frontend http_https
# HTTP 绑定,自动跳转到 HTTPS
bind *:80
redirect scheme https code 301 if !{ ssl_fc }
# HTTPS 绑定
bind *:443 ssl crt /etc/haproxy/ssl/server.pem alpn h2,http/1.1
# HSTS 头
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# 安全头
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header X-XSS-Protection "1; mode=block"
default_backend web_servers会话保持策略
基于 Cookie 的会话保持
backend web_sticky
mode http
balance roundrobin
# 方式一:insert 模式(HAProxy 插入 Cookie)
cookie SERVERID insert indirect nocache maxlife 12h
server web1 192.168.1.20:8080 cookie s1 check
server web2 192.168.1.21:8080 cookie s2 check
# 方式二:prefix 模式(在现有 Cookie 前添加标识)
# cookie JSESSIONID prefix nocache
# 方式三:rewrite 模式(重写现有 Cookie)
# cookie JSESSIONID rewrite nocache基于源 IP 的会话保持
backend web_source
mode http
balance source
hash-type consistent
server web1 192.168.1.20:8080 check
server web2 192.168.1.21:8080 check基于 Stick Table 的会话保持
backend web_sticktable
mode http
balance roundrobin
# 基于 Cookie 的粘性表
stick-table type string len 64 size 100k expire 12h
stick on cookie(JSESSIONID)
server web1 192.168.1.20:8080 check
server web2 192.168.1.21:8080 check监控集成
Prometheus 指标导出
# 安装 HAProxy Exporter
yum install -y haproxy_exporter
# 或使用 Docker 运行
docker run -d --name haproxy_exporter \
-p 9101:9101 \
--restart=always \
prom/haproxy-exporter:v0.12.0 \
--haproxy.scrape-uri="http://admin:StrongPassword123@192.168.1.100:1080/stats;csv"Prometheus 采集配置
# prometheus.yml
scrape_configs:
- job_name: 'haproxy'
static_configs:
- targets: ['192.168.1.10:9101', '192.168.1.11:9101']
labels:
service: 'haproxy'
metric_relabel_configs:
- source_labels: [__name__]
regex: 'haproxy_server.*'
action: keepGrafana 监控面板关键指标
# 核心监控指标
haproxy_server_current_sessions # 当前会话数
haproxy_server_current_queue # 当前队列请求数
haproxy_server_http_responses_total # HTTP 响应码统计
haproxy_server_bytes_in_total # 入站流量
haproxy_server_bytes_out_total # 出站流量
haproxy_server_response_time_average # 平均响应时间
haproxy_server_check_status # 健康检查状态
haproxy_frontend_status # 前端状态
# 告警规则示例
- alert: HAProxyServerDown
expr: haproxy_server_status{state="DOWN"} > 0
for: 1m
labels:
severity: critical
annotations:
summary: "HAProxy backend server is down"启动与验证
启动服务
# 两台节点均执行
# 检查配置语法
haproxy -c -f /etc/haproxy/haproxy.cfg
keepalived -t -f /etc/keepalived/keepalived.conf
# 启动 HAProxy
systemctl start haproxy
systemctl enable haproxy
# 启动 Keepalived
systemctl start keepalived
systemctl enable keepalived
# 查看服务状态
systemctl status haproxy
systemctl status keepalived验证 VIP
# 在主节点查看 VIP
ip addr show eth0 | grep vip
# 预期输出
# inet 192.168.1.100/24 scope global secondary eth0:vip1
# 查看组播通告
tcpdump -i eth0 vrrp -nn
# 查看 Keepalived 状态
cat /var/run/keepalived.state验证负载均衡
# 通过 VIP 访问测试
curl -s http://192.168.1.100/ | head
# 多次请求观察轮询
for i in $(seq 1 10); do
curl -s http://192.168.1.100/ | grep "Server"
sleep 0.5
done
# 查看统计面板
curl -s -u admin:StrongPassword123 "http://192.168.1.100:1080/stats;csv" | head -20验证故障切换
# 在主节点停止 HAProxy 模拟故障
systemctl stop haproxy
# 在备节点观察 VIP 是否漂移
ip addr show eth0 | grep vip
# 检查切换日志
tail -20 /var/log/keepalived-notify.log
# 恢复主节点
systemctl start haproxy
# 观察 VIP 是否回迁
ip addr show eth0 | grep vip性能优化
内核参数优化
cat >> /etc/sysctl.conf << 'EOF'
# 网络连接优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 65535
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
# 文件描述符优化
fs.file-max = 1048576
# TCP 缓冲区优化
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# Keepalive 优化
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
EOF
sysctl -p文件描述符限制
cat >> /etc/security/limits.conf << 'EOF'
haproxy soft nofile 655350
haproxy hard nofile 655350
root soft nofile 655350
root hard nofile 655350
EOFHAProxy 性能调优
global
# 多进程/多线程
nbproc 2
nbthread 4
cpu-map auto:1/1-4 0-3
# 连接优化
maxconn 200000
tune.maxaccept 128
# 缓冲区优化
tune.bufsize 32768
tune.lua.maxmem 0生产部署检查清单
| 检查项 | 说明 | 状态 |
|---|---|---|
| 配置语法检查 | haproxy -c / keepalived -t 验证通过 | [ ] |
| 防火墙规则 | VRRP 协议(112)、业务端口已放行 | [ ] |
| SELinux 配置 | 确保 HAProxy/Keepalived 正常运行 | [ ] |
| 日志配置 | rsyslog 已配置 HAProxy 日志接收 | [ ] |
| 健康检查 | 所有后端服务器健康检查通过 | [ ] |
| 故障切换测试 | VIP 漂移和回迁正常 | [ ] |
| SSL 证书 | 证书有效期、链完整性已验证 | [ ] |
| 监控接入 | Prometheus/Grafana 监控已配置 | [ ] |
| 告警规则 | 服务不可用告警已配置 | [ ] |
| 备份策略 | 配置文件定期备份已启用 | [ ] |
| 安全加固 | 统计面板认证、密码强度验证 | [ ] |
| 资源限制 | 文件描述符、连接数限制已调整 | [ ] |
排错指南
VIP 无法绑定
# 检查 Keepalived 状态
systemctl status keepalived
journalctl -u keepalived --since "10 minutes ago"
# 检查 VRRP 组播
tcpdump -i eth0 proto 112 -nn
# 检查防火墙是否阻止 VRRP
firewall-cmd --list-all
# 需要放行 VRRP 协议
firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
firewall-cmd --reload
# 检查 SELinux
getenforce
# 若为 Enforcing 模式,临时关闭测试
setenforce 0HAProxy 启动失败
# 检查配置语法
haproxy -c -f /etc/haproxy/haproxy.cfg
# 查看详细错误日志
journalctl -u haproxy --since "10 minutes ago"
# 常见错误排查
# 1. 端口被占用
ss -tlnp | grep :80
ss -tlnp | grep :443
# 2. SSL 证书文件权限
ls -la /etc/haproxy/ssl/
# 确保证书文件可读
# 3. 后端服务器不可达
curl -s http://192.168.1.20:8080/health脑裂问题排查
# 在两个节点同时检查 VIP
# node1
ip addr show eth0 | grep 192.168.1.100
# node2
ip addr show eth0 | grep 192.168.1.100
# 如果两个节点都持有 VIP,说明出现脑裂
# 原因分析:
# 1. 网络分区导致组播不通
# 2. 防火墙阻断 VRRP 通告
# 3. 网络设备禁用了组播
# 解决方案:
# 1. 检查网络连通性
ping -c 3 192.168.1.11
# 2. 检查组播路由
ip maddr show eth0
# 3. 使用单播模式替代组播(推荐)
# 在 keepalived.conf 中添加:
# unicast_src_ip 192.168.1.10
# unicast_peer {
# 192.168.1.11
# }后端健康检查失败
# 查看后端状态
echo "show stat" | socat stdio /var/lib/haproxy/stats | cut -d',' -f1,2,18,19
# 手动测试健康检查
curl -v -H "Host: web.example.com" http://192.168.1.20:8080/health
# 查看详细检查日志
echo "show stat" | socat stdio /var/lib/haproxy/stats
# 强制调整后端状态
echo "set server web_servers/web1 state ready" | socat stdio /var/lib/haproxy/stats
echo "set server web_servers/web1 state drain" | socat stdio /var/lib/haproxy/stats优点
- 开源免费:Keepalived 和 HAProxy 均为开源软件,无授权费用
- 成熟稳定:两者均有超过 15 年的生产验证历史
- 高性能:HAProxy 单机可达数十万 QPS,延迟极低
- 灵活配置:支持四层/七层、多种调度算法和会话保持方式
- 故障自愈:VRRP 自动故障切换,无需人工干预
- 运维友好:内置统计面板,支持热重载配置
缺点
- 无自动扩缩容:后端服务器需要手动管理,不像 K8s Service 那样自动弹性伸缩
- 配置复杂:四层/七层混合配置时,参数较多
- 单区域限制:VIP 仅在同一子网有效,跨可用区需要额外方案
- SSL 证书管理:证书更新需要手动或额外脚本处理
- 不具备服务发现:后端服务器变化需要修改配置并重载
性能注意事项
- 连接数规划:根据 maxconn 和后端服务器数量计算总并发能力
- 超时调优:根据业务特点调整 connect/client/server timeout
- 健康检查频率:检查间隔过短增加后端压力,过长导致故障发现延迟
- 日志级别:生产环境建议使用 notice 级别,避免 info 日志量过大
- 缓冲区大小:大文件上传场景需要调大 tune.bufsize
- CPU 绑定:多进程模式下绑定 CPU 核心,减少上下文切换
总结
Keepalived + HAProxy 是一套经过大规模生产验证的高可用负载均衡方案。Keepalived 负责 VIP 管理和自动故障切换,HAProxy 负责流量分发和健康检查,两者配合构成了可靠的服务入口层。在云原生时代,虽然 Kubernetes 等编排工具提供了内置的服务发现和负载均衡,但在传统架构和混合云场景中,Keepalived + HAProxy 仍然是最经济实用的选择。
关键知识点
- VRRP 协议的选举机制和优先级策略
- Keepalived 的 track_script 实现应用级健康检查
- HAProxy 的 frontend/backend/frontend 分层模型
- 四层(TCP)和七层(HTTP)负载均衡的区别与选择
- Cookie、Source IP、Stick Table 三种会话保持策略
- 双主互备架构的设计思路和 DNS 轮询配合
- HAProxy Runtime API 的动态管理能力
- SSL 终端卸载和安全头配置
常见误区
- 认为 Keepalived 只能做 IP 漂移:实际上 Keepalived 还支持 LVS DR/NAT 模式的负载均衡
- 忽略 VRRP 组播的防火墙规则:防火墙阻断组播会导致脑裂
- 健康检查脚本不够严谨:简单的进程检查无法发现应用假死,需结合端口和 HTTP 检查
- VIP 回迁配置不当:nopreempt 可以防止主节点恢复后 VIP 反复切换
- HAProxy 配置热重载导致连接断开:使用 -sf 参数传递旧进程 PID 可实现优雅重载
- 双主架构忽略负载均衡一致性:两个 HAProxy 配置必须完全一致
进阶路线
- 学习 LVS:了解 LVS 的 DR、NAT、TUN 三种模式及 Keepalived 与 LVS 的集成
- DPDK 加速:了解用户态网络协议栈如何提升 HAProxy 性能
- 服务网格:对比 Istio/Envoy 等云原生方案与传统负载均衡的差异
- 全局负载均衡:学习 GSLB、DNS 负载均衡和跨区域高可用设计
- 自动化运维:结合 Ansible/Terraform 实现 HAProxy 配置的自动化管理
- 可观测性:集成 OpenTelemetry 实现全链路追踪
适用场景
- 中大型 Web 应用的 HTTP/HTTPS 负载均衡
- 数据库集群的读写分离代理(MySQL、PostgreSQL)
- 缓存集群的负载均衡(Redis Cluster 代理)
- 消息队列集群的连接代理
- 微服务架构中的 API 网关前置负载均衡
- 传统架构到云原生架构的过渡方案
落地建议
- 先在测试环境完整演练:包括安装、配置、故障切换、恢复全流程
- 统一配置管理:使用 Git 管理配置文件,确保两个节点配置一致
- 监控先行:在上线前完成 Prometheus + Grafana 监控和告警配置
- 制定应急预案:明确故障切换的 SOP,包括手动切换步骤
- 定期演练:每季度进行一次故障切换演练,验证方案有效性
- 日志集中化:将 HAProxy 和 Keepalived 日志统一收集到 ELK
排错清单
| 现象 | 可能原因 | 排查命令 |
|---|---|---|
| VIP 不存在 | Keepalived 未启动 | systemctl status keepalived |
| 两台都有 VIP | 脑裂,组播不通 | tcpdump -i eth0 vrrp |
| 后端全部 DOWN | 健康检查路径错误 | curl http://backend:port/health |
| 503 Service Unavailable | 后端全部不可用 | 查看统计面板 |
| 504 Gateway Timeout | 后端响应超时 | 调整 timeout server |
| 连接数不均衡 | 调度算法不合适 | 切换 balance 算法 |
| SSL 握手失败 | 证书链不完整 | openssl s_client -connect |
| 配置重载失败 | 语法错误 | haproxy -c -f haproxy.cfg |
复盘问题
- Keepalived 的 VRRP 通告间隔设置为多少合适?对故障切换速度有什么影响?
- HAProxy 的 roundrobin 和 leastconn 算法分别适用于什么场景?
- 双主架构中,如何确保两个节点的 HAProxy 配置始终一致?
- 如何实现 HAProxy 的无损热重载?解释 -sf 参数的作用?
- SSL 终端放在 HAProxy 层 vs 放在后端服务层,各自的优劣是什么?
- 如何设计一个跨可用区的高可用负载均衡方案?
