1. 负载均衡概述:为什么需要它
1.1 单机服务的瓶颈
在互联网早期,一台服务器足以支撑一个网站。随着用户量增长,单机面临以下问题:
| 问题 | 表现 | 后果 |
|---|---|---|
| 性能瓶颈 | CPU、内存、磁盘 I/O、网络带宽达到上限 | 响应变慢,超时增多 |
| 单点故障 | 服务器宕机、网卡故障、机房断电 | 服务完全不可用 |
| 扩展困难 | 垂直扩容(加 CPU / 内存)成本高且有上限 | 无法应对流量洪峰 |
负载均衡(Load Balancing) 的核心思想是:将大量并发请求分散到多台后端服务器上,使每台机器只处理一部分流量,从而提升整体吞吐能力、降低单点风险。
1.2 负载均衡要解决的三件事
┌─────────────────┐
客户端请求 ──────►│ 负载均衡器 │
│ (Director/LB) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Real 1 │ │ Real 2 │ │ Real 3 │
└─────────┘ └─────────┘ └─────────┘
- 分发(Distribution):按某种算法将请求分配到后端节点
- 高可用(High Availability):某节点故障时自动剔除、流量转移
- 扩展(Scalability):可动态增删后端节点,水平扩展
1.3 百亿级流量下的负载均衡
在大型互联网场景(如电商大促、春晚红包),负载均衡处于流量入口的第一道关卡:
- 接入层:DNS → CDN → 四层 LB(LVS/F5)→ 七层 LB(Nginx/HAProxy)→ 应用集群
- 性能要求:四层 LB 需支持百万级并发连接、极低延迟
- 可靠性要求:LB 本身不能成为单点,必须主备 / 集群化
四层负载均衡(如 LVS)因工作在内核态,适合超高并发、低延迟 场景;七层负载均衡(如 Nginx)因能解析 HTTP,适合路由、缓存、SSL 终结等应用层逻辑。
2. 四层与七层负载均衡:本质区别与选型
2.1 OSI 模型回顾
┌──────────────────────────────────────────────────────────────┐
│ 七层 Application HTTP、HTTPS、FTP、SMTP、DNS ... │
│ 六层 Presentation 加密、压缩、编码转换 │
│ 五层 Session 会话管理 │
│ 四层 Transport TCP、UDP(端口、连接) ◄── 四层 LB 工作于此 │
│ 三层 Network IP 路由 │
│ 二层 Data Link MAC 地址 │
│ 一层 Physical 物理传输 │
└──────────────────────────────────────────────────────────────┘
- 四层负载均衡(L4) :基于 IP + 端口 转发,不解析应用层协议内容
- 七层负载均衡(L7) :基于 HTTP Header、URL、Cookie 等应用层信息路由
2.2 核心对比
| 维度 | 四层(L4) | 七层(L7) |
|---|---|---|
| 工作层级 | 传输层(TCP/UDP) | 应用层(HTTP/HTTPS) |
| 转发依据 | IP、端口 | URL、Header、Cookie、Host |
| 性能 | 极高(内核转发) | 较高(需解析协议) |
| 灵活性 | 低,无法按 URL 分流 | 高,可灰度、A/B 测试 |
| 典型工具 | LVS、F5、Nginx stream | Nginx、HAProxy、Traefik |
| SSL 终结 | 一般不支持(需透传) | 支持 |
| 真实 IP | 需额外机制(TOA/Proxy Protocol) | 可通过 X-Forwarded-For 传递 |
| 适用协议 | 任意 TCP/UDP | 主要是 HTTP/HTTPS |
2.3 数据包流转对比
四层 LB 转发示意:
Client L4 LB Backend
│ │ │
│──── SYN (CIP:Cport ──► VIP:Vport) ────────────────►│
│ │ 修改目标 IP:Port │
│ │─────────────────────────►│ RIP:Rport
│◄─── 响应 ◄──────────────│◄─────────────────────────│
七层 LB 转发示意:
Client L7 LB Backend
│ │ │
│──── GET /api/user HTTP/1.1 ─────────────────────►│
│ │ 解析 Host/URI │
│ │ 选择 upstream │
│ │──── 新 TCP 连接 ────────►│
│ │ 可修改 Header │
│◄─── HTTP Response ◄─────│◄─────────────────────────│
2.4 工作中如何选择四 / 七层
选四层(LVS / Nginx stream)当:
- 流量极大,追求极致性能(百万 QPS 级)
- 协议非 HTTP(MySQL、Redis、MQ、gRPC over TCP)
- 只需按 IP:Port 分发,无需 URL 路由
- 作为入口第一层,后面再接七层
选七层(Nginx / HAProxy HTTP)当:
- 需要按 URL、域名、Header 路由
- 需要 SSL 卸载、缓存、限流、WAF
- 需要灰度发布、A/B 测试
- Web/API 服务为主
典型生产架构(推荐):
Internet → LVS(四层) → Nginx集群(七层) → 应用服务器
↑ ↑
超高并发入口 灵活路由+SSL+缓存
3. LVS 是什么:架构与核心组件
3.1 LVS 简介
LVS(Linux Virtual Server) 是由章文嵩博士主导开发的开源负载均衡解决方案 ,集成在 Linux 内核中,通过 IPVS(IP Virtual Server) 模块实现四层负载均衡。
特点:
- 内核级转发:数据包在内核空间完成调度,性能接近硬件 LB
- 高并发:可支持数十万并发连接
- 多种工作模式:NAT、DR、TUN、FullNAT
- 丰富调度算法:rr、wrr、lc、wlc、sh、dh 等
- 开源免费:无授权费用
3.2 LVS 架构组成
┌─────────────────────────────────────────────────────────────┐
│ LVS 集群架构 │
│ │
│ Client ──► Director Server (负载调度器) │
│ ├── VIP: 虚拟 IP,对外服务地址 │
│ ├── DIP: Director IP,与 RS 通信 │
│ └── IPVS 内核模块 + ipvsadm 管理工具 │
│ │ │
│ ┌──────────┼──────────┐ │
│ ▼ ▼ ▼ │
│ Real Server Real Server Real Server │
│ RIP1:Port RIP2:Port RIP3:Port │
└─────────────────────────────────────────────────────────────┘
| 组件 | 全称 | 说明 |
|---|---|---|
| Director | 负载调度器 | 运行 IPVS,接收请求并转发到 RS |
| Real Server (RS) | 真实服务器 | 实际处理业务的后端节点 |
| VIP | Virtual IP | 对外提供的虚拟 IP,客户端访问地址 |
| DIP | Director IP | Director 与 RS 通信用的 IP |
| RIP | Real IP | RS 的真实 IP |
| IPVS | IP Virtual Server | Linux 内核模块,实现负载均衡 |
| ipvsadm | 用户态管理工具 | 配置 IPVS 规则 |
3.3 为何需要 LVS
与 Nginx、HAProxy 等用户态代理相比,LVS 的优势:
- 性能:内核转发,无用户态 / 内核态频繁切换,单机可支撑百万连接
- 稳定:久经大规模生产验证(阿里、百度、腾讯等)
- 低开销:Director 只做调度,不终止连接(DR/TUN 模式)
- 适配广:支持 TCP、UDP、SCTP 等
适用场景:
- 大型网站 / Web 服务入口
- 邮件、DNS、FTP 等 TCP 服务
- MySQL/Redis 等数据库读负载(只读副本)
- 游戏服务器、长连接服务
3.4 LVS 与 Keepalived 的关系
- LVS :负责负载均衡调度
- Keepalived :负责 VIP 漂移、健康检查、Director 高可用(VRRP 协议)
二者常配合使用:Keepalived 管理 VIP 和 RS 健康状态,并调用 ipvsadm 或内置 IPVS 配置实现 LB。
4. LVS 工作模型详解:NAT、DR、TUN、FullNAT
LVS 有四种工作模式,差异在于请求与响应的数据包路径。
4.1 NAT 模式
原理: Director 修改数据包的目标 IP(DNAT),RS 响应时修改源 IP(SNAT),请求和响应都经过 Director。
Client ──► Director(VIP) ──DNAT──► RS(RIP)
Client ◄── Director(VIP) ◄──SNAT── RS(RIP)
| 项目 | 说明 |
|---|---|
| Director | 必须开启 ip_forward,通常是 RS 的默认网关 |
| RS 网络 | 可以是私有 IP,与 Director 同网段或跨网段 |
| RS 网关 | 必须指向 Director 的 DIP |
| 优点 | RS 可使用任意操作系统,RS 可出公网 |
| 缺点 | Director 易成瓶颈,扩展 RS 数量受限(约 10~20 台) |
4.2 DR 模式(Direct Routing,最常用)
原理: Director 只修改目标 MAC 地址 ,将包转发给 RS;RS 响应时直接回 Client,不经过 Director。
Client ──► Director(VIP) ──改MAC──► RS(有VIP在lo上)
Client ◄────────────────────────── RS 直接响应
| 项目 | 说明 |
|---|---|
| 要求 | Director 与 RS 必须在同一物理网段(L2) |
| RS 配置 | lo 上绑定 VIP;抑制 ARP 应答 |
| 优点 | 性能最高,Director 压力小,可扩展大量 RS |
| 缺点 | 要求 L2 互通,配置稍复杂 |
4.3 TUN 模式(IP Tunneling)
原理: Director 将请求包封装在 IP 隧道中发给 RS;RS 解封装后处理,响应直接回 Client。
Client ──► Director ──IPIP隧道──► RS(有VIP在tunl0上)
Client ◄────────────────────────── RS 直接响应
| 项目 | 说明 |
|---|---|
| 要求 | RS 支持 IPIP 隧道 |
| 优点 | RS 可跨网段、跨地域 |
| 缺点 | 有隧道封装开销,MTU 需调整 |
4.4 FullNAT 模式
原理: 同时修改源 IP 和目标 IP,RS 无需配置 VIP,也无需与 Director 同网段。
Client ──► Director: 改 src=DIP, dst=RIP ──► RS
Client ◄── Director: 改 src=VIP, dst=CIP ◄── RS
| 项目 | 说明 |
|---|---|
| 优点 | RS 无需 VIP,无 ARP 问题,跨网段灵活 |
| 缺点 | 需内核支持(阿里开源 patch),响应仍过 Director |
4.5 四种模式对比总结
| 模式 | 请求路径 | 响应路径 | 性能 | 复杂度 | 生产常用度 |
|---|---|---|---|---|---|
| NAT | 过 Director | 过 Director | 中 | 低 | ★★★ |
| DR | 过 Director | 直连 Client | 高 | 中 | ★★★★★ |
| TUN | 过 Director(隧道) | 直连 Client | 中高 | 高 | ★★ |
| FullNAT | 过 Director | 过 Director | 中 | 中 | ★★★★ |
选型建议:
- 同机房、Web/DB 读负载 → DR
- 小规模、RS 需出公网 → NAT
- 跨机房、跨地域 → TUN 或 FullNAT
- 云环境、RS 无 VIP 需求 → FullNAT
5. ipvsadm:LVS 集群管理命令全解
5.1 安装与内核模块
# CentOS/RHEL
yum install -y ipvsadm
# Ubuntu/Debian
apt install -y ipvsadm
# 加载 IPVS 内核模块
modprobe ip_vs
modprobe ip_vs_rr # 调度算法模块
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe ip_vs_wlc
# 开机自动加载
cat >> /etc/modules-load.d/ipvs.conf << 'EOF'
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
ip_vs_wlc
nf_conntrack
EOF
# 查看是否加载成功
lsmod | grep ip_vs
5.2 ipvsadm 命令结构
ipvsadm [选项] [虚拟服务] [真实服务器]
# 常用选项
-A 添加虚拟服务
-a 添加真实服务器
-t TCP 服务
-u UDP 服务
-s 指定调度算法
-r 指定 Real Server
-g DR 模式 (Gateway)
-m NAT 模式 (Masquerade)
-i TUN 模式
-w 权重
-p 持久连接超时(秒)
-d 删除
-D 删除虚拟服务
-E 编辑虚拟服务
-e 编辑 RS
-L 列出规则
-l 列出规则(简写)
-C 清空所有规则
-S 保存规则
-R 恢复规则
-Z 清零计数器
--stats 统计信息
--rate 速率信息
-n 数字形式显示 IP/端口
5.3 常用配置示例
添加虚拟服务 + RS(DR 模式)
# 在 Director 上绑定 VIP
ip addr add 192.168.1.100/32 dev eth0
# 添加 TCP 80 端口的虚拟服务,使用 wlc 算法
ipvsadm -A -t 192.168.1.100:80 -s wlc
# 添加 RS(-g 表示 DR 模式)
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:80 -g -w 1
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.12:80 -g -w 2
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.13:80 -g -w 1
# 查看规则
ipvsadm -Ln
NAT 模式
ipvsadm -A -t 192.168.1.100:80 -s rr
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:80 -m -w 1
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.12:80 -m -w 1
持久连接(会话保持)
# 同一客户端 300 秒内固定到同一 RS
ipvsadm -A -t 192.168.1.100:80 -s rr -p 300
删除与清空
# 删除单个 RS
ipvsadm -d -t 192.168.1.100:80 -r 192.168.1.11:80
# 删除整个虚拟服务
ipvsadm -D -t 192.168.1.100:80
# 清空所有规则
ipvsadm -C
5.4 规则持久化
# 保存
ipvsadm-save > /etc/sysconfig/ipvsadm
# 或使用
service ipvsadm save
# 恢复
ipvsadm-restore < /etc/sysconfig/ipvsadm
5.5 查看统计与监控
# 列出规则及连接数、包数
ipvsadm -Ln --stats
# 速率信息
ipvsadm -Ln --rate
# 持续监控(类似 top)
watch -n 1 'ipvsadm -Ln --stats'
输出示例解读:
TCP 192.168.1.100:80 wlc
-> 192.168.1.11:80 Masq 1 0 0
-> 192.168.1.12:80 Masq 2 0 0
- 第一列:协议与 VIP:Port
- 第二列:调度算法
->后:RIP:Port、模式、权重、当前连接数、包数、字节数
6. LVS-NAT 模型:原理、架构与完整实操
6.1 NAT 模型原理深入
NAT 模式下,Director 充当 RS 的网关,对数据包做两次地址转换:
- 入向(DNAT) :
CIP:Cport → VIP:Vport变为CIP:Cport → RIP:Rport - 出向(SNAT) :
RIP:Rport → CIP:Cport变为VIP:Vport → CIP:Cport
Client 认为始终在与 VIP 通信,不知道 RS 的存在。
6.2 NAT 架构与 IP 规划
Internet
│
│ 访问 VIP: 192.168.1.100:80
▼
┌─────────────────────┐
│ Director (LB) │
│ eth0: 192.168.1.10│ ◄── DIP(内网)
│ eth0: 192.168.1.100│ ◄── VIP(可与 DIP 同网卡)
│ ip_forward = 1 │
└──────────┬──────────┘
│ 内网 192.168.1.0/24
┌──────────┼──────────┐
▼ ▼ ▼
RS1: 192.168.1.11 RS2: 192.168.1.12
GW: 192.168.1.10 GW: 192.168.1.10
运行 Nginx/Apache 运行 Nginx/Apache
| 角色 | 主机名 | IP | 说明 |
|---|---|---|---|
| Director | lvs-nat | 192.168.1.10 (DIP), 192.168.1.100 (VIP) | 负载调度器 |
| RS1 | web1 | 192.168.1.11 | 网关 192.168.1.10 |
| RS2 | web2 | 192.168.1.12 | 网关 192.168.1.10 |
| Client | client | 192.168.1.200 | 测试客户端 |
6.3 Director 配置步骤
# ========== Director (192.168.1.10) ==========
# 1. 开启 IP 转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
# 2. 配置 VIP(若与 DIP 同网卡)
ip addr add 192.168.1.100/32 dev eth0
# 3. 安装 ipvsadm
yum install -y ipvsadm
# 4. 清除旧规则
ipvsadm -C
# 5. 添加虚拟服务(NAT 模式 -m)
ipvsadm -A -t 192.168.1.100:80 -s wrr
# 6. 添加 RS
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:80 -m -w 1
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.12:80 -m -w 2
# 7. 验证
ipvsadm -Ln
6.4 Real Server 配置步骤
# ========== RS1 (192.168.1.11) & RS2 (192.168.1.12) ==========
# 1. 设置默认网关为 Director 的 DIP
ip route del default
ip route add default via 192.168.1.10
# 永久生效(CentOS)
cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << 'EOF'
DEVICE=eth0
BOOTPROTO=static
IPADDR=192.168.1.11
NETMASK=255.255.255.0
GATEWAY=192.168.1.10
ONBOOT=yes
EOF
# 2. 安装并启动 Web 服务(用于区分 RS)
yum install -y nginx
echo "RS1 - 192.168.1.11" > /usr/share/nginx/html/index.html
systemctl start nginx && systemctl enable nginx
# RS2 同理,页面内容改为 RS2
注意: RS 上不要 配置 VIP,也不要在 lo 上绑 VIP(那是 DR 模式的做法)。
6.5 测试与验证
# ========== Client (192.168.1.200) ==========
# 1. 访问 VIP
curl http://192.168.1.100
# 2. 多次请求,观察轮询(wrr 下 RS2 权重 2,应获得更多请求)
for i in {1..10}; do curl -s http://192.168.1.100; echo; done
# 3. 在 Director 查看连接统计
ipvsadm -Ln --stats
# 4. 在 RS 上抓包验证
tcpdump -i eth0 host 192.168.1.200 -nn
# 应看到来自 Client IP 的请求(非 VIP)
6.6 NAT 模式常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| Client 无法访问 VIP | Director 未绑 VIP 或防火墙拦截 | 检查 ip addr、iptables |
| RS 能收到包但无响应 | RS 网关未指向 Director | 检查 ip route |
| 响应无法回到 Client | Director 未开 ip_forward | sysctl net.ipv4.ip_forward |
| 连接超时 | RS 路由错误,响应未经过 Director | RS 网关必须是 DIP |
| Director 性能瓶颈 | 所有流量双向往返 | 考虑 DR 模式或增加 Director |
7. LVS-DR 模型:原理、架构与 Shell 自动化
7.1 DR 模型原理深入
DR(Direct Routing)模式下:
- Client 请求
CIP → VIP,到达 Director - Director 根据调度算法选择 RS,只修改二层 MAC,将包转发给 RS
- RS 的 lo 接口上配置了 VIP,因此认为该包是发给自己的
- RS 处理完毕,直接以 VIP 为源 IP 响应 Client(不经过 Director)
关键:Director 与 RS 必须在同一广播域(同一 VLAN / 交换机)。
7.2 ARP 问题与抑制
若 RS 也配置了 VIP,可能引发 ARP 冲突:交换机 / Client 的 ARP 表可能把 VIP 解析到 RS 的 MAC,导致 Client 绕过 Director 直接访问 RS,破坏负载均衡。
解决方案:在 RS 上抑制 ARP
# 抑制 ARP 应答:lo 上的 VIP 不响应 ARP 请求
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
# 抑制 eth0 对 VIP 的 ARP 应答
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
arp_ignore 取值:
| 值 | 含义 |
|---|---|
| 0 | 任意网络接口收到 ARP 请求,只要目标 IP 在本机,就响应 |
| 1 | 仅当目标 IP 配置在收到请求的接口上时才响应 |
arp_announce 取值:
| 值 | 含义 |
|---|---|
| 0 | 使用任意本地地址 |
| 1 | 尽量避免不在同一子网的地址 |
| 2 | 优先使用与目标在同一子网的地址 |
7.3 DR 架构与 IP 规划
Internet
│
▼
┌─────────────────────┐
│ Director (LB) │
│ eth0: 192.168.1.10│
│ VIP: 192.168.1.100│ (绑在 eth0)
└──────────┬──────────┘
│ 同一 L2 网段 192.168.1.0/24
┌──────────┼──────────┐
▼ ▼ ▼
RS1: 192.168.1.11 RS2: 192.168.1.12
lo:0 VIP/32 lo:0 VIP/32
网关: 192.168.1.1 网关: 192.168.1.1 (真实网关,非 Director)
| 角色 | IP | VIP 位置 | 网关 |
|---|---|---|---|
| Director | 192.168.1.10 | eth0: 192.168.1.100/32 | 192.168.1.1 |
| RS1 | 192.168.1.11 | lo:0: 192.168.1.100/32 | 192.168.1.1 |
| RS2 | 192.168.1.12 | lo:0: 192.168.1.100/32 | 192.168.1.1 |
7.4 Director 配置
# ========== Director ==========
ip addr add 192.168.1.100/32 dev eth0
ipvsadm -C
ipvsadm -A -t 192.168.1.100:80 -s wlc
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:80 -g -w 1
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.12:80 -g -w 1
ipvsadm -Ln
7.5 Real Server 配置
# ========== RS1 / RS2 ==========
VIP=192.168.1.100
# 1. lo 上绑定 VIP
ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP up
# 2. 抑制 ARP
cat >> /etc/sysctl.conf << 'EOF'
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
EOF
sysctl -p
# 3. 网关指向真实网关(不是 Director!)
# GATEWAY=192.168.1.1
# 4. 启动 Web 服务
yum install -y nginx
echo "DR-RS1" > /usr/share/nginx/html/index.html
systemctl start nginx
7.6 Shell 脚本自动化配置 LVS-DR
Director 端脚本 lvs-director.sh
#!/bin/bash
# LVS-DR Director 配置脚本
# 用法: ./lvs-director.sh start|stop|status
VIP=192.168.1.100
PORT=80
RS_SERVERS=(
"192.168.1.11:80:1"
"192.168.1.12:80:1"
"192.168.1.13:80:2"
)
SCHEDULER=wlc
IFACE=eth0
start() {
echo "[Director] 启动 LVS-DR..."
# 加载内核模块
modprobe ip_vs
modprobe ip_vs_wlc
# 绑定 VIP
if ! ip addr show dev $IFACE | grep -q "$VIP"; then
ip addr add ${VIP}/32 dev $IFACE
echo "[Director] VIP $VIP 已绑定到 $IFACE"
fi
# 清除旧规则
ipvsadm -C
# 添加虚拟服务
ipvsadm -A -t ${VIP}:${PORT} -s $SCHEDULER
# 添加 RS
for rs in "${RS_SERVERS[@]}"; do
IFS=':' read -r rip rport weight <<< "$rs"
ipvsadm -a -t ${VIP}:${PORT} -r ${rip}:${rport} -g -w $weight
echo "[Director] 添加 RS: ${rip}:${rport} weight=$weight"
done
echo "[Director] LVS-DR 启动完成"
ipvsadm -Ln
}
stop() {
echo "[Director] 停止 LVS-DR..."
ipvsadm -C
ip addr del ${VIP}/32 dev $IFACE 2>/dev/null
echo "[Director] 已停止"
}
status() {
echo "=== VIP 状态 ==="
ip addr show dev $IFACE | grep $VIP
echo "=== IPVS 规则 ==="
ipvsadm -Ln --stats
}
case "$1" in
start) start ;;
stop) stop ;;
status) status ;;
*) echo "用法: $0 {start|stop|status}"; exit 1 ;;
esac
Real Server 端脚本 lvs-rs.sh
#!/bin/bash
# LVS-DR Real Server 配置脚本
# 用法: ./lvs-rs.sh start|stop
VIP=192.168.1.100
IFACE=lo:0
start() {
echo "[RS] 配置 LVS-DR Real Server..."
# 绑定 VIP 到 lo
ifconfig $IFACE $VIP netmask 255.255.255.255 broadcast $VIP up
# ARP 抑制
cat > /etc/sysctl.d/99-lvs-rs.conf << 'EOF'
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
EOF
sysctl -p /etc/sysctl.d/99-lvs-rs.conf
echo "[RS] VIP $VIP 已绑定到 $IFACE,ARP 已抑制"
}
stop() {
ifconfig $IFACE down 2>/dev/null
echo "[RS] 已停止"
}
case "$1" in
start) start ;;
stop) stop ;;
*) echo "用法: $0 {start|stop}"; exit 1 ;;
esac
7.7 DR 模式测试验证
# Client 多次访问
for i in {1..20}; do curl -s http://192.168.1.100; done
# Director 上查看(应有 InPkts 增加,OutPkts 较少,因响应不经过 Director)
ipvsadm -Ln --stats
# RS 上抓包:应看到目标 MAC 是 RS 的 MAC,目标 IP 是 VIP
tcpdump -i eth0 -nn host 192.168.1.100
# RS 上验证响应源 IP 是 VIP
tcpdump -i eth0 -nn src 192.168.1.100 and host <Client_IP>
8. Keepalived + LVS:高可用架构实战
8.1 LVS 高可用问题
单机 Director 存在单点故障:
- Director 宕机 → VIP 不可用 → 整个集群瘫痪
- Director 网卡故障 → 同上
- 维护重启 → 服务中断
解决思路: 部署主备两台 Director ,通过 VRRP 协议共享 VIP,Master 故障时 Backup 接管 VIP 和 IPVS 规则。
8.2 Keepalived 简介
Keepalived 基于 VRRP(Virtual Router Redundancy Protocol),提供:
- VIP 漂移:Master 持 VIP,Backup 监听;Master 故障时 Backup 升主
- 健康检查:检测 RS 和后端服务,故障 RS 自动剔除
- IPVS 集成:可直接在配置文件中定义 LVS 规则,无需手动 ipvsadm
8.3 高可用架构
Internet
│
▼
VIP: 192.168.1.100 (漂移)
│
┌──────────────┴──────────────┐
▼ ▼
Director-M (Master) Director-B (Backup)
192.168.1.10 192.168.1.20
Keepalived: MASTER Keepalived: BACKUP
持有 VIP 监听 VRRP,待命
│ │
└──────────────┬──────────────┘
▼
RS1 / RS2 / RS3 (LVS-DR)
8.4 安装 Keepalived
yum install -y keepalived
# 或
apt install -y keepalived
8.5 Master 配置 /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id LVS_MASTER
vrrp_skip_check_adv_addr
}
# LVS 健康检查脚本(可选)
vrrp_script check_lvs {
script "/etc/keepalived/check_lvs.sh"
interval 3
weight -20
fall 2
rise 2
}
# VRRP 实例:负责 VIP 漂移
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51 # 同一集群必须相同,范围 1-255
priority 100 # Master 优先级高
advert_int 1 # VRRP 通告间隔(秒)
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.100/32 dev eth0
}
track_script {
check_lvs
}
}
# 虚拟服务器:LVS 规则
virtual_server 192.168.1.100 80 {
delay_loop 6
lb_algo wlc
lb_kind DR # NAT 模式改为 NAT
protocol TCP
real_server 192.168.1.11 80 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 80
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.1.12 80 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 80
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.1.13 80 {
weight 2
TCP_CHECK {
connect_timeout 3
connect_port 80
nb_get_retry 3
delay_before_retry 3
}
}
}
8.6 Backup 配置
global_defs {
router_id LVS_BACKUP
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90 # 低于 Master
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.100/32 dev eth0
}
}
virtual_server 192.168.1.100 80 {
delay_loop 6
lb_algo wlc
lb_kind DR
protocol TCP
real_server 192.168.1.11 80 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 80
}
}
real_server 192.168.1.12 80 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 80
}
}
}
8.7 健康检查脚本
# /etc/keepalived/check_lvs.sh
#!/bin/bash
# 检查 IPVS 模块和规则是否存在
if ! ipvsadm -Ln | grep -q "192.168.1.100:80"; then
exit 1
fi
exit 0
chmod +x /etc/keepalived/check_lvs.sh
8.8 启动与故障切换测试
# Master 和 Backup 都执行
systemctl enable keepalived
systemctl start keepalived
# 查看 VIP(应只在 Master 上)
ip addr show eth0 | grep 192.168.1.100
# 查看 IPVS 规则
ipvsadm -Ln
# 模拟 Master 故障
systemctl stop keepalived # 在 Master 上执行
# 在 Backup 上验证 VIP 已漂移
ip addr show eth0 | grep 192.168.1.100
# Client 访问应无感知(短暂中断 1~3 秒)
curl http://192.168.1.100
8.9 脑裂问题与预防
脑裂: 两台 Director 同时认为自己是 Master,都绑定 VIP,导致 ARP 混乱。
预防措施:
- 使用
vrrp_script检测本机服务,异常时降低 priority - 开启
vrrp_strict(新版本默认) - 使用
unicast_src_ip/unicast_peer单播 VRRP,减少广播干扰 - 防火墙放行 VRRP(协议号 112)和组播 224.0.0.18
9. LVS 调度算法深度解析
LVS 提供多种调度算法,分为静态 和动态两类。
9.1 静态调度算法
rr(Round Robin,轮询)
- 原理:依次将请求分配给每个 RS
- 适用:RS 性能相近、请求处理时间接近
- 示例:3 台 RS,请求顺序 1→2→3→1→2→3...
wrr(Weighted Round Robin,加权轮询)
- 原理:按权重比例分配,权重高的 RS 获得更多请求
- 适用:RS 性能不同(如 4 核 vs 8 核)
- 示例:RS1 (w=1), RS2 (w=2) → 比例约 1:2
sh(Source Hashing,源地址哈希)
- 原理:根据客户端源 IP 哈希,同一 IP 固定到同一 RS
- 适用:需要会话保持,且无 cookie 的场景
- 缺点:RS 数量变化时,大量会话重新映射
dh(Destination Hashing,目标地址哈希)
- 原理:根据目标 IP 哈希
- 适用:缓存集群,同一目标 URL 固定到同一缓存节点
9.2 动态调度算法
lc(Least-Connection,最少连接)
- 原理 :将请求分配给当前连接数最少的 RS
- 适用:请求处理时间差异较大的场景
wlc(Weighted Least-Connection,加权最少连接,默认)
- 原理 :
调度连接数 = 当前连接数 / 权重,选值最小的 RS - 适用 :RS 性能不同且请求耗时不同,生产最常用
lblc(Locality-Based Least-Connection)
- 原理:基于目标 IP 的哈希,在对应 RS 组中选最少连接
- 适用:分布式缓存
lblcr(Locality-Based Least-Connection with Replication)
- 原理:lblc 的增强,支持缓存复制到多个节点
sed(Shortest Expected Delay)
- 原理 :
(C+1)/U,C 为当前连接数,U 为权重,选值最小 - 适用:请求处理时间接近
nq(Never Queue)
- 原理:sed 的改进,若有 RS 连接数为 0,直接分配,不排队
9.3 算法对比与选型
| 算法 | 类型 | 会话保持 | 考虑权重 | 考虑连接数 | 推荐场景 |
|---|---|---|---|---|---|
| rr | 静态 | 否 | 否 | 否 | 同构集群、短连接 |
| wrr | 静态 | 否 | 是 | 否 | 异构集群、短连接 |
| sh | 静态 | 是 (源 IP) | 否 | 否 | 无 cookie 的会话保持 |
| dh | 静态 | 是 (目标) | 否 | 否 | 缓存集群 |
| lc | 动态 | 否 | 否 | 是 | 长连接、耗时差异大 |
| wlc | 动态 | 否 | 是 | 是 | 生产默认首选 |
| sed/nq | 动态 | 否 | 是 | 是 | 优化延迟 |
9.4 配置示例
# 轮询
ipvsadm -A -t 192.168.1.100:80 -s rr
# 加权轮询
ipvsadm -A -t 192.168.1.100:80 -s wrr
# 源地址哈希(会话保持)
ipvsadm -A -t 192.168.1.100:80 -s sh
# 加权最少连接(推荐)
ipvsadm -A -t 192.168.1.100:80 -s wlc
# 持久连接(与算法配合,单位:秒)
ipvsadm -A -t 192.168.1.100:80 -s wlc -p 300
10. 实战案例:LVS-DR 实现 MySQL 负载均衡
10.1 场景说明
对 MySQL 只读从库 做读负载均衡(写仍走主库),是 LVS 的经典场景之一。
应用服务器
│
│ 读请求
▼
VIP: 192.168.1.100:3306
│
┌─────────┴─────────┐
▼ ▼
MySQL-Slave1 MySQL-Slave2
192.168.1.21:3306 192.168.1.22:3306
注意: LVS 做 MySQL 负载均衡时:
- 仅适合读 负载或无状态连接
- 写操作、事务、主从复制配置需应用层配合
- 推荐使用 DR 模式 + sh(源哈希) 或 持久连接,减少连接迁移
10.2 架构与 IP 规划
| 角色 | IP | 说明 |
|---|---|---|
| Director | 192.168.1.10 | VIP: 192.168.1.100 |
| MySQL-S1 | 192.168.1.21 | 从库 1 |
| MySQL-S2 | 192.168.1.22 | 从库 2 |
| App | 192.168.1.50 | 应用服务器 |
10.3 Director 配置
# 绑定 VIP
ip addr add 192.168.1.100/32 dev eth0
# MySQL 3306 端口,源哈希保持连接稳定
ipvsadm -C
ipvsadm -A -t 192.168.1.100:3306 -s sh -p 3600
ipvsadm -a -t 192.168.1.100:3306 -r 192.168.1.21:3306 -g -w 1
ipvsadm -a -t 192.168.1.100:3306 -r 192.168.1.22:3306 -g -w 1
ipvsadm -Ln
10.4 MySQL Real Server 配置
# ========== 两台 MySQL 从库 ==========
VIP=192.168.1.100
# 1. lo 绑定 VIP
ifconfig lo:0 $VIP netmask 255.255.255.255 up
# 2. ARP 抑制
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
# 3. 确保 MySQL 监听 0.0.0.0:3306
# my.cnf: bind-address = 0.0.0.0
systemctl start mysqld
10.5 Keepalived 健康检查(MySQL)
virtual_server 192.168.1.100 3306 {
delay_loop 6
lb_algo sh
lb_kind DR
persistence_timeout 3600
protocol TCP
real_server 192.168.1.21 3306 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 3306
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.1.22 3306 {
weight 1
TCP_CHECK {
connect_timeout 3
connect_port 3306
}
}
}
10.6 验证
# 应用端连接 VIP
mysql -h 192.168.1.100 -P 3306 -u readonly -p -e "SELECT @@hostname, @@server_id;"
# 多次执行,观察是否分配到不同从库(sh 算法下同一 Client IP 固定同一从库)
for i in {1..5}; do
mysql -h 192.168.1.100 -u readonly -p'password' -e "SELECT @@hostname;" 2>/dev/null
done
# Director 统计
ipvsadm -Ln --stats
10.7 MySQL 负载均衡注意事项
- 主从延迟:读从库可能读到旧数据,需业务容忍或读主
- 连接池:应用连接池应对 VIP,而非单个 RS IP
- 故障切换 :Keepalived TCP_CHECK 只能检测端口,建议配合
mysql_check脚本 - 半同步 / 组复制:与 LVS 无冲突,但拓扑设计需统一规划
11. Nginx 四层负载均衡:原理与配置
11.1 Nginx 四层 vs LVS 四层
| 维度 | LVS | Nginx stream |
|---|---|---|
| 实现层级 | 内核 IPVS | 用户态事件驱动 |
| 性能 | 极高 | 高(万级~十万级并发) |
| 配置灵活性 | ipvsadm/Keepalived | 配置文件,易读易维护 |
| 七层能力 | 无 | 同一 Nginx 可同时做七层 |
| 健康检查 | 需 Keepalived | 原生支持 |
| SSL 终结 | 不支持 | stream 可 ssl_preread |
结论: 超大规模入口用 LVS;需要灵活配置、与七层统一、中等规模用 Nginx stream。
11.2 安装与 stream 模块
Nginx 1.9.0+ 自带 stream 模块,需编译时启用:
# 检查是否支持
nginx -V 2>&1 | grep stream
# 若无,编译添加
./configure --with-stream --with-stream_ssl_module
11.3 基本四层配置
/etc/nginx/nginx.conf:
# 注意:stream 与 http 块同级,都在 main 上下文
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
events {
worker_connections 65535;
use epoll;
}
# ========== 四层负载均衡 ==========
stream {
# 日志格式
log_format stream_log '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received"';
access_log /var/log/nginx/stream-access.log stream_log;
# 上游服务器组(nginx官方叫法实际上是rs)
upstream backend_web {
least_conn; # 调度算法
server 192.168.1.11:80 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:80 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.13:80 weight=1 max_fails=3 fail_timeout=30s backup;
}
# 监听并转发
server {
listen 8080; # 监听端口
proxy_pass backend_web;
proxy_connect_timeout 5s;
proxy_timeout 300s;
}
}
# ========== 七层配置(可选,同一 Nginx) ==========
http {
upstream web {
server 192.168.1.11:80;
server 192.168.1.12:80;
}
server {
listen 80;
location / {
proxy_pass http://web;
}
}
}
11.4 stream 调度算法
| 指令 | 说明 |
|---|---|
round_robin |
默认,轮询 |
least_conn |
最少连接 |
hash $remote_addr consistent |
源 IP 哈希,一致性哈希 |
hash $remote_addr$remote_port |
源 IP + 端口哈希 |
upstream mysql_backend {
least_conn;
hash $remote_addr consistent; # 与 least_conn 二选一
server 192.168.1.21:3306;
server 192.168.1.22:3306;
}
11.5 健康检查与故障转移
upstream backend {
server 192.168.1.11:80 max_fails=3 fail_timeout=30s;
server 192.168.1.12:80 max_fails=3 fail_timeout=30s;
server 192.168.1.13:80 backup; # 仅当主 RS 全挂时使用
}
max_fails:连续失败次数fail_timeout:失败窗口 / 禁用时长backup:备用服务器
Nginx Plus 支持主动健康检查;开源版依赖被动检测(连接失败)。
11.6 测试
nginx -t && systemctl reload nginx
# 测试四层转发
curl http://127.0.0.1:8080
# 查看 stream 日志
tail -f /var/log/nginx/stream-access.log
12. LVS、Nginx、HAProxy 对比
12.1 三者定位
| 工具 | 层级 | 实现 | 典型场景 |
|---|---|---|---|
| LVS | 四层 | 内核 IPVS | 超大规模入口、百万并发 |
| Nginx | 四 + 七层 | 用户态 | Web 服务、反向代理、静态资源、中小规模 LB |
| HAProxy | 四 + 七层 | 用户态 | 专业 LB、TCP/HTTP、精细健康检查 |
12.2 详细对比
| 维度 | LVS | Nginx | HAProxy |
|---|---|---|---|
| 性能 | ★★★★★ | ★★★★ | ★★★★ |
| 四层能力 | 原生内核 | stream 模块 | 原生支持 |
| 七层能力 | 无 | 强大 | 强大 |
| 配置方式 | ipvsadm/Keepalived | 配置文件 | 配置文件 |
| 健康检查 | 需 Keepalived | 被动 / Plus 主动 | 主动 + 被动 |
| SSL | 不支持 | 支持 | 支持 |
| 会话保持 | sh、持久连接 | ip_hash、sticky | cookie、source |
| 动态上下线 | 需脚本 | 需 reload | 支持 socket 热更新 |
| 日志 | 有限 | 灵活 | 详细 |
| 学习成本 | 中 | 低 | 中 |
12.3 常见问题
Q1:LVS 的 NAT 和 DR 区别?
NAT:Director 做 DNAT+SNAT,请求响应都过 Director,RS 网关指向 Director。
DR:Director 只改 MAC,RS 直接响应 Client,需 lo 绑 VIP 并抑制 ARP,要求 L2 互通。
DR 性能更高,是生产首选。
Q2:为什么 DR 模式 RS 要在 lo 上绑 VIP?
请求包目标 IP 是 VIP,到达 RS 时目标 MAC 是 RS 的 MAC。RS 必须在某个接口上 "拥有"VIP,内核才会接收该包。绑在 lo 上并抑制 ARP,可避免 RS 响应 ARP 导致 VIP 漂移。
Q3:Nginx 和 LVS 都做四层,怎么选?
流量极大(百万级)选 LVS;需要与七层统一、配置灵活、中等并发选 Nginx stream。常见架构是 LVS 做第一层,Nginx 做第二层。
Q4:Keepalived 原理?
基于 VRRP,Master 持 VIP 并发 VRRP 通告,Backup 监听。Master 故障时 Backup 升主接管 VIP。可配合 script 做健康检查,实现 Director 和 RS 的高可用。
Q5:wlc 和 lc 区别?
lc 选连接数最少的 RS;wlc 选
连接数/权重最小的 RS,考虑 RS 性能差异,是 LVS 默认算法。
Q6:如何防止 LVS 脑裂?
VRRP 优先级、track_script 检测、unicast VRRP、防火墙规范、避免网络分区。
13. Nginx 四层代理 MySQL 实战
13.1 场景
应用通过 Nginx stream 连接 MySQL 从库集群,实现读负载均衡。
13.2 架构
App Server ──► Nginx (192.168.1.10:3306) ──► MySQL-S1 / MySQL-S2
13.3 完整配置
stream {
log_format mysql_log '$remote_addr [$time_local] $protocol '
'$status $bytes_sent $bytes_received '
'$session_time "$upstream_addr"';
access_log /var/log/nginx/mysql-access.log mysql_log;
upstream mysql_read {
least_conn;
server 192.168.1.21:3306 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.22:3306 weight=1 max_fails=3 fail_timeout=30s;
}
server {
listen 3306;
proxy_pass mysql_read;
proxy_connect_timeout 10s;
proxy_timeout 600s; # MySQL 长连接超时
proxy_buffer_size 16k;
}
}
13.4 双端口读写分离(扩展)
stream {
# 读:从库
upstream mysql_read {
least_conn;
server 192.168.1.21:3306;
server 192.168.1.22:3306;
}
# 写:主库
upstream mysql_write {
server 192.168.1.20:3306;
}
server {
listen 3306;
proxy_pass mysql_read;
}
server {
listen 3307;
proxy_pass mysql_write;
}
}
应用配置:读 3306,写 3307。
13.5 验证
nginx -t && systemctl reload nginx
mysql -h 192.168.1.10 -P 3306 -u user -p -e "SELECT @@hostname;"
tail -f /var/log/nginx/mysql-access.log
14. 大并发架构:四层 + 七层组合设计
14.1 为何需要 L4 + L7 组合
单一七层 Nginx 在超大规模下:
- 需解析 HTTP,CPU 开销大于纯四层
- SSL 终结消耗大
- 单机连接数有上限
分层架构:
Internet
│
▼
┌─────────────────┐
│ LVS / F5 (L4) │ ◄── 第一层:超高并发入口
│ VIP 漂移 HA │ 内核转发,百万连接
└────────┬────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Nginx L7 │ │ Nginx L7 │ │ Nginx L7 │ ◄── 第二层:七层
│ SSL │ │ 缓存 │ │ 路由 │ URL路由、限流
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
▼
┌───────────────┐
│ 应用服务器 │ ◄── 第三层:业务
└───────────────┘
14.2 仅七层模式(中小规模)
Internet → Nginx (L7) → App Servers
适用:QPS 万级以下,需 URL 路由、SSL、缓存。
14.3 Nginx 四层 + 多 Nginx 七层
Internet → Nginx Stream (L4) → Nginx1/Nginx2/Nginx3 (L7) → App
L4 Nginx 配置:
stream {
upstream nginx_l7_cluster {
least_conn;
server 192.168.1.11:80 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:80 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.13:80 weight=1 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
proxy_pass nginx_l7_cluster;
proxy_connect_timeout 3s;
proxy_timeout 300s;
}
}
L7 Nginx 配置(每台):
http {
upstream app_backend {
server 192.168.2.11:8080;
server 192.168.2.12:8080;
}
server {
listen 80;
location / {
proxy_pass http://app_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;
}
location /api/ {
proxy_pass http://app_backend;
}
location /static/ {
root /var/www;
expires 30d;
}
}
}
14.4 LVS + Nginx 七层(大规模标准架构)
Internet → LVS(DR) → Nginx×N (L7) → App×N
- LVS:DR 模式,wlc 算法,Keepalived HA
- Nginx:SSL 终结、静态资源、反向代理
- App:无状态,可水平扩展
14.5 性能调优要点
LVS 层:
# 连接跟踪表大小
echo 655360 > /proc/sys/net/ipv4/ip_conntrack_max
# Director 文件描述符
ulimit -n 655350
Nginx 层:
worker_processes auto;
worker_rlimit_nofile 655350;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
}
15. 多级代理下的真实客户端 IP 获取
15.1 问题背景
多级代理后,后端应用看到的 $remote_addr 是上一级代理的 IP,而非真实 Client IP。
Client (1.2.3.4) → LVS → Nginx → App
App 看到的 remote_addr = Nginx 的 IP
日志、限流、风控、审计都需要真实 IP。
15.2 Nginx 相关变量
| 变量 | 含义 |
|---|---|
$remote_addr |
直连 Nginx 的客户端 IP(可能是上一级代理) |
$proxy_add_x_forwarded_for |
追加 X-Forwarded-For 链 |
$http_x_forwarded_for |
请求头中的 X-Forwarded-For |
$http_x_real_ip |
请求头中的 X-Real-IP |
$realip_remote_addr |
realip 模块解析后的真实 IP |
15.3 标准传递方式
Nginx 转发时设置 Header:
location / {
proxy_pass http://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;
}
后端 Nginx 使用 realip 模块还原:
# 信任上游代理 IP 段
set_real_ip_from 192.168.1.0/24;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# 之后 $remote_addr 即为真实 Client IP
log_format main '$remote_addr - $remote_user [$time_local] "$request"';
15.4 多级代理示例
Client → LVS → Nginx1 (L4) → Nginx2 (L7) → App
Nginx1 (L4 stream): 四层无法修改 HTTP Header,真实 IP 需通过 proxy_protocol 或 TOA 模块传递。
Nginx2 (L7) 启用 proxy_protocol:
# Nginx1 stream 发送 proxy_protocol
stream {
server {
listen 80;
proxy_pass 192.168.1.12:80;
proxy_protocol on;
}
}
# Nginx2 接收
server {
listen 80 proxy_protocol;
set_real_ip_from 192.168.1.11;
real_ip_header proxy_protocol;
location / {
proxy_pass http://app;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
15.5 应用层获取真实 IP
Java (Spring):
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
// X-Forwarded-For 可能有多级: "client, proxy1, proxy2"
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
Python (Flask):
from flask import request
def get_client_ip():
if request.headers.get('X-Forwarded-For'):
return request.headers['X-Forwarded-For'].split(',')[0].strip()
return request.headers.get('X-Real-IP', request.remote_addr)
15.6 安全注意
X-Forwarded-For可被客户端伪造,必须 配合set_real_ip_from只信任已知代理- 不要无条件信任
X-Forwarded-For的第一个 IP - 生产环境建议 LVS 层使用 TOA 或在最外层 Nginx 统一注入
16. Nginx 反向代理参数详解
16.1 连接与超时
location / {
proxy_pass http://backend;
# 与后端建立连接的超时
proxy_connect_timeout 60s;
# 向后端发送请求的超时
proxy_send_timeout 60s;
# 从后端读取响应的超时
proxy_read_timeout 60s;
# 客户端与 Nginx 之间的超时
send_timeout 60s;
}
| 参数 | 默认 | 说明 |
|---|---|---|
proxy_connect_timeout |
60s | 连接后端超时 |
proxy_send_timeout |
60s | 发送请求到后端超时 |
proxy_read_timeout |
60s | 从后端读取响应超时 |
16.2 Header 传递
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# 不传递某些头
proxy_pass_request_headers on; # 默认 on
proxy_pass_request_body on; # 默认 on
16.3 缓冲与 Body
proxy_buffering on; # 默认 on,缓冲后端响应
proxy_buffer_size 4k; # 响应头缓冲
proxy_buffers 8 4k; # 响应体缓冲
proxy_busy_buffers_size 8k;
proxy_request_buffering on; # 请求体缓冲
client_max_body_size 10m; # 客户端请求体大小限制
关闭缓冲场景: 流式响应、SSE、大文件下载
proxy_buffering off;
proxy_cache off;
16.4 重定向与 URL
# 后端返回 302 时,重写 Location 头
proxy_redirect http://backend:8080/ http://$host/;
# 自动替换
proxy_redirect default;
# URL 路径替换
location /api/ {
proxy_pass http://backend/v1/; # /api/foo → /v1/foo
}
proxy_pass 路径规则:
| location | proxy_pass | 请求 URI | 转发 URI |
|---|---|---|---|
/api/ |
http://backend/ |
/api/user |
/user |
/api/ |
http://backend |
/api/user |
/api/user |
/api/ |
http://backend/v1/ |
/api/user |
/v1/user |
16.5 故障转移与重试
upstream backend {
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 max_fails=3 fail_timeout=30s;
}
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
16.6 HTTPS 后端
location / {
proxy_pass https://backend;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/ca.crt;
proxy_ssl_server_name on;
proxy_ssl_name backend.example.com;
}
16.7 WebSocket
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
16.8 常用变量实测
# 调试 location,查看各变量值
location /debug/ {
default_type text/plain;
return 200 "
remote_addr: $remote_addr
remote_user: $remote_user
request_uri: $request_uri
uri: $uri
args: $args
host: $host
http_host: $http_host
scheme: $scheme
request_method: $request_method
http_user_agent: $http_user_agent
http_x_forwarded_for: $http_x_forwarded_for
http_x_real_ip: $http_x_real_ip
proxy_add_x_forwarded_for: $proxy_add_x_forwarded_for
";
}
访问 /debug/ 可直观看到各变量在多级代理下的值。
17. 生产最佳实践与故障排查
17.1 架构选型 checklist
- 预估 QPS、并发连接数、带宽
- 确定四 / 七层需求(URL 路由?SSL?)
- 选 LVS / Nginx / HAProxy 组合
- Director/LB 高可用(Keepalived 主备)
- RS 健康检查与自动剔除
- 监控:连接数、QPS、延迟、5xx 率
- 真实 IP 传递方案
- 灰度与扩容流程
17.2 常见问题排查
| 现象 | 可能原因 | 排查命令 |
|---|---|---|
| VIP 无法访问 | VIP 未绑定、防火墙、ARP | ip addr, iptables -L, arp -n |
| 部分 RS 无流量 | 权重为 0、被剔除、算法 | ipvsadm -Ln --stats |
| DR 模式 RS 收不到包 | lo 未绑 VIP、ARP 未抑制 | ip addr show lo, sysctl arp_ignore |
| NAT 模式无响应 | RS 网关错误、ip_forward 未开 | ip route, sysctl ip_forward |
| 连接超时 | 后端服务 down、防火墙 | curl, telnet, tcpdump |
| 脑裂 | VRRP 配置不一致、网络分区 | journalctl -u keepalived |
| 502 Bad Gateway | 后端无响应、upstream 全挂 | nginx -t, 查 error.log |
| 真实 IP 不对 | Header 未传或未信任 | 查 $remote_addr, realip 配置 |
17.3 监控指标
LVS:
# 连接数、包数
ipvsadm -Ln --stats
# 网卡流量
sar -n DEV 1
# 连接跟踪表
cat /proc/net/ip_conntrack | wc -l
Nginx:
# stub_status 模块
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
Keepalived:
systemctl status keepalived
journalctl -u keepalived -f
17.4 安全建议
- Director 仅开放必要端口,禁止 RS 对 VIP 的 direct 访问(DR 除外 lo)
- Keepalived 认证密码强度足够
- 限制
set_real_ip_from信任范围 - 定期更新 Nginx、Keepalived 版本
- LB 层可配合 DDoS 防护、限流
17.5 总结:技术选型速查
┌─────────────────────────────────────────────────────────────────┐
│ 负载均衡选型速查 │
├─────────────────────────────────────────────────────────────────┤
│ 百万并发、纯四层 → LVS (DR) + Keepalived │
│ Web 服务、需 SSL/路由 → Nginx (七层) │
│ TCP 服务、中等规模 → Nginx stream 或 HAProxy │
│ MySQL 读负载 → LVS-DR + sh/wlc 或 Nginx stream │
│ 超大入口 + 灵活七层 → LVS (L4) → Nginx (L7) → App │
│ 专业 LB、精细健康检查 → HAProxy │
└─────────────────────────────────────────────────────────────────┘