在互联网服务架构中,单点故障是最大的隐患。一台VPS无论配置多高,一旦宕机就意味着服务中断。对于生产环境来说,高可用不再是可选项,而是必须项。本文将手把手带你使用 HAProxy、Keepalived 和 Consul 搭建一套生产级高可用负载均衡集群,实现自动故障转移、健康检查和服务发现,让你的服务真正达到 99.99% 的可用性目标。
一、架构设计:为什么选择 HAProxy + Keepalived + Consul
在众多负载均衡方案中,为什么我们选择这个组合?让我们逐一分析每个组件的定位和协同方式:
- HAProxy — 专业级四层/七层负载均衡器,支持丰富的调度算法、健康检查、SSL终端,单机可处理百万级并发连接
- Keepalived — 基于 VRRP 协议的 VIP 漂移方案,确保入口 IP 永远可用,主节点故障时备用节点秒级接管
- Consul — 分布式服务发现与配置中心,后端服务注册后 HAProxy 自动感知,无需手动修改配置和重载
三者的协同关系如下:
| 组件 | 职责 | 故障场景 |
|---|---|---|
| Keepalived | VIP 漂移,保证入口 IP 可用 | 主节点宕机 → 备节点接管 VIP |
| HAProxy | 请求分发,健康检查 | 后端节点挂 → 自动剔除,恢复后自动加回 |
| Consul | 服务注册与发现 | 新节点上线/下线 → HAProxy 配置自动更新 |
整体架构是:客户端请求到达 VIP → Keepalived 所在的主 HAProxy 接收请求 → HAProxy 通过 Consul 动态发现后端服务 → 将请求分发到健康的后端节点。如果主 HAProxy 节点故障,Keepalived 将 VIP 迁移到备节点,整个过程对客户端完全透明。
二、环境准备与拓扑规划
2.1 服务器规划
我们需要至少 4 台 VPS 来搭建完整的高可用集群。以下是一个最小化生产拓扑:
| 角色 | 数量 | 最低配置 | 推荐系统 |
|---|---|---|---|
| 负载均衡节点(LB) | 2 | 1C/1G | Ubuntu 22.04 / Debian 12 |
| Consul Server | 3 | 1C/1G | Ubuntu 22.04 / Debian 12 |
| 后端应用节点 | 2+ | 2C/4G | 按应用需求 |
如果资源有限,Consul Server 可以与 LB 节点混合部署(测试环境推荐),但生产环境建议独立部署以保证稳定性。
2.2 网络规划
各节点的 IP 分配示例(使用内网 IP 段 10.0.0.0/24):
1
2
3
4
5
6 VIP: 10.0.0.100
LB-Master: 10.0.0.11
LB-Backup: 10.0.0.12
Consul-Server: 10.0.0.21, 10.0.0.22, 10.0.0.23
Backend-1: 10.0.0.31
Backend-2: 10.0.0.32
注意:Keepalived 的 VRRP 需要二层广播可达,LB-Master 和 LB-Backup 必须在同一子网内。如果你的 VPS 提供商支持浮动 IP(如 DigitalOcean、Hetzner),优先使用浮动 IP 替代 VRRP。
三、Consul 集群部署与配置
3.1 安装 Consul
在所有 Consul Server 节点上执行:
1
2
3
4 # 下载并安装 Consul
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install -y consul
3.2 配置 Consul Server
在每台 Consul Server 上创建配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 sudo mkdir -p /etc/consul.d
cat > /etc/consul.d/server.json << 'EOF'
{
"node_name": "consul-server-1",
"server": true,
"bootstrap_expect": 3,
"datacenter": "dc1",
"data_dir": "/opt/consul",
"bind_addr": "10.0.0.21",
"client_addr": "0.0.0.0",
"retry_join": ["10.0.0.21", "10.0.0.22", "10.0.0.23"],
"ui_config": {
"enabled": true
},
"performance": {
"raft_multiplier": 1
},
"enable_local_script_checks": true
}
EOF
sudo mkdir -p /opt/consul
sudo chown -R consul:consul /opt/consul
sudo systemctl enable --now consul
注意每台 Server 的
1 | node_name |
和
1 | bind_addr |
要改成对应的值。集群启动后验证:
1
2
3
4
5
6
7
8 # 查看集群成员
consul members
# 输出应包含3个 Server 节点,状态为 alive
# Node Address Status Type Build Protocol DC Partition
# consul-server-1 10.0.0.21:8301 alive server 1.17.0 2 dc1 default
# consul-server-2 10.0.0.22:8301 alive server 1.17.0 2 dc1 default
# consul-server-3 10.0.0.23:8301 alive server 1.17.0 2 dc1 default
3.3 后端服务注册
在后端节点上创建服务定义文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 cat > /etc/consul.d/web-service.json << 'EOF'
{
"service": {
"name": "web-app",
"tags": ["http", "v1"],
"port": 8080,
"check": {
"id": "web-app-health",
"name": "HTTP Health Check",
"http": "http://127.0.0.1:8080/health",
"interval": "10s",
"timeout": "3s",
"deregister_critical_service_after": "30s"
}
}
}
EOF
# 启动 Consul Agent(Client 模式)
cat > /etc/consul.d/client.json << 'EOF'
{
"node_name": "backend-1",
"server": false,
"datacenter": "dc1",
"data_dir": "/opt/consul",
"bind_addr": "10.0.0.31",
"retry_join": ["10.0.0.21", "10.0.0.22", "10.0.0.23"],
"enable_local_script_checks": true
}
EOF
1 | deregister_critical_service_after |
是一个关键配置——当健康检查持续失败超过 30 秒,Consul 会自动将服务从目录中移除,避免 HAProxy 将流量发给已经死掉的节点。
四、HAProxy 配置与 Consul 集成
4.1 安装 HAProxy
在两台 LB 节点上分别安装:
1
2 sudo apt install -y haproxy
sudo systemctl enable haproxy
4.2 启用 Consul Backend 动态解析
HAProxy 原生支持 Consul 作为 DNS 解析后端。这是实现动态服务发现的关键。修改
1 | /etc/haproxy/haproxy.cfg |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 # 全局配置
global
log /dev/log local0
maxconn 50000
daemon
# 默认配置
defaults
log global
mode http
option httplog
option dontlognull
option forwardfor
timeout connect 5s
timeout client 50s
timeout server 50s
retries 3
# Consul DNS 解析器
resolvers consul_dns
nameserver consul1 10.0.0.21:8600
nameserver consul2 10.0.0.22:8600
nameserver consul3 10.0.0.23:8600
resolve_retries 3
timeout retry 2s
hold valid 10s
hold obsolete 30s
accepted_payload_size 8192
# 统计页面
frontend stats
bind *:8404
mode http
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
# HTTP 前端
frontend http_in
bind *:80
# 绑定域名
acl host_app hdr(host) -i app.example.com
use_backend web_app if host_app
# 默认后端
default_backend web_app
# 后端:从 Consul 动态发现
backend web_app
balance roundrobin
option httpchk GET /health
http-check expect status 200
default-server inter 10s fall 3 rise 2 resolvers consul_dns resolve-prefer ipv4
server-template web 5 _http._tcp.web-app.service.dc1.consul check
1 | server-template |
是最核心的一行配置。它的含义是:
-
1web
— 服务器名称前缀
-
15
— 最多解析 5 个后端实例
-
1_http._tcp.web-app.service.dc1.consul
— Consul DNS SRV 记录查询格式
-
1check
— 启用健康检查
HAProxy 会定期通过 Consul DNS 查询
1 | web-app |
服务的可用实例,自动添加或移除后端服务器,完全不需要手动修改配置或重载 HAProxy。
4.3 HTTPS 终端配置
对于生产环境,强烈建议在 HAProxy 层面做 SSL 终端。使用 Let’s Encrypt + certbot 自动签发证书:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 sudo apt install -y certbot
# 手动获取证书
certbot certonly --standalone -d app.example.com
# 合并证书格式(HAProxy 需要 PEM 格式)
cat /etc/letsencrypt/live/app.example.com/fullchain.pem \
/etc/letsencrypt/live/app.example.com/privkey.pem \
> /etc/haproxy/certs/app.example.com.pem
# 在 haproxy.cfg 前端配置中添加
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/app.example.com.pem
http-request redirect scheme https unless { ssl_fc }
default_backend web_app
# 自动续期脚本
cat > /etc/cron.d/certbot-haproxy << 'EOF'
0 0 1 */2 * root certbot renew --quiet --deploy-hook "cat /etc/letsencrypt/live/app.example.com/fullchain.pem /etc/letsencrypt/live/app.example.com/privkey.pem > /etc/haproxy/certs/app.example.com.pem && systemctl reload haproxy"
EOF
五、Keepalived 实现 VIP 高可用
5.1 安装 Keepalived
1
2 sudo apt install -y keepalived
sudo systemctl enable keepalived
5.2 主节点配置
在 LB-Master (10.0.0.11) 上创建
1 | /etc/keepalived/keepalived.conf |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 global_defs {
router_id LB_MASTER
vrrp_skip_check_adv_addr
vrrp_garp_master_refresh 5
}
# HAProxy 健康检查脚本
vrrp_script chk_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 2
weight -20
fall 3
rise 2
}
# VRRP 实例
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass YourSecurePass99
}
virtual_ipaddress {
10.0.0.100/24 dev eth0
}
track_script {
chk_haproxy
}
# 主节点成为 MASTER 后执行
notify_master "/etc/keepalived/notify.sh master"
# 降级为 BACKUP 后执行
notify_backup "/etc/keepalived/notify.sh backup"
# 进入 FAULT 状态后执行
notify_fault "/etc/keepalived/notify.sh fault"
}
5.3 备节点配置
在 LB-Backup (10.0.0.12) 上创建配置,仅需修改
1 | state |
、
1 | priority |
和
1 | router_id |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 global_defs {
router_id LB_BACKUP
vrrp_skip_check_adv_addr
vrrp_garp_master_refresh 5
}
vrrp_script chk_haproxy {
script "/usr/bin/killall -0 haproxy"
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 YourSecurePass99
}
virtual_ipaddress {
10.0.0.100/24 dev eth0
}
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"
}
5.4 状态切换通知脚本
创建
1 | /etc/keepalived/notify.sh |
,用于在状态切换时发送告警和执行清理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #!/bin/bash
STATE=$1
HOSTNAME=$(hostname)
VIPS="10.0.0.100"
case $STATE in
master)
logger -t keepalived "[$HOSTNAME] Transition to MASTER - taking over VIPs: $VIPS"
# 发送告警通知(可接入钉钉/Slack/企业微信 webhook)
curl -s -X POST "https://your-webhook.example.com/alert" \
-H 'Content-Type: application/json' \
-d "{"text": "[$HOSTNAME] Keepalived became MASTER - VIPs: $VIPS"}"
;;
backup)
logger -t keepalived "[$HOSTNAME] Transition to BACKUP - releasing VIPs"
;;
fault)
logger -t keepalived "[$HOSTNAME] Entering FAULT state"
curl -s -X POST "https://your-webhook.example.com/alert" \
-H 'Content-Type: application/json' \
-d "{"text": "⚠️ [$HOSTNAME] Keepalived FAULT state detected!"}"
;;
esac
关键参数解释:
-
1priority 100 vs 90
— Master 优先级更高,正常情况下持有 VIP
-
1weight -20
— 当 HAProxy 进程不可用时,优先级自动减 20(100→80),低于 Backup 的 90,触发自动切换
-
1advert_int 1
— VRRP 通告间隔 1 秒,确保故障在 3 秒内被检测到
-
1virtual_router_id 51
— 同一子网内多个 Keepalived 实例必须使用不同的 VRID
六、高级配置与生产优化
6.1 HAProxy 调度算法选择
HAProxy 提供多种调度算法,适用于不同场景:
| 算法 | 特点 | 适用场景 |
|---|---|---|
| roundrobin | 轮询,权重可调 | 后端性能相近的通用场景 |
| leastconn | 最少连接数优先 | 长连接场景(WebSocket、数据库) |
| source | 源 IP 哈希 | 需要会话保持的场景 |
| uri | URI 哈希 | 缓存服务 |
| random | 随机分配 | 无状态 API 服务 |
如果后端节点性能不一致,可以搭配
1 | weight |
参数:
1
2
3 backend web_app
balance leastconn
server-template web 5 _http._tcp.web-app.service.dc1.consul check weight 100
6.2 连接优化与内核参数调优
高并发场景下,系统内核参数的调优与 HAProxy 同等重要。在两台 LB 节点上修改
1 | /etc/sysctl.conf |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 # TCP 连接队列
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# TCP 缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# TIME_WAIT 优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# 允许绑定非本地 IP(Keepalived 需要此选项启动前绑定 VIP)
net.ipv4.ip_nonlocal_bind = 1
# TCP 保活
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# 连接跟踪表大小
net.netfilter.nf_conntrack_max = 1048576
应用配置:
1 sudo sysctl -p
6.3 HAProxy 连接速率限制
防止突发流量压垮后端,在 HAProxy 中配置连接速率限制:
1
2
3
4
5
6
7
8
9
10
11
12 frontend http_in
bind *:80
# 每个源 IP 最大 20 并发连接
stick-table type ip size 100k expire 30s store conn_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_conn_rate(0) gt 100 }
# 全局最大并发连接数
maxconn 20000
default_backend web_app
七、故障测试与验证
7.1 验证 VIP 漂移
在 LB-Master 上手动停止 HAProxy,观察 VIP 是否自动迁移:
1
2
3
4
5
6
7
8
9
10 # 在 LB-Master 上执行
sudo systemctl stop haproxy
# 在 LB-Backup 上查看 IP 地址
ip addr show eth0 | grep 10.0.0.100
# 应该能看到 VIP 已经漂移到 Backup 节点
# 在 LB-Backup 上查看 Keepalived 日志
journalctl -u keepalived -f
# 预期输出:Entering MASTER STATE
7.2 验证后端故障自动剔除
1
2
3
4
5
6
7
8
9
10 # 在某个后端节点停止服务
sudo systemctl stop your-web-app
# 在 HAProxy 统计页面观察
# http://10.0.0.100:8404/stats
# 对应的后端服务器状态应变为 DOWN
# 恢复服务后
sudo systemctl start your-web-app
# 状态自动恢复为 UP
7.3 验证 Consul 服务发现
1
2
3
4
5
6
7
8 # 添加新的后端节点
# 1. 在新节点上启动 Consul Client + Web 服务
# 2. 在 Consul 中注册服务
consul catalog services
# 应能看到 web-app 服务
# 3. 在 HAProxy Stats 页面确认新节点出现
# HAProxy 无需重载,新节点自动被纳入负载均衡池
7.4 模拟整机宕机
最极端的测试——直接关掉主节点:
1
2
3
4
5
6
7
8
9
10
11
12 # 在 LB-Master 上执行(谨慎!)
sudo shutdown -h now
# 在客户端持续请求测试
while true; do
curl -s -o /dev/null -w '%{http_code}\n' http://10.0.0.100/health
sleep 0.5
done
# 预期结果:
# 200 → 200 → 200 → (短暂中断 < 3秒) → 200 → 200
# Keepalived 检测到 Master 不可用,Backup 在 3 秒内接管
八、监控与告警体系
8.1 HAProxy 指标导出
使用
1 | haproxy_exporter |
将 HAProxy 统计数据导出为 Prometheus 格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 # 安装 haproxy_exporter
wget https://github.com/prometheus/haproxy_exporter/releases/download/v0.15.0/haproxy_exporter-0.15.0.linux-amd64.tar.gz
tar xzf haproxy_exporter-0.15.0.linux-amd64.tar.gz
sudo cp haproxy_exporter-0.15.0.linux-amd64/haproxy_exporter /usr/local/bin/
# 创建 systemd 服务
cat > /etc/systemd/system/haproxy-exporter.service << 'EOF'
[Unit]
Description=HAProxy Exporter
After=network.target
[Service]
ExecStart=/usr/local/bin/haproxy_exporter \
--haproxy.scrape-uri=http://127.0.0.1:8404/stats;csv \
--web.listen-address=:9101
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now haproxy-exporter
8.2 Consul 健康检查监控
Consul 自带健康检查 API,可以直接查询集群整体健康状况:
1
2
3
4
5
6
7
8
9
10
11 # 查看所有不健康的服务
consul health service web-app --passing=false
# 输出示例:
# Node ID Name Status
# backend-2 web-app web-app critical
# 通过 API 查询(适合集成到监控脚本)
curl -s http://127.0.0.1:8500/v1/health/service/web-app?passing | \
python3 -m json.tool | grep -c '"Status": "passing"'
# 返回健康实例数量
8.3 关键告警规则
以下是推荐配置的 Prometheus 告警规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 groups:
- name: haproxy
rules:
- alert: HAProxyBackendDown
expr: haproxy_server_status{state="DOWN"} > 0
for: 2m
labels:
severity: critical
annotations:
summary: "HAProxy backend {{ $labels.server }} is DOWN"
- alert: HAProxyHighErrorRate
expr: rate(haproxy_server_response_errors_total[5m]) / rate(haproxy_server_responses_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "HAProxy backend {{ $labels.server }} error rate exceeds 5%"
- alert: HAProxyHighQueue
expr: haproxy_server_current_queue > 100
for: 3m
labels:
severity: warning
annotations:
summary: "HAProxy server {{ $labels.server }} queue exceeds 100"
- name: keepalived
rules:
- alert: KeepalivedStateChange
expr: keepalived_vrrp_state != 2
for: 1m
labels:
severity: critical
annotations:
summary: "Keepalived VRRP instance {{ $labels.instance }} is not MASTER"
- name: consul
rules:
- alert: ConsulServiceCritical
expr: consul_health_service_status{status="critical"} > 0
for: 3m
labels:
severity: critical
annotations:
summary: "Consul service {{ $labels.service_name }} is critical on {{ $labels.node }}"
九、常见问题与排错指南
9.1 VIP 无法漂移
最常见的排查步骤:
1
2
3
4
5
6
7
8
9
10
11 # 1. 检查防火墙是否放行 VRRP 协议(IP 协议号 112)
sudo iptables -A INPUT -p 112 -j ACCEPT
# 2. 检查 Keepalived 日志
journalctl -u keepalived --since '10 minutes ago'
# 3. 确认两端配置的 virtual_router_id 和 auth_pass 完全一致
# 4. 确认 ip_nonlocal_bind 已启用
cat /proc/sys/net/ipv4/ip_nonlocal_bind
# 应输出 1
9.2 HAProxy 无法解析 Consul 服务
1
2
3
4
5
6 # 手动测试 Consul DNS 解析
dig @10.0.0.21 -p 8600 web-app.service.dc1.consul SRV
# 预期返回 SRV 记录,包含后端 IP 和端口
# 如果返回空结果,检查 Consul 中服务是否注册成功:
consul catalog services
9.3 后端节点频繁上下线
如果后端节点在 HAProxy 统计页面上频繁闪烁 UP/DOWN,通常是健康检查超时导致。调整参数:
1
2
3 backend web_app
# 增加健康检查间隔和超时时间
default-server inter 10s fall 5 rise 3 timeout check 5s
同时在 Consul 侧也调整检查间隔:
1
2
3
4
5 "check": {
"interval": "15s",
"timeout": "5s",
"deregister_critical_service_after": "60s"
}
十、总结
通过 HAProxy + Keepalived + Consul 的组合,我们构建了一个具备以下能力的高可用负载均衡集群:
- 入口高可用:Keepalived 通过 VIP 漂移实现秒级故障切换,客户端无感知
- 后端高可用:HAProxy 的健康检查 + Consul 的服务发现实现故障自动剔除和自动纳管
- 水平扩展:新后端节点上线后自动加入负载均衡池,无需任何人工干预
- 可观测性:通过 Prometheus + Grafana 实现全链路监控和告警
这套架构的运维成本并不高——大部分组件开箱即用,且故障自愈能力很强。对于正在从单机架构向集群架构演进的团队来说,这是一个经过大规模生产验证的方案,值得在实际项目中落地。
下一步的演进方向可以考虑:引入 Consul Connect 实现服务网格(mTLS 加密)、添加 HAProxy Data Plane API 实现更灵活的配置管理、以及结合 GitOps 实现配置版本化与自动化部署。
汤不热吧