业务背景
我们服务的某武汉电商平台,日均PV 200万+,峰值 QPS 3000+。最初部署在光谷单一机房,但随着业务增长,面临以下问题:
- 单点故障风险:机房断电/断网导致全网不可用
- 跨区域访问慢:沌口/汉口用户访问光谷机房延迟较高
- 容量瓶颈:大促期间单机房资源不足
决定采用双机房架构:光谷主机房 + 沌口备用机房。
架构设计
┌──────────────┐
│ DNS 智能解析 │
│ (阿里云DNS) │
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 光谷-LB-M │ │ 光谷-LB-S │ │ 沌口-LB │
│ (Master) │ │ (Slave) │ │ (Backup) │
│ Nginx+Keepalived │ Nginx+Keepalived │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
└──────┬───────┘ │
▼ ▼
┌────────────┐ ┌────────────┐
│ 光谷业务集群│ │ 沌口业务集群│
│ (Web×6) │ │ (Web×3) │
│ (DB主+从) │ │ (DB只读) │
│ (Redis集群) │ │ (Redis从) │
└────────────┘ └────────────┘
Nginx 负载均衡配置
基础配置
# nginx.conf
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
use epoll;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'upstream_addr=$upstream_addr '
'upstream_response_time=$upstream_response_time';
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml;
# 上游服务器组
upstream guanggu_backend {
server 10.0.1.101:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.1.102:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.1.103:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.1.104:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.1.105:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.1.106:8080 weight=5 max_fails=3 fail_timeout=30s;
keepalive 32; # 长连接池
}
upstream zhuankou_backend {
server 10.0.2.201:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.2.202:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.2.203:8080 weight=3 max_fails=3 fail_timeout=30s;
keepalive 16;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://guanggu_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
# 健康检查页面
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
# Nginx 状态页
location /nginx_status {
stub_status on;
allow 10.0.0.0/8; # 仅允许内网访问
deny all;
}
}
}
Keepalived 高可用配置
# 光谷 Master 节点 keepalived.conf
! Configuration File for keepalived
global_defs {
router_id LB_GUANGGU_MASTER
notification_email {
ops@example.com
}
notification_email_from keepalived@example.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
}
vrrp_instance VI_1 {
state MASTER # 主节点
interface eth0 # 绑定网卡
virtual_router_id 51 # VRRP 路由 ID(同一集群须一致)
priority 110 # 优先级(主 > 备)
advert_int 1 # 心跳间隔(秒)
authentication {
auth_type PASS
auth_pass your_secret_password
}
virtual_ipaddress {
10.0.0.100 # VIP(虚拟 IP)
}
track_script {
check_nginx # 检测 Nginx 是否存活
}
}
vrrp_script check_nginx {
script "/usr/local/bin/check_nginx.sh"
interval 2 # 每2秒检测一次
weight -5 # 检测失败时优先级减5
fall 3 # 连续3次失败判定为宕机
rise 2 # 连续2次成功判定为恢复
}
# 光谷 Slave 节点 keepalived.conf(仅列出差异部分)
vrrp_instance VI_1 {
state BACKUP
priority 100 # 低于 Master
# ... 其余相同 ...
}
健康检测脚本
# /usr/local/bin/check_nginx.sh
#!/bin/bash
count=$(ps aux | grep nginx | grep -v grep | wc -l)
if [ $count -eq 0 ]; then
exit 1 # Nginx 未运行
fi
# 检测 Nginx 是否能正常响应
code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/health)
if [ $code != "200" ]; then
exit 1 # Nginx 异常
fi
exit 0
DNS 智能解析配置
在阿里云 DNS 配置:
- 默认解析指向 VIP:10.0.0.100(光谷主)
- 武汉电信用户 → 光谷 VIP
- 武汉联通用户 → 光谷 VIP
- 沌口附近用户 → 沌口 LB 直连 IP
- 海外用户 → 沌口 LB(备用线路)
故障切换测试
测试1:Nginx 进程崩溃
# 在 Master 节点模拟 Nginx 崩溃
$ sudo killall -9 nginx
# 观察 Keepalived 日志
$ tail -f /var/log/messages
# [INFO] Nginx health check failed!
# [WARNING] VRRP_Instance(VI_1) Transition to FAULT state
# [NOTICE] VIP released
# Slave 节点接管
# [NOTICE] Received higher priority advert
# [INFO] Becoming MASTER state
# [NOTICE] VIP acquired: 10.0.0.100
# 切换时间:约 3 秒(advert_int × fall + 抢占时间)
测试2:Master 整机宕机
# 直接关闭 Master 服务器电源
# Slave 节点日志
# [WARNING] Master not responding!
# [NOTICE] Transition to MASTER state
# [NOTIVE] VIP acquired
# 切换时间:约 3 秒(advert_int × 3 次 missed)
# 对用户的影响:约 3 秒的部分请求返回 502
测试3:Master 恢复
# 重启 Master 服务器
# Master 日志
# [NOTICE] Higher priority advert received
# [INFO] Transition to MASTER state
# [NOTICE] VIP acquired back
# 注意:默认配置下 Master 恢复后会抢占 VIP
# 如需避免频繁切换,Slave 可设置 nopreempt
监控告警
# 使用 Zabbix 监控
# 监控项:
# 1. VIP 所在节点(应该在 Master 上)
# 2. Nginx 进程状态
# 3. 后端服务器健康状态(up/down 数量)
# 4. QPS 和响应时间
# 5. Keepalived 状态切换事件
# 告警规则:
# - VIP 漂移 → P0 立即通知
# - 后端服务器 down 超过 1 台 → P1 通知
# - QPS 超过阈值 → P2 通知
# - 响应时间 P99 > 3s → P2 通知
运行数据
双机房架构上线 6 个月的数据:
- 可用性:99.998%(仅 1 次短暂故障,因光谷机房电力检修主动切换)
- 平均响应时间:光谷用户 45ms,沌口用户 62ms(之前统一 85ms)
- 故障切换次数:3 次(1 次计划内,2 次意外)
- 平均切换时间:3.2 秒
- 双十一峰值 QPS:8500(平稳承载)