LVS 与 Nginx 负载均衡:从原理到生产实战

1. 负载均衡概述:为什么需要它

1.1 单机服务的瓶颈

在互联网早期,一台服务器足以支撑一个网站。随着用户量增长,单机面临以下问题:

问题 表现 后果
性能瓶颈 CPU、内存、磁盘 I/O、网络带宽达到上限 响应变慢,超时增多
单点故障 服务器宕机、网卡故障、机房断电 服务完全不可用
扩展困难 垂直扩容(加 CPU / 内存)成本高且有上限 无法应对流量洪峰

负载均衡(Load Balancing) 的核心思想是:将大量并发请求分散到多台后端服务器上,使每台机器只处理一部分流量,从而提升整体吞吐能力、降低单点风险。

1.2 负载均衡要解决的三件事

复制代码
                    ┌─────────────────┐
   客户端请求 ──────►│   负载均衡器     │
                    │  (Director/LB)  │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
         ┌─────────┐   ┌─────────┐   ┌─────────┐
         │ Real 1  │   │ Real 2  │   │ Real 3  │
         └─────────┘   └─────────┘   └─────────┘
  1. 分发(Distribution):按某种算法将请求分配到后端节点
  2. 高可用(High Availability):某节点故障时自动剔除、流量转移
  3. 扩展(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 的优势:

  1. 性能:内核转发,无用户态 / 内核态频繁切换,单机可支撑百万连接
  2. 稳定:久经大规模生产验证(阿里、百度、腾讯等)
  3. 低开销:Director 只做调度,不终止连接(DR/TUN 模式)
  4. 适配广:支持 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
  • 跨机房、跨地域 → TUNFullNAT
  • 云环境、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 的网关,对数据包做两次地址转换:

  1. 入向(DNAT)CIP:Cport → VIP:Vport 变为 CIP:Cport → RIP:Rport
  2. 出向(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)模式下:

  1. Client 请求 CIP → VIP,到达 Director
  2. Director 根据调度算法选择 RS,只修改二层 MAC,将包转发给 RS
  3. RS 的 lo 接口上配置了 VIP,因此认为该包是发给自己的
  4. 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 混乱。

预防措施:

  1. 使用 vrrp_script 检测本机服务,异常时降低 priority
  2. 开启 vrrp_strict(新版本默认)
  3. 使用 unicast_src_ip / unicast_peer 单播 VRRP,减少广播干扰
  4. 防火墙放行 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 负载均衡注意事项

  1. 主从延迟:读从库可能读到旧数据,需业务容忍或读主
  2. 连接池:应用连接池应对 VIP,而非单个 RS IP
  3. 故障切换 :Keepalived TCP_CHECK 只能检测端口,建议配合 mysql_check 脚本
  4. 半同步 / 组复制:与 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_protocolTOA 模块传递。

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 安全建议

  1. Director 仅开放必要端口,禁止 RS 对 VIP 的 direct 访问(DR 除外 lo)
  2. Keepalived 认证密码强度足够
  3. 限制 set_real_ip_from 信任范围
  4. 定期更新 Nginx、Keepalived 版本
  5. 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                             │
└─────────────────────────────────────────────────────────────────┘

0voice · GitHub

相关推荐
风曦Kisaki1 小时前
# Linux运维Day04:集群与 LVS 负载均衡(LVS-NAT 集群,LVS-DR 集群)
linux·运维·lvs
Shadow(⊙o⊙)1 小时前
Linux基础IO-1.0——open、close、read及write-深入手搓分析!
linux·运维·服务器·开发语言·c++·学习
yyuuuzz1 小时前
境外云服务器使用常见问题梳理
运维·服务器·网络·aws
小饼干在学嘎瓦1 小时前
HTTP和RPC有什么区别?好奇怪的问题!
网络协议·http·rpc
仍然.1 小时前
网络层IP协议
服务器·网络协议·tcp/ip
暗夜猎手-大魔王1 小时前
转载--Hermes Agent 02 | 模型无关的秘密:200+ 模型的统一接入层
网络
会编程的土豆1 小时前
Docker 里面的镜像(Image)和容器(Container)到底是什么
运维·docker·容器
s_w.h1 小时前
【 linux 】进程的调度算法
linux·运维·服务器
c++逐梦人1 小时前
多路转接epoll
linux·网络·epoll