Nginx反向代理配置实例

web03服务器,配置一个网站,监听在8080
[root@web03 ~]# cd /etc/nginx/conf.d/
[root@web03 conf.d]# vim web.conf
server {
listen 8080;
server_name localhost;
location / {
root /code/8080;
index index.html;
allow all;
}
}
[root@web03 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web03 conf.d]# systemctl restart nginx
[root@web03 ~]# mkdir -p /code/8080
[root@web03 ~]# echo "listening 8080 ..." > /code/8080/index.html
- proxy代理服务,配置监听80端口,使能够通过代理服务器访问到后端的
192.168.226.134的8080端口站点内容
[root@proxy ~]# cd /etc/nginx/conf.d/
[root@proxy conf.d]# vim proxy_web_node1.conf
server {
listen 80;
server_name proxy.test.com;
location / {
proxy_pass http://192.168.226.134:8080;
}
}
[root@proxy conf.d]# nginx -t
[root@proxy conf.d]# systemctl restart nginx
- 存在的问题,通过抓包可以看到客户端是使用域名对网站进行访问的,但是代理却是使用的
IP地址加端口号 - 当访问80端口的时候,没有域名的情况下,默认会去找排在最上面的那个配置文件。
- 所以我们需要解决这个问题,保留住最开始的请求头部信息。
架构说明
text
客户端服务器 (192.168.226.128)
↓ 访问 http://proxy.test.com (域名)
↓ hosts: 192.168.226.132 proxy.test.com
代理服务器 (192.168.226.132)
↓ 监听 80端口
↓ proxy_pass http://192.168.226.134:8080
↓ 转发请求(保留原始头信息)
后端Web服务器 (192.168.226.134)
↓ 监听 8080端口
↓ 返回 "listening 8080 ..."
实践
- 后端web服务器

- 中间代理服务器proxy_server

- 后端业务处理服务器

1. 客户端服务器(192.168.226.128)
角色:访问发起方 / 用户端
职责:
- 发起 HTTP 请求,目标域名是
http://proxy.test.com - 通过本地
/etc/hosts文件,将域名proxy.test.com解析为代理服务器的IP192.168.226.132 - 不参与任何
Nginx配置,纯粹是”用户”角色
简单理解:就是”用户”或”浏览器”
2. 代理服务器(192.168.226.132)
角色:反向代理 / 入口网关
职责:
- 监听 80 端口,接收来自客户端的所有请求
- 域名匹配:识别
server_name proxy.test.com,处理该域名的请求 - 转发请求:通过
proxy_pass将请求转发给后端服务器http://192.168.226.134:8080 - 保留原始请求信息:通过
proxy_set_header将客户端的真实IP、域名等信息传递给后端,让后端知道请求来自谁 - 负载均衡(可选):如果后端有多个服务器,可以在此做分发
简单理解:就是”前台”或”门卫”,所有请求先到它这里,再由它转交给真正干活的人
关键配置:
nginx
server {
listen 80;
server_name proxy.test.com;
location / {
proxy_pass http://192.168.226.134:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
3. 后端 Web 服务器(192.168.226.134)
角色:真正的业务处理 / 应用服务器
职责:
- 监听 8080 端口(避免和代理服务器的 80 端口冲突)
- 提供业务内容:返回静态页面、处理业务逻辑、读取数据库等
- 接收代理转发来的请求,处理完成后将结果返回给代理服务器,由代理服务器再返回给客户端
- 可以配置多个,组成集群,由代理服务器做负载均衡
简单理解:就是”后台员工”或”真正干活的”,负责提供实际内容
关键配置:
nginx
server {
listen 8080;
server_name localhost;
root /code/8080;
index index.html;
}
架构原理
1. 代理服务器(132)标准配置
这是你作为入口网关的完整配置,每一条都有具体作用:
nginx
server {
# 1. 监听端口
listen 80; # 监听所有网卡的80端口(不要写127.0.0.1,否则外部访问不了)
server_name proxy.test.com; # 匹配客户端请求的域名
location / {
# 2. 转发目标(指向后端)
proxy_pass http://192.168.226.134:8080;
# 3. 【核心】保留原始域名(解决多站点路由问题)
proxy_set_header Host $host;
# 4. 【核心】传递真实客户端IP(解决日志溯源问题)
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 5. 【核心】启用HTTP/1.1长连接(解决高并发性能问题)
proxy_http_version 1.1;
proxy_set_header Connection ""; # 清除默认的"close",启用Keep-Alive
# 6. 【可选】传递原始协议(如果后端需要区分http/https)
proxy_set_header X-Forwarded-Proto $scheme;
# 7. 【可选】超时控制(防止后端响应慢导致连接断开)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 8. 【可选】缓冲控制(平衡性能与实时性)
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
}
2. 后端服务器(134)标准配置
这是真正提供内容的服务器配置:
nginx
server {
listen 8080; # 监听8080端口(与代理的proxy_pass一致)
server_name localhost; # 这里实际接收的是代理传来的Host头
root /code/8080; # 网站根目录
index index.html index.htm; # 默认首页文件
location / {
# 允许所有访问(如果内网环境)
allow all;
}
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
}
3. 客户端(128)配置
客户端只需要做好域名解析即可,无需配置Nginx:
bash
# 编辑 /etc/hosts,添加:
192.168.226.132 proxy.test.com
4. 完整请求流程(配图理解)
text
客户端(128)
↓ 访问 http://proxy.test.com
↓ /etc/hosts 解析 → 192.168.226.132
↓
代理服务器(132)
↓ listen 80; server_name proxy.test.com;
↓ proxy_pass → http://192.168.226.134:8080
↓ 传递:Host, X-Real-IP, X-Forwarded-For
↓ 使用:HTTP/1.1 长连接
↓
后端服务器(134)
↓ listen 8080;
↓ 根据 Host: proxy.test.com 匹配站点
↓ 记录日志:$http_x_forwarded_for = 真实客户端IP
↓
返回 "This is the server3 web" ✅
Nginx 反向代理高并发配置总结
核心配置
nginx
proxy_http_version 1.1;
proxy_set_header Connection "";
为什么要加?
| 对比项 | 默认(HTTP/1.0) | 加配置后(HTTP/1.1) |
|---|---|---|
| 连接方式 | 短连接(一次请求一建一断) | 长连接(Keep-Alive,复用连接) |
| 高并发后果 | 端口快速耗尽,大量 TIME_WAIT,新请求进不来 | 几十个固定连接反复使用,稳定高效 |
| 资源消耗 | 耗 CPU(频繁握手/挥手) | 耗少量内存,节省 CPU 和时间 |
为什么要配两行?
proxy_http_version 1.1;→ 告诉Nginx用 HTTP/1.1 协议向后端发请求proxy_set_header Connection "";→ 删掉默认的Connection: close,让连接保持不断开
缺一不可:只写第一行不写第二行,后端收到 Connection: close,长连接不会生效。
什么时候必须加?
- ✅ 生产环境 / 高并发场景 → 必须加

Nginx负载均衡
Nginx负载均衡基本概述
为什么要使用负载均衡
- 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用
Nginx负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾

四层和七层负载均衡
一、Web服务 → 为什么选Nginx(七层)?
场景举例
- 用户访问
http://proxy.test.com - 需要根据不同的URL转发到不同的后端(
/api→API服务器,/static→ 静态资源服务器) - 需要保持用户的会话(同一个用户的请求始终落到同一台服务器)
- 需要记录客户端的真实
IP
为什么Nginx能做这些?
Nginx是七层负载均衡,它会完整地接收客户端的HTTP请求,拆开”信封”,读取里面的内容:
text
GET /api/user HTTP/1.1
Host: proxy.test.com ← Nginx能读到这个域名
Cookie: session_id=abc123 ← Nginx能读到这个Cookie
User-Agent: curl/7.76.1
基于这些信息,Nginx可以做:
| 功能 | 怎么实现 | 为什么四层做不了 |
|---|---|---|
| 域名路由 | 根据 Host: proxy.test.com 转发到对应的后端 |
四层看不到域名 |
| URL路径匹配 | location /api/ 转发到API服务器,location /static/ 转发到静态服务器 |
四层看不到URL |
| 会话保持 | 根据Cookie里的 session_id 把同一用户固定到同一台后端 |
四层看不到Cookie |
| Header改写 | 添加 X-Forwarded-For 传递真实IP |
四层无法修改HTTP头 |
| 重定向/rewrite | 根据业务规则重写URL | 四层无法操作应用层数据 |
选型结论
只要你的业务逻辑和HTTP协议相关(域名、URL、Cookie、Header),就必须用七层负载均衡(
Nginx)。
二、数据库/端口转发 → 为什么选LVS(四层)?
场景举例
MySQL数据库集群:多个MySQL实例,需要把请求分发给不同的数据库服务器- Redis缓存集群:多个Redis节点,需要做读写分离
- 游戏服务器:基于TCP长连接,转发玩家的游戏数据包
- 端口映射:把公网的某个端口转发到内网某台机器的指定端口
为什么选LVS?
数据库和游戏服务器使用的是TCP协议,不是HTTP协议,它们的数据格式是自定义的二进制协议,没有HTTP头这种概念。
text
MySQL协议数据包:
[包头][包体] ← 全是二进制,没有"域名"或"URL"这个字段
所以用Nginx(七层)来处理MySQL流量是无效的,因为Nginx只能解析HTTP协议,看不懂MySQL协议。
LVS(四层) 的做法是:
只改
IP和端口,不碰数据包内容。
text
客户端(128) → 访问 192.168.226.10:3306 (VIP)
LVS(四层) → 把目标IP改成 192.168.226.20:3306 (后端MySQL)
数据包内容 → 原封不动,MySQL协议数据完全保留
为什么LVS效率高?
| 对比项 | LVS(四层) |
Nginx(七层) |
|---|---|---|
| 拆包 | 不拆,只看IP+端口 |
拆,读HTTP头 |
| CPU消耗 | 极低 | 较高 |
| 延迟 | 微秒级 | 毫秒级 |
| 吞吐量 | 非常高 | 中等 |
选型结论
只要你的流量不是HTTP协议(如
MySQL、Redis、游戏),就用四层负载均衡(LVS/HAProxy的tcp模式)。
三、实际生产架构:四层 + 七层混合使用
真正的大规模系统,通常四层和七层配合使用,各司其职:
text
互联网流量
↓
【四层负载均衡 LVS】(扛住千万级并发)
↓ 只做IP+端口转发,不拆包,效率极高
↓
【七层负载均衡 Nginx 集群】(多台)
↓ 拆包,读域名/URL/Cookie
↓
【后端应用服务器】(Tomcat、PHP、Node.js)
分工:
LVS(四层):站在最前面,负责扛住大流量,把请求分发给后端的多台Nginx。Nginx(七层):负责做智能路由(按域名/URL分发),处理会话保持,传递真实IP。- 后端服务器:真正执行业务逻辑。
四、一句话总结
| 场景 | 用什么 | 核心原因 |
|---|---|---|
| Web服务(HTTP) | Nginx(七层) |
需要读域名、URL、Cookie来做智能转发 |
| 数据库/端口转发(TCP) | LVS(四层) |
看不懂HTTP协议,只需要快速转包,效率最高 |
| 大规模生产 | LVS+ Nginx混合 |
四层扛流量,七层做路由,各取所长 |
记住:七层能干四层的活(但没必要,效率低),四层干不了七层的活(因为看不懂HTTP)。这就是为什么Web服务必须用七层,而数据库转发用四层就够了。
Nginx upstream虚拟配置语法
Syntax: upstream name { ... }
Default: -
Context: http
#upstream例
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.example.com:8080 backup;
}
server {
location / {
proxy_pass http://backend;
}
}
Web01服务器上配置Nginx
[root@web01 ~]# cd /etc/nginx/conf.d/
[root@web01 conf.d]# vim node.conf
server {
listen 80;
server_name node.test.com;
location / {
root /code/node;
index index.html;
}
}
[root@web01 conf.d]# mkdir -p /code/node
[root@web01 conf.d]# echo "web01 ..." > /code/node/index.html
[root@web01 conf.d]# systemctl restart nginx
# 关闭防火墙和SElinux
[root@web02 conf.d]# setenforce 0
[root@web02 conf.d]# systemctl stop firewalld
Web02服务器上配置Nginx
[root@web02 ~]# cd /etc/nginx/conf.d/
[root@web02 conf.d]# vim node.conf
server {
listen 80;
server_name node.test.com;
location / {
root /code/node;
index index.html;
}
}
[root@web02 conf.d]# mkdir -p /code/node
[root@web02 conf.d]# echo "web02 ..." > /code/node/index.html
[root@web02 conf.d]# systemctl restart nginx
# 关闭防火墙和SElinux
[root@web02 conf.d]# setenforce 0
[root@web02 conf.d]# systemctl stop firewalld
配置Nginx负载均衡
[root@lb01 ~]# cd /etc/nginx/conf.d/
[root@lb01 conf.d]# vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
[root@lb01 conf.d]# vim node_proxy.conf
upstream node {
server 192.168.226.20:80;
server 192.168.226.30:80;
}
server {
listen 80;
server_name node.test.com;
location / {
proxy_pass http://node;
include proxy_params;
}
}
[root@lb01 conf.d]# nginx -t
[root@lb01 conf.d]# systemctl restart nginx
因为 upstream node { ... } 这个块定义了 node 这个名字,所以当你在 proxy_pass http://node; 里写 node 时,Nginx 就知道你要找的是这个 upstream 块里定义的服务器列表。
详细解释:node 是怎么变成后端地址的?
第一步:你定义了 upstream 块
nginx
upstream node { # ← 这里定义了一个"上游服务器组",名字叫 node
server 192.168.226.20:80; # 后端服务器1
server 192.168.226.30:80; # 后端服务器2
}
upstream 是 Nginx 的一个指令,作用是定义一个后端服务器组,并给它起个名字(这里叫 node)。
第二步:proxy_pass 引用这个名字
nginx
location / {
proxy_pass http://node; # ← 这里的 node 就是上面定义的 upstream 名字
}
当Nginx 看到 proxy_pass http://node; 时,它会:
- 去查找有没有定义过名叫
node的upstream块 - 找到了,就按照
upstream node里定义的服务器列表去转发 - 按照负载均衡算法(默认轮询),选中一台后端服务器
- 把请求转发给
192.168.226.20:80或192.168.226.30:80
类比理解
| 概念 | 类比 |
|---|---|
upstream node { ... } |
你手机通讯录里存了一个联系人,名字叫”老王”,里面存了老王的手机号 |
proxy_pass http://node; |
你发微信说”转发给老王” |
实际转发到 192.168.226.20:80 |
微信后台查到”老王”的手机号,把消息发过去了 |
所以 node 只是一个别名,Nginx会根据这个别名去 upstream 块里找到真正的 IP 地址。
如果你不写 upstream 会怎样?
你也可以直接在 proxy_pass 里写 IP:
nginx
location / {
proxy_pass http://192.168.226.20:80; # 不经过 upstream,直接写死 IP
}
这样就不用定义 upstream node 了。但这样写的问题是:
- 只能转发给一台后端服务器,做不了负载均衡
- 如果后端服务器
IP变了,需要手动改配置
所以生产环境都推荐用 upstream 方式,方便扩展和管理。
总结
node不是自动生效的,是因为你在upstream node里定义了它,Nginx才知道node代表什么。这是一种”先定义、后引用”的配置逻辑。
| 配置 | 作用 |
|---|---|
upstream node { server 20:80; server 30:80; } |
定义:把 20 和 30 这两台机器打包,起名叫 node |
proxy_pass http://node; |
引用:使用上面定义的 node 组进行转发 |
实践
- 代理服务器

Nginx 的 include 指令支持相对路径,相对的是 Nginx 的主配置文件所在目录,也就是 /etc/nginx/。
路径解析规则
| 写法 | Nginx 实际查找路径 |
|---|---|
include proxy_params; |
/etc/nginx/proxy_params |
include conf.d/node_proxy.conf; |
/etc/nginx/conf.d/node_proxy.conf |
include /etc/nginx/proxy_params; |
/etc/nginx/proxy_params(绝对路径) |
所以你在 /etc/nginx/conf.d/ 目录下写 include proxy_params;,Nginx并不会去 conf.d/ 里找,而是回到 /etc/nginx/ 目录去找这个文件。

- 后端web服务器

- 客户端,域名轮询访问

Nginx负载均衡调度算法
| 调度算法 | 概述 |
|---|---|
| 轮询 | 按时间顺序逐一分配到不同的后端服务器(默认) |
| weight | 加权轮询,weight值越大,分配到的访问几率越高 |
ip_hash |
每个请求按访问IP的hash结果分配,这样来自同一IP的固定访问一个后端服务器 |
url_hash |
按照访问URL的hash结果来分配请求,是每个URL定向到同一个后端服务器 |
| least_conn | 最少链接数,那个机器链接数少就分发 |
1. RR – 轮询(Round-Robin)
默认算法,请求按顺序轮流分发到后端服务器。
nginx
upstream load_pass {
server 192.168.226.10:80;
server 192.168.226.20:80;
}
特点:
- 每个请求按时间顺序逐一分配到不同后端服务器
- 适合后端服务器性能一致的场景
- 无需额外配置
2. WR – 权重轮询(Weighted Round-Robin)
通过 weight 参数分配权重,权重越高分配到的请求越多。
nginx
upstream load_pass {
server 192.168.226.10:80 weight=5; # 权重5,处理5份请求
server 192.168.226.20:80; # 默认权重1,处理1份请求
}
特点:
- 权重比例 = 5:1,每6次请求中5次去web01,1次去web02
- 适合服务器性能不同的场景
- 高性能服务器设置更高的权重
3. ip_hash – IP哈希
根据客户端IP地址进行哈希计算,确保同一客户端IP始终访问同一台后端服务器。
nginx
upstream load_pass {
ip_hash;
server 192.168.226.10:80;
server 192.168.226.20:80;
}
特点:
- 保持会话(Session)一致性
- 适合有状态的应用(如购物车、登录状态)
- 不能与 weight 一起使用
- 如果所有客户端经过同一个代理访问,ip_hash 看到的是代理IP,会导致所有请求转发到同一台后端服务器
注意事项:
nginx
# ❌ 错误:ip_hash 不能和 weight 一起使用
upstream load_pass {
ip_hash;
server 192.168.226.10:80 weight=5; # 会报错!
server 192.168.226.20:80;
}
# ✅ 正确:ip_hash 单独使用
upstream load_pass {
ip_hash;
server 192.168.226.10:80;
server 192.168.226.20:80;
}
4. least_conn – 最少连接
将请求分发给当前活动连接数最少的服务器。
nginx
upstream load_pass {
least_conn;
server 192.168.226.10:80;
server 192.168.226.20:80;
}
特点:
- 适合长连接场景(如WebSocket、数据库连接)
- 避免请求堆积在某台服务器上
5. fair – 响应时间优先(第三方模块)
根据后端服务器的响应时间来分配请求,响应时间短的优先。
nginx
upstream load_pass {
fair;
server 192.168.226.10:80;
server 192.168.226.20:80;
}
特点:
- 需要安装第三方模块
- 动态调整,响应快的服务器处理更多请求
二、服务器状态参数
nginx
upstream load_pass {
server 192.168.226.10:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.226.20:80 weight=2;
server 192.168.226.30:80 backup;
server 192.168.226.40:80 down;
}
1. weight(权重)
nginx
server 192.168.226.10:80 weight=5;
说明:
- 默认值为1
- 权重值越高,分配到的请求越多
- 适合后端服务器硬件配置不同的场景
2. max_fails(最大失败次数)
nginx
server 192.168.226.10:80 max_fails=3;
正确理解:
- 在
fail_timeout指定的时间窗口内,如果失败次数达到max_fails次,nginx 就认为该服务器不可用 - 失败包括:连接超时、读取超时、后端返回 5xx 错误等
- 默认值为 1,即一次失败就标记为不可用
3. fail_timeout(失败超时时间)
nginx
server 192.168.226.10:80 max_fails=3 fail_timeout=30s;
正确理解:
fail_timeout有两个作用:- 失败统计的时间窗口:在 30s 内如果失败3次,标记该服务器为不可用
- 标记不可用的持续时间:被标记为不可用后,nginx 会在 30s 后再次尝试连接该服务器
工作流程:
text
时间轴:
0s -------- 10s -------- 20s -------- 30s -------- 40s -------- 50s
| | | | | |
请求1失败 请求2失败 请求3失败 标记不可用 尝试恢复 恢复成功
(30s内3次失败)
(30s后自动尝试)
4. backup(备份服务器)
nginx
server 192.168.226.30:80 backup;
正确理解:
- 只有当所有主服务器都不可用时,才会将请求转发给 backup 服务器
- backup 服务器平时不处理请求,只作为备用
- 用于高可用保障
5. down(下线标记)
nginx
server 192.168.226.40:80 down;
正确理解:
- 永久标记该服务器为不可用
- nginx 不会向该服务器转发任何请求
- 常用于服务器维护或升级时,优雅地移除服务器
- 需要恢复时,手动注释掉
down或删除该行
三、参数对照表
| 参数 | 作用 | 默认值 | 注意事项 |
|---|---|---|---|
weight |
权重,值越大分配请求越多 | 1 | |
max_fails |
fail_timeout 时间内允许的最大失败次数 | 1 | 达到后标记为不可用 |
fail_timeout |
① 失败统计时间窗口 ② 标记不可用的持续时间 | 10s | 两个作用合并在一个参数中 |
backup |
备份服务器,主服务器全挂时启用 | 无 | |
down |
永久标记为不可用 | 无 | 用于维护场景 |
max_conns |
最大并发连接数 | 0(无限制) | 需要商业版 |
resolve |
动态解析域名 | 无 | 后端使用域名时配置 |
四、配置场景选择
| 场景 | 推荐算法 | 原因 |
|---|---|---|
无状态应用(API、静态资源) |
RR 轮询 | 简单、均衡、无需额外配置 |
| 服务器性能不同 | WR 权重轮询 | 高性能服务器处理更多请求 |
| 需要会话保持(登录状态、购物车) | ip_hash | 同一用户始终访问同一后端 |
| 长连接、WebSocket 场景 | least_conn | 避免连接集中在某台服务器 |
| 响应时间敏感场景 | fair | 响应快的服务器优先处理 |
五、常见问题
Q1:ip_hash 计算的是代理服务器的IP吗?
A:默认计算 $remote_addr(客户端真实IP)。但如果客户端经过代理访问,nginx看到的是代理服务器的IP,会导致所有请求都转发到同一台后端服务器。
解决方案:
nginx
# 获取真实客户端IP
set_real_ip_from 代理服务器IP段;
real_ip_header X-Forwarded-For;
upstream load_pass {
ip_hash;
server 192.168.226.10:80;
server 192.168.226.20:80;
}
Q2:max_fails 和 fail_timeout 如何配合工作?
A:在 fail_timeout 时间内,如果失败次数达到 max_fails 次,nginx 标记该服务器为不可用。经过 fail_timeout 时间后,nginx 会再次尝试连接。
text
示例配置:max_fails=3 fail_timeout=30s
1. 30s 内失败3次 → 标记不可用
2. 30s 后自动尝试恢复连接
3. 恢复成功 → 重新加入负载均衡池
4. 恢复失败 → 继续标记为不可用,等待下一个30s
Q3:backup 和 down 的区别?
A:
| 参数 | 状态 | 何时接收请求 | 使用场景 |
|---|---|---|---|
backup |
动态 | 主服务器全部不可用时 | 高可用保障 |
down |
永久 | 永不接收请求 | 服务器维护 |
六、完整配置示例
nginx
upstream node {
# 主服务器:权重5,失败3次标记不可用,30s后重试
server 192.168.226.10:80 weight=5 max_fails=3 fail_timeout=30s;
# 主服务器:默认权重1,失败3次标记不可用,60s后重试
server 192.168.226.20:80 max_fails=3 fail_timeout=60s;
# 备份服务器:主服务器全挂时启用
server 192.168.226.30:80 backup;
# 下线服务器:正在维护中
server 192.168.226.40:80 down;
}
server {
listen 80;
server_name node.test.com;
location / {
proxy_pass http://node;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
upstream 配置位置
text
客户端 (192.168.226.40)
↓ 访问 http://node.test.com
↓
【代理服务器/负载均衡器】 (192.168.226.10) ← 在这里配置 upstream!
↓ /etc/nginx/conf.d/node.conf
↓ upstream node { ... }
├──→ 后端1 (192.168.226.20:80) ← 被代理的服务器
└──→ 后端2 (192.168.226.30:80) ← 被代理的服务器
Nginx 负载均衡会话保持详解
一、什么是会话保持(Session Stickiness)?
在负载均衡环境下,用户的请求会被分发到不同的后端服务器。
text
用户A → 负载均衡器 → 第1次请求 → web01(登录成功,session保存在web01)
用户A → 负载均衡器 → 第2次请求 → web02(web02没有用户的session,需要重新登录!)❌
会话保持的目的:确保同一个用户的请求始终被转发到同一台后端服务器,或者多台服务器共享session数据。
二、Session 和 Cookie 的区别
1. Cookie(客户端存储)
text
浏览器(客户端)
↓ 存储位置:浏览器本地
↓ 存储内容:用户标识信息(如 session_id)
↓ 特点:
- 大小限制:一般 4KB 左右
- 自动发送:每次请求会自动携带
- 安全性:数据可见,可被篡改(需加密)
Cookie 是钥匙,Session 是保险柜。
2. Session(服务端存储)
text
服务器
↓ 存储位置:服务器内存/文件/数据库
↓ 存储内容:用户实际数据(用户名、权限、购物车等)
↓ 特点:
- 安全:数据存储在服务端,用户无法篡改
- 灵活:可以存储任意类型的数据
- 生命周期:由服务器控制
3. 两者关系
text
用户登录流程:
1. 用户输入账号密码 → 提交到服务器
2. 服务器验证通过 → 创建 Session(存储用户信息)
3. 服务器生成 Session ID → 通过 Cookie 返回给浏览器
4. 浏览器保存 Cookie(包含 Session ID)
5. 后续请求 → 浏览器自动携带 Cookie
6. 服务器根据 Session ID → 找到对应的 Session 数据
text
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 浏览器 │ │ 负载均衡器 │ │ web01 │
│ │ │ │ │ │
│ Cookie │ ──→ │ 分发请求 │ ──→ │ Session │
│ session_id │ │ │ │ 用户数据 │
│ = abc123 │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
↓
同一个用户
session_id=abc123


实践会话保持
纯逻辑架构图(服务依赖关系)
text
【用户请求】
|
v
┌─────────────────┐
│ Nginx │ ← 静态资源(图片/CSS/JS)直接返回
│ (HTTP入口) │ ← 动态请求转发给 PHP-FPM
└────────┬────────┘
│ (FastCGI 协议)
v
┌─────────────────┐
│ PHP-FPM │ ← 执行网站业务代码
│ (应用运行时) │ ← phpMyAdmin 也运行在这里(管理工具)
└────────┬────────┘
│
┌────────────────┼────────────────┐
│ │ │
v v v
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Session │ │ 业务缓存 │ │ 持久化 │
│ (会话) │ │ (查询缓存)│ │ 数据存储 │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
v
┌─────────────────┐
│ Redis │ ← 缓存层(临时存储)
│ (内存数据库) │ ← 存储:登录态、验证码、热点数据
└─────────────────┘
│
│(缓存未命中时回源)
v
┌─────────────────┐
│ MySQL │ ← 持久化层(永久存储)
│ (关系型数据库) │ ← 存储:用户表、订单表、商品表
└─────────────────┘
准备工作安装Lnmp架构环境
在LNMP架构中为Nginx服务器(服务器A)安装PHP运行环境和扩展的完整命令
dnf install -y epel-release
dnf groupinstall "Development Tools" -y
dnf install -y wget gcc make libxml2-devel openssl-devel sqlite-devel libcurl-devel libpng-devel libjpeg-devel oniguruma libzip-devel
dnf install -y php php-cli php-fpm php-mysqlnd php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-intl
dnf install -y php-pecl-redis
php -m | grep redis
架构图
text
┌─────────────────────────────────────────────────────────────────────────┐
│ 【内网环境 192.168.226.0/24】 │
│ │
│ ┌─────────────────────┐ ┌──────────────────────────────────────┐ │
│ │ 【数据库服务器】 │ │ 【Web服务器集群】 │ │
│ │ 192.168.226.10 │ │ │ │
│ │ │ │ ┌────────────────────────────┐ │ │
│ │ ┌─────────────┐ │ │ │ Web服务器1 │ │ │
│ │ │ MariaDB │ │ │ │ 192.168.226.20 │ │ │
│ │ │ 端口:3306 │◄──┼────┼───│ Nginx + PHP-FPM │ │ │
│ │ └─────────────┘ │ │ │ phpMyAdmin │ │ │
│ │ │ │ └────────────────────────────┘ │ │
│ │ 远程连接账号: │ │ │ │
│ │ root@'192.168. │ │ ┌────────────────────────────┐ │ │
│ │ 226.%' │ │ │ Web服务器2 │ │ │
│ │ 密码:123456 │ │ │ 192.168.226.30 │ │ │
│ └─────────────────────┘ │ │ Nginx + PHP-FPM │ │ │
│ │ │ phpMyAdmin │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │
│ 数据流向:Web服务器(20/30) ---> 远程连接 ---> 数据库服务器(10):3306 │
│ 访问入口:用户 ---> Web服务器(20或30) ---> phpMyAdmin管理数据库 │
└─────────────────────────────────────────────────────────────────────────┘
- 在10服务器上部署
mariadb数据库,提供phpmyadmin访问
# 安装mariadb数据库软件
yum install mariadb-server mariadb -y
# 启动数据库并且设置开机自启动
systemctl start mariadb
systemctl enable mariadb
# 设置mariadb的密码
mysqladmin password '123456'
# 验证数据库是否工作正常
mysql -uroot -p123456 -e "show databases;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
# 创建访问用户
grant all privileges on *.* to root@'192.168.226.%' identified by '123456' with grant option;
flush privileges;
部署LNMP
[root@server2 ~]# yum install nginx -y
[root@server2 ~]# vim /etc/php-fpm.d/www.conf
listen = 9000
[root@server2 ~]# sed -i '/^user/c user = nginx' /etc/php-fpm.d/www.conf
[root@server2 ~]# sed -i '/^group/c group = nginx' /etc/php-fpm.d/www.conf
[root@server2 ~]# systemctl restart php-fpm.service
[root@server2 ~]# ss -tnl
[root@server2 ~]# cd /etc/nginx/conf.d/
[root@server2 ~]# vim php.conf
server {
listen 80;
server_name php.test.com;
root /code/phpmyadmin;
location / {
index index.php index.html;
}
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
[root@server2 ~]# nginx -t
[root@server2 ~]# nginx -s reload
[root@server2 ~]# mkdir /code
[root@server2 ~]# cd /code/
[root@server2 ~]# unzip phpMyAdmin-5.1.1-all-languages.zip
[root@server2 ~]# mv phpMyAdmin-5.1.1-all-languages phpmyadmin
[root@server2 ~]# chown -R nginx.nginx /var/lib/php/
[root@server3 ~]# cd /code/phpmyadmin/
[root@server2 phpmyadmin]# cp config.sample.inc.php config.inc.php
[root@server2 phpmyadmin]# vim config.inc.php
$cfg['Servers'][$i]['host'] = '192.168.226.10';
为什么写 listen = 9000?
因为你的 Nginx 配置里写的是:
nginx
fastcgi_pass 127.0.0.1:9000;
这两边必须一致,否则 PHP-FPM 收不到 Nginx 转发的请求。
直观理解
text
Nginx 把请求发到 → 127.0.0.1:9000
↓
PHP-FPM 必须在这个地址等着
↓
所以 PHP-FPM 要写 listen = 9000
如果两边不一致:
| Nginx 转发到 | PHP-FPM 监听 | 结果 |
|---|---|---|
127.0.0.1:9000 |
listen = 9000 |
✅ 能通信 |
127.0.0.1:9000 |
listen = /run/php-fpm/www.sock |
❌ 找不到,报错 |
因为 phpMyAdmin 需要一个 config.inc.php 才能连接数据库,而官方只提供了一个模板文件 config.sample.inc.php,你需要复制一份再修改。
详细解释
| 文件 | 作用 | 能不能直接改 |
|---|---|---|
config.sample.inc.php |
配置模板,官方提供的示例 | ❌ 不能直接改,改了下次升级会覆盖 |
config.inc.php |
真正的配置文件,phpMyAdmin 实际读取的 | ✅ 复制一份来改 |
第1步:编辑 PHP-FPM配置文件
bash
[root@server2 ~]# vim /etc/php-fpm.d/www.conf
| 部分 | 含义 |
|---|---|
vim |
Vi IMproved,Linux 下最常用的文本编辑器 |
/etc/php-fpm.d/www.conf |
PHP-FPM 的进程池配置文件路径 |
这个文件里配置什么?
listen:PHP-FPM 监听的地址和端口user/group:PHP-FPM 进程的运行用户和组pm:进程管理方式(dynamic/static)pm.max_children:最大子进程数
为什么叫 www.conf?
PHP-FPM支持多个进程池(pool),每个池可以有独立的配置。www.conf 是默认的进程池配置文件。如果你有多个网站,可以复制成 blog.conf、shop.conf 等。
text
/etc/php-fpm.d/ (配置目录)
│
┌─────────┼─────────┬──────────┐
▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐ ┌────────┐
│www.conf│ │blog.conf│ │shop.conf│ │api.conf│ ← 所有 .conf 文件
└──────┘ └──────┘ └──────┘ └────────┘
│ │ │ │
▼ ▼ ▼ ▼
PHP-FPM 自动加载并管理所有这些池
核心机制
PHP-FPM 的加载逻辑
PHP-FPM 主程序启动时,会自动扫描并加载 /etc/php-fpm.d/ 目录下所有以 .conf 结尾的文件。
这个行为由 PHP-FPM 主配置文件决定:
bash
# /etc/php-fpm.conf(主配置文件)
include=/etc/php-fpm.d/*.conf # ← 这行告诉 PHP-FPM:加载这个目录下所有 .conf
每个 .conf 文件 = 一个独立的进程池
| 文件 | 池名称 | 作用 |
|---|---|---|
www.conf |
[www] |
处理网站A的PHP请求 |
blog.conf |
[blog] |
处理网站B的PHP请求 |
shop.conf |
[shop] |
处理网站C的PHP请求 |
api.conf |
[api] |
处理API服务的PHP请求 |
实际网络流程
当用户请求一个 PHP 文件时:
text
用户访问 http://192.168.226.20/index.php
│
▼
┌──────────────────────────────────────────────────────────────┐
│ server2 (192.168.226.20) │
│ │
│ ① Nginx 收到请求(监听 80 端口) │
│ │ │
│ ② 匹配到 location ~ .php$ │
│ │ │
│ ③ fastcgi_pass 127.0.0.1:9000 │
│ │ │
│ └─────▶ 数据包走"回环接口(loopback)" │
│ 不经过网卡,不经过交换机,直接在内存里转发 │
│ 目的地:127.0.0.1:9000(本机) │
│ │ │
│ ④ PHP-FPM 正在 9000 端口等待,接收到请求 │
│ │ │
│ ⑤ PHP-FPM 执行 /code/phpmyadmin/index.php │
│ │ │
│ ⑥ index.php 连接 192.168.226.10:3306(数据库服务器) │
│ │ │
│ ⑦ 数据返回 → Nginx → 用户浏览器 │
text
用户访问 http://php.test.com/index.php
│
▼
┌─────────────────────────────────┐
│ Nginx (本机) │
│ │
│ ① 请求的是 .php 文件 │
│ ↓ │
│ ② 匹配 location ~ .php$ │
│ ↓ │
│ ③ fastcgi_pass 127.0.0.1:9000 │
│ (转发给本机 9000 端口) │
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ PHP-FPM (本机) │
│ │
│ ④ 监听 9000 端口 │
│ ↓ │
│ ⑤ 找到 www.conf 池 │
│ ↓ │
│ ⑥ 执行 index.php │
│ ↓ │
│ ⑦ 连接 192.168.226.10:3306 │
│ (数据库服务器) │
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 数据库服务器 192.168.226.10 │
│ 返回数据给 PHP-FPM │
└─────────────────────────────────┘
│
▼
Nginx → 用户浏览器显示页面
text
用户请求 http://php.test.com/index.php(.php 结尾)
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ① Nginx 收到请求 │
│ 看到是 .php 结尾 → 匹配 location ~ .php$ │
│ │
│ ② location 里配置了 fastcgi_pass 127.0.0.1:9000 │
│ 意思:把这个请求交给本机的 PHP-FPM 管理 │
│ │
│ ③ 安装 Nginx 的时候,同时也装了 php-fpm │
│ (yum install php-fpm) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ④ PHP-FPM 是一个"进程管理器" │
│ ● Master 进程:负责管理下面所有的 Worker 进程 │
│ ● Worker 进程:真正执行 PHP 代码的进程 │
│ │
│ ⑤ www.conf 定义了: │
│ ● listen = 9000(监听 9000 端口) │
│ ● user = nginx(以 nginx 用户身份运行) │
│ ● 这个进程池就是专门处理 phpMyAdmin 请求的 │
│ │
│ ⑥ Worker 进程执行 /code/phpmyadmin/index.php │
│ 也就是执行进程池里的这个 PHP 文件 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ⑦ index.php 里 require 了 config.inc.php │
│ config.inc.php 里配置了: │
│ $cfg['Servers'][$i]['host'] = '192.168.226.10'; │
│ │
│ ⑧ 这个配置的意思是: │
│ "我要连接远程的数据库服务器(192.168.226.10)" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ⑨ PHP 代码执行 SQL 查询(如 SELECT * FROM users) │
│ 通过网络发送到 192.168.226.10:3306 │
│ │
│ ⑩ MySQL 数据库执行查询,把结果集(数据)返回给 PHP │
└─────────────────────────────────────────────────────────────┘
│
▼
PHP 拼成 HTML 页面
│
▼
Nginx 返回给用户浏览器


接入负载均衡
[root@lb01 ~]# vim /etc/nginx/conf.d/proxy_php.com.conf
upstream php {
server 192.168.226.20:80;
server 192.168.226.30:80;
}
server {
listen 80;
server_name php.test.com;
location / {
proxy_pass http://php;
include proxy_params;
}
}
[root@lb01 ~]# nginx -t
[root@lb01 ~]# systemctl restart nginx
- 常用优化配置
- Proxy代理网站常用优化配置如下,将配置写入新文件,调用时使用include引用即可
[root@Nginx ~]# vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;

准备redis内存数据库
[root@server1 ~]# yum -y install redis
[root@server1 ~]# vim /etc/redis/redis.conf
bind 0.0.0.0
[root@server1 ~]# systemctl enable --now redis
Created symlink /etc/systemd/system/multi-user.target.wants/redis.service → /usr/lib/systemd/system/redis.service.
[root@localhost ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 511 0.0.0.0:6379 0.0.0.0:*
LISTEN 0 80 *:3306 *:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 511 [::]:80 [::]:*
bind 参数详解:
| 配置 | 含义 | 谁能连接 |
|---|---|---|
bind 127.0.0.1 |
只监听本机(默认) | 只有本机可以连接 |
bind 0.0.0.0 |
监听所有 IP 地址 | 任何机器都可以连接(需密码保护) |
bind 192.168.1.100 |
只监听指定 IP | 只有该 IP 网段的机器可连 |
为什么要改成 0.0.0.0?
因为你的架构中,Web 服务器(192.168.226.20)需要连接Redis服务器(192.168.226.10),如果不改,Redis只监听本机,其他机器连不上。
安全提醒:bind 0.0.0.0 允许任何人连接,生产环境建议:
- 设置密码(
requirepass) - 用防火墙限制只允许特定
IP访问
[root@server2 ~]# vim /etc/php.ini
[Session]
;session.save_handler = files
session.save_handler = redis
session.save_path = "tcp://192.168.226.10:6379"
;session.save_path = "tcp://192.168.226.10:6379?auth=centos" #如果redis存在密码,则使用该方式
session.auto_start = 1
[root@server2 ~]# vim /etc/php-fpm.d/www.conf
#注释php-fpm.d/www.conf里面的两条内容,否则session内容会一直写入/var/lib/php/session目录中
;php_value[session.save_handler] = files
;php_value[session.save_path] = /var/lib/php/session
[root@server2 conf.d]# systemctl restart php-fpm.service
1. 核心目标:将Session存储从“文件”切换到“Redis”
默认情况下,PHP的Session数据存储在服务器硬盘的文件中(/var/lib/php/session)。你的配置旨在将所有Session数据迁移到Redis内存数据库中,以提升高并发下的读写速度,并方便多台Web服务器共享Session。
2. 逐步配置详解
第一步:修改 /etc/php.ini(全局生效)
这是PHP的核心配置文件,你在这里做了三处关键修改:
session.save_handler = redis告诉PHP,不再使用默认的files(文件)方式,而是使用redis扩展模块来处理Session。session.save_path = "tcp://192.168.226.10:6379"指定Redis服务器的IP和端口。tcp://是协议头,告诉PHP使用TCP连接。session.auto_start = 1这个设置意味着每次请求开始时,PHP会自动调用session_start()。 ⚠️ 注意:启用后,如果你的某些页面不需要Session,也会白白占用Redis连接资源。通常建议在代码中手动调用session_start(),这里设为1是为了简化测试或老旧项目的改造。
第二步:修改 /etc/php-fpm.d/www.conf(覆盖全局)
PHP-FPM(处理动态请求的进程管理器)有自己的配置,可以覆盖php.ini的设置。
-
你注释掉的两行:
ini
;php_value[session.save_handler] = files ;php_value[session.save_path] = /var/lib/php/session这两行的原始作用是强制将Session存回文件。如果不注释,它们会覆盖你在
php.ini中设置的redis,导致Session依然写入硬盘。注释掉它们,就等于移除了这个“覆盖指令”,让php.ini中的Redis配置正式生效。
3. 为什么需要两步配置?(关键点)
php.ini是“最高指示”,但PHP-FPM的www.conf拥有更高优先级的局部设置权限。- 你的操作逻辑是:在
php.ini中设定目标为Redis,然后在www.conf中废除干扰项(Files),确保规则统一。
==总结==:通过在php.ini中开启Redis存储,并屏蔽www.conf中的文件存储覆盖指令,最终迫使所有PHP Session数据落地到指定的Redis数据库中。




Nginx四层负载均衡场景实践
Nginx如何配置四层负载均衡
1、通过访问负载均衡的5555端口,实际是后端的web01的22端口在提供服务;
2、通过访问负载均衡的6666端口,实际是后端的MySQL的3306端口在提供服务。
- 创建存放四层负载均衡配置文件的目录
[root@lb01 ~]# yum install nginx-mod-stream
[root@lb01 ~]# vim /etc/nginx/nginx.conf
include /etc/nginx/conf.c/*.conf;
#include /etc/nginx/conf.d/*.conf;
[root@lb01 ~]# mkdir /etc/nginx/conf.c
- 配置四层负载均衡
[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf
stream {
upstream lb {
server 192.168.226.20:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.226.30:80 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass lb;
}
}
二、四层配置代码逐行详解
nginx
stream { # ← 四层负载均衡模块
upstream 1b { # ← 定义后端服务器组,名称叫"1b"
server 192.168.226.20:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.226.30:80 weight=5 max_fails=3 fail_timeout=30s;
}
server { # ← 定义代理服务器
listen 80; # ← 监听本机的80端口
proxy_connect_timeout 3s; # ← 连接后端超时时间
proxy_timeout 3s; # ← 数据传输超时时间
proxy_pass 1b; # ← 转发到 upstream 定义的 "1b" 组
}
}
访问流程
text
用户浏览器访问 http://192.168.226.10(默认80端口)
↓
[负载均衡器 lb01] 监听 :80
↓
四层转发(TCP层,不解析HTTP内容)
↓
选择后端服务器(根据 weight 权重)
↓
web01 (192.168.226.20:80) 或 web02 (192.168.226.30:80)
↓
返回网站首页给用户



配置四层负载均衡日志
- 四层负载均衡开启日志
- 四层负载均衡是没有access的日志的,因为在nginx.conf的配置中,access的日志格式是配置在HTTP下的,而四层负载均衡配置实在HTTP以外的
- 如果需要日志则需要配置在stream下面
[root@lb01 ~]# cd /etc/nginx/conf.c/
[root@lb01 conf.c]# vim lb_domain.conf
stream {
log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol '
'"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ;
access_log /var/log/nginx/proxy.log proxy;
upstream lb {
server 192.168.226.20:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.226.30:80 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass lb;
}
}
[root@lb01 conf.c]# nginx -t
[root@lb01 conf.c]# systemctl restart nginx

Nginx四层负载均衡端口转发
- 使用
Nginx四层负载均衡实现TCP的转发
请求负载均衡 5555 ---> 192.168.226.20:22;
请求负载均衡 6666 ---> 192.168.226.30:3306;
- 配置
Nginx四层负载均衡实现TCP的转发
[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf
stream {
log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol '
'"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ;
access_log /var/log/nginx/proxy.log proxy;
#定义转发ssh的22端口
upstream ssh {
server 192.168.226.20:22;
}
#定义转发mysql的3306端口
upstream mysql {
server 192.168.226.30:3306;
}
server {
listen 5555;
proxy_connect_timeout 3s;
proxy_timeout 300s;
proxy_pass ssh;
}
server {
listen 6666;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass mysql;
}
}
[root@lb01 ~]# nginx -t
[root@lb01 ~]# setenforce 0
[root@lb01 ~]# systemctl restart nginx


# 安装mariadb数据库软件
yum install mariadb-server mariadb -y
# 启动数据库并且设置开机自启动
systemctl start mariadb
systemctl enable mariadb
# 设置mariadb的密码
mysqladmin password '123456'
# 验证数据库是否工作正常
mysql -uroot -p123456 -e "show databases;"
# 创建访问用户
grant all privileges on *.* to root@'192.168.226.%' identified by '123456' with grant option;
flush privileges;
# 测试访问
[root@server4 ~]# mysql -uroot -p123456 -h 192.168.226.10 -P 6666
MariaDB [(none)]>
[root@server4 ~]# ssh root@192.168.226.10 -p 5555
root@192.168.226.10 password:

Keepalived高可用
一般是指2台机器启动着完全相同的业务系统,当有一台机器down机了,另外一台服务器就能快速的接管,对于访问的用户是无感知的。
硬件通常使用 F5
软件通常使用 keepalived
keepalived软件是基于VRRP协议实现的,VRRP虚拟路由冗余协议,主要用于解决单点故障问题
一、为什么需要高可用?
单点故障问题
text
用户 → [Nginx负载均衡器] → 后端服务器
↑
如果这台机器挂了?
所有服务全部不可用!
高可用解决方案
text
用户 → [VIP虚拟IP] → 主Nginx (Active) ← 正常工作
↘ 备Nginx (Standby) ← 随时待命
主Nginx挂了 → VIP自动漂移到备Nginx → 服务不中断!
二、硬件负载均衡 vs 软件负载均衡
| 类型 | 代表产品 | 优点 | 缺点 |
|---|---|---|---|
| 硬件 | F5、A10、Radware |
性能极高、稳定、功能丰富 | 价格昂贵(几十万起) |
| 软件 | Keepalived、HAProxy、Nginx |
免费、灵活、易扩展 | 性能受限于服务器 |
三、VRRP 协议详解(核心!)
什么是 VRRP?
VRRP = Virtual Router Redundancy Protocol(虚拟路由冗余协议)
简单说:把多台路由器虚拟成一台”虚拟路由器”,通过一个虚拟IP对外提供服务。
四、VRRP 核心概念详解
1. 虚拟路由器
text
┌─────────────────────────────────────┐
│ 虚拟路由器 (VRRP组) │
│ 虚拟IP: 192.168.226.100 │
│ 虚拟MAC: 00-00-5e-00-01-51 │
└─────────────────────────────────────┘
↓ ↓
┌───────────┐ ┌───────────┐
│ Master │ │ Backup │
│ 实际IP: │ │ 实际IP: │
│ .10 │ │ .11 │
└───────────┘ └───────────┘
- 虚拟
IP(VIP):对外提供服务的浮动IP - 虚拟MAC:
00-00-5e-00-01-{VRID},VRID范围 1-255
2. Master(主路由器)
- 唯一对外提供服务
- 响应虚拟
IP的ARP请求 - 定期发送
VRRP通告(心跳包)
3. Backup(备份路由器)
- 不对外提供服务
- 监听 Master 的通告
- Master 挂了 → 接替成为新的 Master
五、选举机制(重点!)
优先级(Priority)
text
Master 选举依据:优先级越高越优先
配置示例:
Master: priority 150
Backup: priority 100
抢占模式 vs 非抢占模式
① 抢占模式(默认)
text
时刻1: Master(150) 运行中 ✅
时刻2: Master 挂了 ❌
时刻3: Backup(100) 接管成为新 Master ✅
时刻4: 原Master恢复(150) → 立即抢回Master位置!(抢占)
特点:优先级高的永远是 Master
② 非抢占模式(nopreempt)
text
时刻1: Master(150) 运行中 ✅
时刻2: Master 挂了 ❌
时刻3: Backup(100) 接管成为新 Master ✅
时刻4: 原Master恢复(150) → 继续当Backup,不抢占!
特点:只要Master不挂,优先级高的也得等着
六、三种状态机
text
┌─────────────┐
│ Initialize │ ← 系统启动/Keepalived启动
│ (初始化) │
└──────┬──────┘
│
┌──────▼──────┐
│ 检查优先级 │
└──────┬──────┘
│
┌──────────────┼──────────────┐
↓ ↓ ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master │ │ Backup │ │ Fault │
│ (主状态) │ │ (备状态) │ │ (故障) │
└─────────────┘ └─────────────┘ └─────────────┘
==总结==:都是通过优先级值选举出来的
Keepalived高可用安装配置
安装keepalived
[root@lb01 ~]# yum install -y keepalived
[root@lb02 ~]# yum install -y keepalived
配置master
- 找到配置文件
[root@lb02 ~]# rpm -qc keepalived
/etc/keepalived/keepalived.conf
/etc/sysconfig/keepalived
- 修改配置
[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
global_defs { #全局配置
router_id lb01 #标识身份->名称
}
vrrp_instance VI_1 {
state MASTER #标识角色状态
interface ens160 #网卡绑定接口
virtual_router_id 50 #虚拟路由id
priority 150 #优先级
advert_int 1 #监测间隔时间
#use_vmac #使用虚拟mac地址,因为路由问题,可能导致原本的IP不可用
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
}
virtual_ipaddress {
192.168.226.100 #虚拟的VIP地址
}
}
配置backup
[root@lb02 ~]# vim /etc/keepalived/keepalived.conf
global_defs {
router_id lb02
}
vrrp_instance VI_1 {
state BACKUP
interface ens160
virtual_router_id 50
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.226.100
}
}


1. 需要关闭防火墙/放行协议的原因
Keepalived 的核心是 VRRP 协议,它通过多播(224.0.0.18)或单播发送心跳包来让主备之间互相检测存活状态。
- 如果防火墙阻挡了 VRRP 报文:
- MASTER 收不到 BACKUP 的报文 → 不知道对方存在
- BACKUP 收不到 MASTER 的报文 → 以为 MASTER 挂了,自己也变成 MASTER
- 结果:两台都认为自己该持有 VIP,出现 “脑裂”,或者 VIP 无法正常漂移
- 所以要放行:要么彻底关防火墙,要么放行
VRRP协议(协议号 112)。
工作原理:
- 启动时,MASTER(
.10)优先级更高,抢占并绑定 VIP - BACKUP(
.20)检测到 MASTER 存活,主动放弃 VIP,处于待命状态 - 只有 MASTER 对外提供服务,BACKUP 只负责监听心跳
- 如果 MASTER 宕机,BACKUP 才会接管 VIP,保证服务不中断
所以你看到 .10 有 192.168.226.100,.20 没有 → 这就是高可用的正常状态。
关闭服务器进行抢占


- 配置非抢占式
- 两个节点的state都必须配置为BACKUP
- 两个节点都必须加上配置 nopreempt
- 其中一个节点的优先级必须要高于另外一个节点的优先级。
- 两台服务器的角色状态都启用
nopreempt后,必须修改角色状态统一为BACKUP,唯一的区分就是优先级。
Master配置
vrrp_instance VI_1 {
state BACKUP
priority 150
nopreempt
}
Backup配置
vrrp_instance VI_1 {
state BACKUP
priority 100
nopreempt
}

非抢占式的影响:
| 场景 | 行为 |
|---|---|
.10 宕机 → .20 接管 VIP |
✅ 正常切换 |
.10 恢复上线 |
❌ 不会抢回 VIP(因为 nopreempt) |
VIP 会一直留在 .20 上 |
直到 .20 也宕机,VIP 才回到 .10 |
.10 关了,.20 会在 3 秒内自动接管 VIP,服务不受影响。.10 恢复后,因为配置了nopreempt,不会抢回 VIP,VIP 继续留在 .20 上,直到 .20 也出问题。这是非抢占模式的设计行为。

高可用keepalived故障脑裂
由于某些原因,导致两台keepalived高可用服务器在指定时间内,无法检测到对方的心跳,各自取得资源及服务的所有权,而此时的两台高可用服务器又都还活着。
脑裂故障原因
- 服务器网线松动等网络故障
- 服务器硬件故障发生损坏现象而崩溃
- 主备都开启
firewalld防火墙
解决脑裂故障方案
- 如果发生脑裂,则随机kill掉一台即可
- 在backup上编写检测脚本, 测试如果能ping通master并且backup节点还有vIP的话则认为产生了脑裂
# 检测脑裂脚本
[root@server2 ~]# cat check_split_brain.sh
#!/bin/sh
vip=192.168.226.100
lb01_ip=192.168.226.10
while true;do
ping -c 2 $lb01_ip &>/dev/null
if [ $? -eq 0 -a `ip add|grep "$vip"|wc -l` -eq 1 ];then
echo 'ha is split brain.warning!!!' `date`>> /root/check_split_brain.sh.out
systemctl stop keepalived
else
echo "ha is ok..." >> /root/check_split_brain.sh.out
fi
sleep 5
done
[root@server2 ~]# chmod +x check_split_brain.sh
[root@server2 ~]# /root/check_split_brain.sh &
[root@server2 ~]# tail -f check_split_brain.sh.out
为什么 cat 看到既有 warning 又有 ok?
text
ha is split brain.warning!!! 2026年 08月 29日 星期六 18:05:42 CST
ha is ok...
ha is ok...
ha is ok...
ha is ok...
| 时间 | 状态 | 输出 |
|---|---|---|
18:05:42 |
脑裂时 | ha is split brain.warning!!! |
18:05:47 |
修复后(VIP 释放) | ha is ok... |
18:05:52 |
继续检查 | ha is ok... |
| … | … | … |
你的理解完全正确:脚本一直在 ping,当条件不满足时就输出 ok。本质变化是 VIP 从 .226.20 上消失了,所以条件从 true 变成了 false。
一图总结
text
循环开始
↓
ping MASTER (192.168.226.10)
↓
┌─────────────────────────────────┐
│ 能通? AND 自己有 VIP? │
└─────────────────────────────────┘
↓ ↓
YES NO
↓ ↓
脑裂! 正常!
↓ ↓
systemctl stop echo "ha is ok..."
keepalived
(自杀,释放VIP)
↓
VIP 消失 → 下次循环条件不成立 → 输出 ok


Keepalived+Nginx实现网站高可用方案
VIP:192.168.226.100
lb01:192.168.226.10:8080
lb02:192.168.226.20:8080
web01:192.168.226.30:80
web02:192.168.226.50:80
首先部署web01和web02的网站(以web01为例)
[root@server3 ~]# yum install nginx -y
[root@server3 ~]# echo "web01 ..." > /usr/share/nginx/html/index.html
[root@server3 ~]# nginx
# 访问web01、web02测试
[root@server5 ~]# curl 192.168.226.30
web01 ...
[root@server5 ~]# curl 192.168.226.50
web02 ...

部署lb01和lb02(以lb01为例)
[root@server1 ~]# yum install nginx -y
[root@server1 ~]# yum install nginx-mod-stream -y
[root@server1 ~]# vim /etc/nginx/nginx.conf
include /etc/nginx/conf.c/*.conf;
[root@server1 ~]# mkdir /etc/nginx/conf.c
[root@server1 ~]# vim /etc/nginx/conf.c/lb_domian.conf
stream {
upstream lb {
server 192.168.226.30:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.226.50:80 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 8080;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass lb;
}
}
[root@server1 ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@server1 ~]# nginx
[root@server1 ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 511 [::]:80 [::]:*
部署成功:轮询算法

部署keepalived
[root@server1 ~]# yum install keepalived -y
[root@server1 ~]# vim /etc/keepalived/keepalived.conf
global_defs { #全局配置
router_id lb01 #标识身份->名称
}
vrrp_instance VI_1 {
state MASTER #标识角色状态
interface ens160 #网卡绑定接口
virtual_router_id 50 #虚拟路由id
priority 150 #优先级
advert_int 1 #监测间隔时间
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
}
virtual_ipaddress {
192.168.226.100 #虚拟的VIP地址
}
}
[root@server1 ~]# systemctl start keepalived.service
[root@server2 ~]# vim /etc/keepalived/keepalived.conf
global_defs { #全局配置
router_id lb02 #标识身份->名称
}
vrrp_instance VI_1 {
state BACKUP #标识角色状态
interface ens160 #网卡绑定接口
virtual_router_id 50 #虚拟路由id
priority 100 #优先级
advert_int 1 #监测间隔时间
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
}
virtual_ipaddress {
192.168.226.100 #虚拟的VIP地址
}
}
[root@server2 ~]# systemctl start keepalived.service
访问VIP进行测试
[root@server5 ~]# for i in {1..6};do curl 192.168.226.100:8080;done
web01 ...
web02 ...
web01 ...
web02 ...
web01 ...
web02 ...

当lb01、lb02产生宕机的时候,keepalived应该可以检测到并发生飘移
bash
#!/bin/bash
# 获取当前 Nginx 进程数量(正常是 9,挂了是 0)
nginx_process_number=`ps -C nginx --no-header | wc -l`
# 判断:如果进程数等于 0,说明 Nginx 挂了
if [ $nginx_process_number -eq 0 ]; then
# 尝试重启 Nginx
systemctl restart nginx
# 重启后再次检查进程数
nginx_process_number=`ps -C nginx --no-header | wc -l`
# 如果还是 0(重启失败)
if [ $nginx_process_number -eq 0 ]; then
exit 1 # 返回 1 → Keepalived 触发 VIP 漂移
else
exit 0 # 返回 0 → 正常,不切换
fi
else
# Nginx 正常运行(进程数 = 9),不做任何事
exit 0
fi
这个脚本的作用是:==检测 Nginx是否正常运行,如果挂了就尝试重启,重启失败则通知 Keepalived 进行 VIP 漂移。==

bash
# 1. 查看 Nginx 正常运行时的进程数
[root@localhost ~]# ps -C nginx --no-header | wc -l
9 # 1个master + 8个worker = 9个进程 ✅
# 2. 停止 Nginx
[root@localhost ~]# nginx -s stop
[root@localhost ~]# ps -C nginx --no-header | wc -l
0 # Nginx 已停止,进程数为 0 ✅
# 3. 强制杀掉所有 Nginx 进程
[root@localhost ~]# pkill nginx
[root@localhost ~]# ps -C nginx --no-header | wc -l
0 # 全部被杀掉,进程数为 0 ✅
# 4. 重新启动 Nginx
[root@localhost ~]# nginx
[root@localhost ~]# ps -C nginx --no-header | wc -l
9 # 恢复正常,9个进程 ✅
# 5. 给脚本添加执行权限
[root@localhost ~]# chmod +x /usr/local/src/check_nginx_pid.sh
在配置文件里配置你的脚本路径
[root@server1 ~]# vim /etc/keepalived/keepalived.conf
global_defs { #全局配置
router_id lb01 #标识身份->名称
}
vrrp_script check_web {
script "/usr/local/src/check_nginx_pid.sh" -> # 这里是增加脚本路径
interval 5
}
vrrp_instance VI_1 {
state MASTER #标识角色状态
interface ens160 #网卡绑定接口
virtual_router_id 50 #虚拟路由id
priority 150 #优先级
advert_int 1 #监测间隔时间
track_script { # -> 这里是添加脚本调用
check_web
}
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
}
virtual_ipaddress {
192.168.226.100 #虚拟的VIP地址
}
}
1. 健康检查脚本定义
text
vrrp_script check_web {
script "/usr/local/src/check_nginx_pid.sh"
interval 5
}
- script:指定检测
Nginx是否存活的脚本路径 - interval 5:每 5 秒执行一次脚本
- 作用:脚本返回 0 表示
Nginx正常,非 0 表示故障
2. 脚本调用与认证
text
track_script {
check_web
}
authentication {
auth_type PASS
auth_pass 1111
}
- track_script:引用上面定义的
check_web脚本,若脚本检测失败则触发 VIP 漂移 - authentication:主备节点间通信的认证方式(密码必须一致)

脚本状态对应表
Nginx 状态 |
进程数 | 脚本执行 | 返回值 | Keepalived 动作 |
|---|---|---|---|---|
| 正常运行 | 9 | 走 else 分支 | exit 0 | 不做任何事,保持 MASTER |
| 挂了但重启成功 | 0 → 9 | 走 if 分支,重启后恢复 | exit 0 | 不做任何事,保持 MASTER |
| 挂了且重启失败 | 0 → 0 | 走 if 分支,重启后还是 0 | exit 1 | VIP 漂移到备机 |


Keepalived高可用性与代理的关联
Keepalived与代理(如 Nginx、HAProxy、LVS)通常组合使用,解决互联网服务中的==单点故障==问题。
1. 各自核心职责
| 组件 | 核心职责 | 技术本质 |
|---|---|---|
Keepalived |
对外保持同一个虚拟IP(VIP),保证入口地址始终可用。 |
高可用(HA):通过 VRRP 协议检测代理服务是否存活。如果主代理宕机,立即将 VIP 漂移到备用节点。 |
代理(Nginx/HAProxy/LVS) |
接收客户端请求,根据负载均衡算法转发给后端 Real Server。 | 负载均衡(LB):将流量分发到多台后端服务器,提高吞吐量和容错能力。 |
2. 组合使用的核心原因
只有Nginx代理而没有 Keepalived:
- 所有流量经过单台
Nginx。 - 该
Nginx服务器宕机、关机或网络中断时,整个网站无法访问(==单点故障==)。
只有 Keepalived 而没有代理:
Keepalived本身不处理业务请求,仅提供可漂移的IP。- 请求到达 VIP 后,如果没有代理层接收并转发,流量无法进入后端业务系统。
组合后的效果:
Keepalived为代理层提供高可用保障,确保入口不中断;代理为后端应用提供负载均衡,确保处理能力充足。
3. 工作流程
text
1. 用户访问域名
|
▼
2. DNS 解析指向虚拟 IP(VIP)
|
▼
3. Keepalived Master 节点持有该 VIP
| (通过 VRRP 协议周期性检测 Master 存活状态)
▼
4. 代理(Nginx)监听 80/443 端口
| (配置了 upstream 后端服务器池)
▼
5. 代理根据权重/轮询等算法,将请求转发给后端 Web Server
4. 故障转移机制
当 Master 节点发生故障时:
Keepalived检测:Backup 节点在 3 个心跳周期内(约 3 秒)未收到 Master 的 VRRP 广播包。- VIP 漂移:Backup 节点立即接管 VIP。
- 代理接管:新请求通过 VIP 到达 Backup 节点的代理服务。
- 业务无感:Backup 节点的代理继续将请求转发给后端服务器池。
对用户而言,网站入口地址不变,服务未中断。
5. 常见组合方式
| 组合方式 | 优势 | 适用场景 |
|---|---|---|
LVS + Keepalived |
性能极高,四层转发,无数据拷贝,抗DDoS 能力强 |
大型门户、直播平台、TCP 四层入口 |
Nginx + Keepalived |
配置灵活,支持七层 HTTP 高级特性(正则、缓存)、SSL 卸载 |
Web 应用、微服务网关、API 管理 |
HAProxy + Keepalived |
支持四层和七层负载,健康检查功能强大 | 高要求 TCP/HTTP 负载均衡场景 |
关联总结
Keepalived 保证代理服务的高可用,确保入口 IP不丢失;代理负责请求分发,保证流量能到达后端业务服务器。两者协同工作,共同保障网站入口的 7×24 小时不间断服务。
LVS的NAT模式
当用户访问服务器集群提供的服务时,发往虚拟IP地址的请求数据包(负载均衡器的外部IP地址)到达负载均衡器。
负载均衡器检查数据包的目标地址和端口号。如果根据虚拟服务器规则表匹配虚拟服务器服务,则通过调度算法从集群
中选择 真实服务器,并将连接添加到记录已建立连接的哈希表中。然后,将目标地址和数据包的端口重写为所选服务器
的目标地址和端口,并将数据包转发到服务器。当传入数据包属于此连接并且可以在哈希表中找到所选服务器时,将重写
该数据包并将其转发到所选服务器。当回复数据包返回时,负载均衡器将数据包的源地址和端口重写为虚拟服务的源地址
和端口。连接终止或超时后,连接记录将在哈希表中删除。
LVS的NAT模式特点:
RS应该使用私有地址,网关为DIP
DIP和RIP属于同一个网段
请求和响应报文都需要经过DS(性能瓶颈)
支持端口映射
RS可以使用任意操作系统
一、整体流程:一个请求的”NAT之旅”
当用户访问服务器集群提供的服务时,发往虚拟IP地址的请求数据包到达负载均衡器。整个过程可以拆解为以下几个关键步骤:
| 步骤 | 方向 | 操作 | 数据包详情(基于 192.168.226.xxx) |
|---|---|---|---|
| ① | 客户端 → DS |
用户访问 VIP | 客户端 192.168.226.128:12345 → VIP 192.168.226.100:80 |
| ② | DS内部 |
匹配虚拟服务规则 | DS检查规则表,命中 192.168.226.100:80 的服务 |
| ③ | DS 内部 |
调度算法选 RS | 通过轮询等算法选中 RS1(192.168.226.30:80),并在哈希表中记录该连接 |
| ④ | DS → RS1 |
DNAT 转换 |
目标地址改写为 192.168.226.30:80,转发给RS1 |
| ⑤ | RS1 内部 |
处理业务 | RS1 处理请求,生成响应数据 |
| ⑥ | RS1 → DS |
响应包先回 DS |
RS1 的响应包 192.168.226.30:80 → 客户端 192.168.226.128:12345,==因网关指向 DIP==(192.168.226.10),先回到 DS |
| ⑦ | DS → 客户端 |
SNAT 转换 |
源地址改写为 192.168.226.100:80 → 客户端 192.168.226.128:12345 |
当后续同一个连接的数据包到达时,DS可以在哈希表中查找到之前记录的 RS,直接将数据包转发给对应的 RS,无需重新调度。连接终止或超时后,连接记录在哈希表中删除。
在这个过程中,
DS扮演了一个”网络地址转换器”的角色,对外屏蔽了内部的真实服务器结构。客户端全程只与 VIP 交互,感知不到后端 RS 的存在。
在 192.168.226.x 网段的环境里:
DS这台机器本身有一个真实IP(DIP),比如192.168.226.10,用于和后端服务器通信。DS上同时还配置了一个 VIP,比如192.168.226.100,这个IP是对外公布的服务地址,客户端只认这个地址。
关键点:客户端访问 VIP → VIP 就在 DS上 → DS 直接收到包 →DS 做 DNAT→ 转发给 RS。
流程图
text
客户端 (192.168.226.128)
|
| 访问 http://192.168.226.100:80
↓
┌─────────────────────────────────┐
│ DS(负载均衡器) │
│ 网卡上配置了两个地址: │
│ ├── VIP: 192.168.226.100 │ ← 客户端访问的就是这个
│ └── DIP: 192.168.226.10 │ ← 用来和 RS 通信
│ │
│ 收到包后,做 DNAT: │
│ 目标地址 192.168.226.100:80 │
│ → 改为 192.168.226.30:80 │
└─────────────────────────────────┘
|
| 转发给 RS1
↓
┌─────────────────────────────────┐
│ RS1 (192.168.226.30) │
│ 处理请求,响应包通过网关 │ # RS 的网关 = DS(的 DIP 地址),这是让响应包“回家”做地址转换的
│ (192.168.226.10) 回到 DS │ 唯一通道
└─────────────────────────────────┘
核心配置要点(必须满足的条件)
基于 192.168.226.x 网段,NAT 模式有以下硬性要求:
RS 使用私有地址,且网关指向 DIP
| 角色 | IP地址 |
网关设置 |
|---|---|---|
DS(DIP) |
192.168.226.10 |
— |
RS1(RIP) |
192.168.226.30 |
默认网关必须指向 192.168.226.10 |
RS2(RIP) |
192.168.226.40 |
默认网关必须指向 192.168.226.10 |
- 为什么? 请求包经过
DS后,目标IP变成了 RS 的私有IP(如192.168.226.30)。RS 处理完请求后,响应包要回给客户端(192.168.226.128)。但 RS 自己不知道客户端在哪,所以它的默认网关必须指向DS的 DIP。只有这样,响应包才会先回到DS,由DS做SNAT后发出。 - 若不满足会怎样? RS 可能会把响应包直接丢给它的默认网关(比如路由器),而这个路由器不认识这个连接,就会丢包,导致访问失败。
DIP 和 RIP 属于同一个网段
在我们的环境里,DIP(192.168.226.10)和所有 RIP(192.168.226.30、192.168.226.40)都在 192.168.226.0/24 网段内。
- 为什么?
DS改写目标地址后,要通过二层交换(MAC 地址) 把包发给 RS。如果 DIP 和 RIP 不在同一网段,DS 就需要通过路由转发,这会增加复杂度和延迟。
支持的额外特性
- 支持端口映射:例如客户端访问
192.168.226.100:8080,DS可以将其映射到RS1的192.168.226.30:80,实现内外端口不一致。 - RS 操作系统无关:RS 可以是 Windows、Linux、Unix 等任何操作系统,只要能正常处理 TCP/IP 协议即可。
LVS的DR模式
一、整体流程:一个请求的 “DR 之旅”
DR 模式的核心思想是:请求走 DS,响应直接回客户端,从而避免 NAT 模式中响应流量也经过 DS 的性能瓶颈。
在 192.168.226.x 网段环境下,一个请求的完整流程如下:
| 步骤 | 方向 | 操作 | 数据包详情(基于192.168.226.xxx) |
|---|---|---|---|
| ① | 客户端 → DS |
用户访问 VIP | 客户端 192.168.226.128:12345 → VIP 192.168.226.100:80 |
| ② | DS 内部 |
匹配虚拟服务规则 | DS 检查规则表,命中 192.168.226.100:80 的服务 |
| ③ | DS内部 |
调度算法选 RS | 通过调度算法选中RS1(192.168.226.30),在哈希表中记录连接 |
| ④ | DS → RS1 |
修改 MAC 地址 | DS 将数据帧的目标 MAC 地址改为 RS1 的 MAC 地址,在 LAN 上重新传输。数据包的 IP 地址不变(目标 iP 仍是 192.168.226.100) |
| ⑤ | RS1 内部 |
处理请求 | RS1 的 lo 接口上配置了 VIP(192.168.226.100),所以它能接收并处理目标为 VIP 的请求 |
| ⑥ | RS1 → 客户端 |
响应直接返回 | RS1将响应包源地址设为 192.168.226.100,直接通过网关(路由器 192.168.226.1)发回给客户端,不经过DS |
关键区别:DR 模式中,
DS只修改了数据帧的 MAC 地址(在二层工作),而没有修改IP地址和端口(没有DNAT)。RS 收到的请求包目标IP仍然是 VIP,因为 RS 的 lo 接口上也配置了 VIP,所以它能正常处理这个包。
关键校验:
- ✅
DS和所有 RS 在同一个二层网络(LAN)中,通过交换机/HUB 相连。 - ✅ RS 的默认网关指向路由器
192.168.226.1,绝不能指向 DIP(192.168.226.10)。 - ✅ RS 的 lo 接口上配置了 VIP(
192.168.226.100),并设置了ARP抑制。
二、DR 模式的核心机制详解
1. 为什么 DR 模式只需要改 MAC 地址?
在 NAT 模式中,DS 做 DNAT把目标 IP从 VIP 改成 RIP,所以响应包必须先回DS做 SNAT。
但在 DR 模式中:
DS不改IP地址,只把数据帧的目标 MAC 地址从DS的 MAC 改成 RS 的 MAC。- RS 收到的包:目标
IP仍然是192.168.226.100(VIP),但因为 RS 的 lo 接口上也配置了192.168.226.100,所以 RS 的 TCP/IP协议栈认为这个包是发给自己的,正常接收并处理。 - RS 响应时,源地址直接设为
192.168.226.100,因为客户端访问的就是这个IP,所以客户端能正确识别响应包。
text
客户端 192.168.226.128
|
| 请求:目标 IP = 192.168.226.100
↓
┌──────────────────────────────────────────────────┐
│ DS(负载均衡器) │
│ ┌──────────────────────────────────────────┐ │
│ │ 收到请求包: │ │
│ │ 源 IP: 192.168.226.128 │ │
│ │ 目标 IP: 192.168.226.100 (VIP) │ │
│ │ 目标 MAC: DS 的 MAC │ │
│ └──────────────────────────────────────────┘ │
│ 调度算法选中 RS1 │
│ ↓ 只改 MAC,不改 IP │
│ ┌──────────────────────────────────────────┐ │
│ │ 转发包: │ │
│ │ 源 IP: 192.168.226.128 (不变) │ │
│ │ 目标 IP: 192.168.226.100 (VIP, 不变) │ │
│ │ 目标 MAC: 改为 RS1 的 MAC │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────┐
│ RS1(物理网卡 IP: 192.168.226.30) │
│ lo 接口别名: 192.168.226.100 │
│ ┌──────────────────────────────────────────┐ │
│ │ 收到包,检查目标 IP: 192.168.226.100 │ │
│ │ → lo 上有这个 IP,收下! │ │
│ │ 处理请求,生成响应 │ │
│ └──────────────────────────────────────────┘ │
│ ↓ 响应直接发,不经过 DS │
│ 源 IP: 192.168.226.100 (VIP) │
│ 目标 IP: 192.168.226.128 │
└──────────────────────────────────────────────────┘
↓
直接到达客户端 192.168.226.128 ✅
==本质==:
有相同的 VIP → 能直接通信 → 绕过 DS”
- “有相同的 VIP” → RS 的 lo 接口上配置了和
DS一样的 VIP,所以 RS 认为自己也是这个IP的“主人”。 - “能直接通信” → RS 收到请求后,直接以 VIP 的身份响应客户端,不需要经过
DS转手。 - “绕过
DS” → 响应流量完全不经过DS,减轻了DS的压力,这就是 DR 模式性能高的根本原因
ARP 抑制(最关键的一步!)
为什么需要 ARP抑制?
在 192.168.226.x 网段中,DS 和所有 RS 的 lo 接口都配置了 VIP(192.168.226.100)。如果不做任何限制:
- 当路由器
192.168.226.1询问“谁有192.168.226.100的 MAC 地址?”时,DS和所有 RS 都会响应。 - 路由器可能把 VIP 的 MAC 地址映射到某一台 RS 上,导致部分请求绕过
DS直接发给 RS,破坏了负载均衡。
2、 ARP 抑制的作用(核心:防止“喧宾夺主”)
在LVS-DR 模式中,DS(调度器)和 RS(真实服务器)都配置了同一个 VIP。如果不做处理,当客户端发送ARP请求询问“谁是 VIP?”时,RS 也会响应,导致客户端直接把请求发给 RS,==DS 就失去了负载均衡的作用==。
ARP 抑制的具体作用:
- 隐藏 RS 的 VIP:让 RS 在网络上“装作不知道”自己有这个 VIP,不响应外部对 VIP 的
ARP请求。 - 确保流量入口唯一:保证只有
DS会响应 VIP 的ARP请求,从而确保所有客户端的请求必须先经过DS。 - 实现原理:通常通过修改 Linux 内核参数(
arp_ignore和arp_announce)来实现,让 RS 只在特定的 lo 接口上识别 VIP,而不在物理网卡上对外宣告 VIP。
3、LVS-DR 模式核心总结(本质与流程)
DR 模式的本质可以总结为以下三点:
- 请求必须经过
DS(入口在DS)- 客户端请求到达
DS,DS根据调度算法选择一台 RS。 - 关键动作:
DS只修改目标 MAC 地址(改为 RS 的 MAC),不修改源IP和目标IP(目标IP依然是 VIP)。 DS将数据包转发给选中的 RS。
- 客户端请求到达
- 响应不经过
DS(出口在 RS)- RS 收到数据包后,发现目标
IP是自己 lo 接口上的 VIP,于是接收并处理请求。 - 关键动作:RS 处理完毕后,直接以 VIP 为源
IP,客户端IP为目标IP,将响应包直接发送给客户端。 - 响应流量完全不经过
DS,这就是 DR 模式性能极高的根本原因(DS只处理入站请求,不处理出站响应,吞吐量极大提升)。
- RS 收到数据包后,发现目标
ARP抑制是前提(保障机制)- 正是因为有了
ARP抑制,才保证了“请求先到DS”这一前提。 - 如果没有
ARP抑制,客户端可能直接找到 RS,DS就形同虚设。
- 正是因为有了
LVS的TUN模式
在LVS(NAT)模式的集群环境中,由于所有的数据请求及响应的数据包都需要经过LVS调度器转发,如果后端服务器的数量大于10台,则调度器就会成为整个集群环境的瓶颈。我们知道,数据请求包往往远小于响应数据包的大小。因为响应数据包中包含有客户需要的具体数据,所以LVS(TUN)的思路就是将请求与响应数据分离,让调度器仅处理数据请求,而让真实服务器响应数据包直接返回给客户端。IP隧道(IP tunning)是一种数据包封装技术,它可以将原始数据包封装并添加新的包头(内容包括新的源地址及端口、目标地址及端口),从而实现将一个目标为调度器的VIP地址的数据包封装,通过隧道转发给后端的真实服务器(Real Server),通过将客户端发往调度器的原始数据包封装,并在其基础上添加新的数据包头(修改目标地址为调度器选择出来的真实服务器的IP地址及对应端口),LVS(TUN)模式要求真实服务器可以直接与外部网络连接,真实服务器在收到请求数据包后直接给客户端主机响应数据。
一、整体流程:一个请求的 “TUN 之旅”
text
客户端 (CIP)
|
| 请求:目标 IP = VIP
↓
[ DS (调度器) ]
|
| 1. 收到请求包(目标 IP 是 VIP)
| 2. 调度算法选中 RS1
| 3. 关键动作:IP 隧道封装
| - 在原始数据包外,再加一层新的 IP 包头
| - 新目标 IP = RS1 的 RIP (192.168.226.30)
| - 新源 IP = DS 的 DIP (192.168.226.100)
|
| 封装后的包: [ 新 IP 头 (DIP -> RIP) ] + [ 原始 IP 头 (CIP -> VIP) + 数据 ]
↓
[ RS1 (真实服务器) ]
|
| 1. 收到封装包,发现新目标 IP 是自己(RIP),于是拆开外层包头
| 2. 看到内层原始包头,目标 IP 是 VIP,而自己的 lo 接口上配置了 VIP
| 3. 接收并处理请求,生成响应
|
| 4. 响应:直接通过物理网卡发回给客户端
| - 源 IP = VIP (192.168.226.100)
| - 目标 IP = CIP (192.168.226.128)
↓
直接到达客户端 (CIP) ✅
二、 TUN 模式的核心机制详解
1. 为什么 TUN 模式需要 IP隧道封装?
在 DR 模式中,DS 和 RS 必须在同一个物理网段(二层网络),因为 DS 是通过修改 MAC 地址 来转发数据包的。
但在 TUN 模式中,DS 和 RS 可以跨越不同的网段甚至不同的机房(三层网络)。因为无法直接修改 MAC 地址,DS必须使用 IP隧道技术(IPIP):
DS端:把原始数据包(目标IP是 VIP)整个作为数据,再在外面套上一个新的IP包头(目标IP是 RS 的 RIP),然后通过路由发出去。- RS 端:收到包后,剥掉外层
IP包头,露出里面的原始数据包。发现目标IP是 VIP,且自己的lo接口上有 VIP,于是正常处理。
2. RS 响应时如何直接回给客户端?
与 DR 模式完全一致:
- RS 处理完请求后,源
IP设为 VIP(因为客户端请求的就是这个IP),目标IP设为客户端IP。 - RS 通过自己的物理网卡,直接将该响应包发送给客户端(或通过 RS 自己的网关路由出去),完全不经过
DS。
3. 隧道机制的关键要求
- RS 必须支持
IP隧道协议:RS 的操作系统内核必须支持IPIP隧道(Linux 默认支持,通过modprobe ipip加载模块)。 - RS 的 lo 接口必须配置 VIP:这是让 RS 认为“这个包是发给我的”的关键,也是实现响应源
IP为 VIP 的前提。 - RS 的网关不能指向
DS:因为响应包要直接发给客户端,如果网关指向DS,响应包就会先发给DS,违背了“响应不经过DS”的初衷,DS又会成为瓶颈。
三、 TUN 模式的核心配置要点(必须满足的条件)
- VIP 配置:
DS的物理网卡上配置 VIP;RS 的lo接口上配置 VIP(通常需要做ARP抑制,虽然 TUN 模式下DS和 RS 可能不在同一网段,ARP影响较小,但配置 lo 依然是标准做法)。 - 隧道配置:
DS和 RS 都需要配置IPIP隧道(如ip tunnel add tunl0 mode ipip),并分配隧道IP。 - 路由配置:
DS需要将发往 RS 的包通过隧道路由出去;RS 需要将响应包通过物理网卡路由给客户端(不能经过DS)。 - RS 网关:RS 的默认网关绝对不能指向
DS的 DIP,必须指向自己所在网段的真实网关(或直接配置去往客户端网段的路由)。
四、 TUN 模式支持的额外特性
- 跨网段部署:DIP、VIP、RIP 可以是公网地址,且可以分布在不同的物理网段,甚至跨越机房(这是 TUN 最大的优势)。
- 请求报文经由 Director,响应不经由 Director:与 DR 模式一样,实现了请求与响应的分离,极大提升了集群的吞吐量。
- 不支持端口映射:与 DR 模式一样,
DS不修改端口,因此无法做端口映射(如把 80 端口映射到 8080)。 - RS 的 OS 须支持隧道功能:这是 TUN 模式特有的硬性要求。
五、 LVS-TUN 模式核心总结(本质与流程)
- 请求必须经过
DS(入口在DS)- 客户端请求到达
DS,DS根据调度算法选择一台 RS。 - 关键动作:
DS不修改原始IP包头,而是在原始包外再封装一层新的IP包头(新目标IP是 RS 的 RIP),通过隧道发给 RS。
- 客户端请求到达
- 响应不经过
DS(出口在 RS)- RS 收到封装包,剥离外层包头,发现内层目标
IP是 VIP(自己的 lo 上有),于是处理请求。 - 关键动作:RS 处理完毕后,直接以 VIP 为源
IP,客户端IP为目标IP,将响应包直接发送给客户端。 - 响应流量完全不经过
DS,减轻了DS的压力。
- RS 收到封装包,剥离外层包头,发现内层目标
ARP抑制与隧道机制ARP抑制在 TUN 模式中依然重要(防止 RS 在物理网络上响应 VIP 的ARP),但 TUN 模式更核心的是IP隧道封装,它解决了 DR 模式不能跨网段的限制。
一句话终极总结():
“TUN 模式用 IP 隧道封装突破网段限制,DS 只负责加个壳转发请求,RS 拆壳处理后直接回包给客户端;请求走 DS,响应走 RS,性能高且能跨机房。”
LVS-DR与 LVS-TUN 终极对比总结
本质定性:
TUN 模式本质上是 DR 模式在跨网段场景下的“升级版”。两者的核心思想完全一致,即“请求经过DS,响应不经过 DS”,从而实现请求与响应的分离,极大提升集群吞吐量。
唯一的核心区别(DS如何转发请求):
- DR 模式(二层转发):在数据链路层修改目标 MAC 地址(改为 RS 的 MAC),要求
DS与 RS 必须在同一物理网段(二层互通)。 - TUN 模式(三层转发):在网络层给原始数据包加一层新的
IP包头(IP隧道封装,目标IP为 RS 的 RIP),允许DS与 RS 在不同网段(三层互通,可跨机房)。
核心原理全一样,请求过DS,响应不过 DS;区别只在怎么转,DR 改 MAC 限同网段,TUN 套 IP 可跨网段。”
LVS的三大类算法
一、 静态算法(Static Scheduling)
核心特点:调度器不关心后端服务器当前有多忙、是否宕机,只根据预设的规则(如轮流、权重、源 IP)进行分配。适合无状态、处理时间短且服务器性能相近的服务。
1. 轮询(Round Robin, RR)
- 原理:最公平的“排排坐,吃果果”。请求 1 给 A,请求 2 给 B,请求 3 给 C,请求 4 再回到 A。
- 适用:无状态服务(如静态网页),且所有服务器性能完全一致。
- 缺点:现实中服务器性能往往不同,性能差的机器容易被压垮。
2. 加权轮询(Weighted Round Robin, WRR)
- 原理:给性能好的服务器更高的权重。比如 A(权重3),B(权重1),那么分配序列就是 A, A, A, B, A, A, A, B… 按比例分配。
- 适用:服务器性能差异较大的场景,通过权重来“能者多劳”。
3. 源地址哈希(Source IPHash, SH)
- 原理:对客户端的源
IP进行哈希计算,同一个IP的请求永远固定分配给同一台服务器。 - 适用:需要会话保持(Session 保持)的场景。比如用户登录后,如果下次请求被分到另一台机器,Session 就丢了,SH 可以解决这个问题。
- 缺点:如果某个
IP段(比如一个公司出口)疯狂发请求,会导致那台服务器压力过大(负载倾斜)。
二、 动态算法(Dynamic Scheduling)
核心特点:调度器会实时监控后端服务器的连接数等状态,动态调整分配。适合处理时间差异大、负载变化快的场景。
1. 最少连接(Least Connections, LC)
- 原理:谁的当前连接数最少,就把新请求给谁。这能动态平衡负载。
- 适用:请求处理时间差异大的场景(如数据库查询、复杂计算),因为处理慢的机器连接数会积累,新请求就不给它了。
- 缺点:不看服务器性能。一台性能极强但当前连接数稍多的服务器,可能不如一台性能差但连接数少的服务器受青睐。
2. 加权最少连接(Weighted Least Connections, WLC)
- 原理:
LVS的默认算法。结合了“权重”和“连接数”。公式:当前连接数 ÷ 权重。计算结果越小,说明该服务器相对越“闲”,优先分配。 - 例子:A(权重2,连接4,4/2=2),B(权重1,连接1,1/1=1)。B 的值更小,所以新请求给 B。
- 适用:服务器性能差异大,且请求处理时间不稳定的场景(最通用、最智能的算法之一)。
3. 基于局部性的最少连接(Locality-Based Least Connections, LBLC)
- 原理:专为缓存集群(如
CDN、Squid)设计。它优先把请求分配给“已经缓存了目标内容”的服务器。如果都没缓存,就按 LC(最少连接)分配。 - 适用:缓存服务器集群,目的是提高缓存命中率,减少回源。
4. 带复制的基于局部性最少连接(LBLCR)
- 原理:在
LBLC基础上增加了“复制”功能。如果目标内容没在任何服务器上,LBLC只是随便找台机器处理,而LBLCR会把内容复制到负载较轻的机器上,并让后续请求都指向这台机器。 - 适用:大型缓存集群,进一步提升命中率。
5. 目标地址哈希(DestinationIP Hash, DH)
- 原理:对请求的目标
IP进行哈希,同一目标IP的请求固定给同一台服务器。 - 适用:正向代理场景(如网关转发到特定后端服务)。
三、 其他算法(特殊场景优化)
这些算法在 WLC 和 SED 的基础上,为了减少延迟或避免排队而设计。
1. 最短预期延迟(Shortest Expected Delay, SED)
- 原理:基于
(当前连接数 + 1) ÷ 权重计算。与 WLC 类似,但更激进地倾向于权重高的服务器。 - 场景:需要优先利用高性能服务器的场景。
2. 永不队列(Never Queue, NQ)
- 原理:如果存在空闲服务器(连接数为 0),直接分配给它,不排队。如果都忙,再按 SED 算法分配。
- 场景:对响应延迟要求极高的场景,避免请求在高负载服务器上排队。
核心总结与选择指南(速查表)
| 算法类型 | 算法名称 | 核心逻辑 | 典型适用场景 |
|---|---|---|---|
| 静态 | RR | 轮流分配 | 无状态、性能一致的集群 |
| 静态 | WRR |
按权重轮流 | 性能差异大、无状态 |
| 静态 | SH | 源IP哈希 |
需要 Session 保持 |
| 动态 | LC | 谁连接少给谁 | 请求处理时间差异大 |
| 动态 | WLC |
(连接数/权重) 最小优先 | 最通用,性能差异大且请求时间不稳 |
| 动态 | LBLC |
优先给有缓存的机器 | 缓存集群(CDN) |
| 动态 | LBLCR |
缓存复制 + 优先给有缓存的 | 大型缓存集群 |
| 动态 | DH | 目标IP 哈希 |
正向代理、网关转发 |
| 其他 | SED |
(连接数+1)/权重 最小优先 | 优先压榨高性能服务器 |
| 其他 | NQ |
有空闲机器就上,不等 | 极低延迟要求,避免排队 |
一句话记忆法:
- 要会话保持用 SH;
- 要智能动态用 WLC(默认);
- 要缓存优化用
LBLC/LBLCR; - 要极致低延迟用 NQ。
LVS 的NAT案例

LVS之NAT案例
注意:RS的网关一定要指向DS的仅主机网卡地址
# 环境准备
DIP:192.168.195.10/24
VIP:192.168.226.10/16
RIP1:192.168.195.20 GATEWAY:192.168.195.10 # GATEWAY是RS网关要指向DS的DIP(物理网卡地址)
RIP2:192.168.195.30 GATEWAY:192.168.195.10
# DS部署
1.安装ipvsadm工具
[root@server1 ~]# yum install ipvsadm ‐y
2.打开转发,配置nat
[root@server1 ~]# echo 1 >> /proc/sys/net/ipv4/ip_forward
[root@server1 ~]# iptables ‐t nat ‐F
[root@server1 ~]# iptables ‐t nat -A POSTROUTING -s 192.168.195.0/24 -o ens160 -j MASQUERADE
3.设置wrr策略和添加RS主机
[root@server1 ~]# ipvsadm ‐A ‐t 192.168.226.10:80 ‐s wrr
[root@server1 ~]# ipvsadm ‐a ‐t 192.168.226.10:80 ‐r 192.168.195.11:80 ‐m ‐w 1
[root@server1 ~]# ipvsadm ‐a ‐t 192.168.226.10:80 ‐r 192.168.195.12:80 ‐m ‐w 1
4.查看策略信息
[root@server1 ~]# ipvsadm ‐ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
‐> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.226.10:80 wrr
‐> 192.168.195.11:80 Masq 1 0 0
‐> 192.168.195.12:80 Masq 1 0 0
# RS部署
# RS的网关必须指向DS
[root@server2 ~]# echo "RS2" > /usr/share/nginx/html/index.html
[root@server3 ~]# echo "RS3" > /usr/share/nginx/html/index.html
[root@server1 ~]# curl 192.168.226.10
RS1
[root@server1 ~]# curl 192.168.226.10
RS2
注意:selinux策略和防火墙!
一、 核心角色与网卡规划表
在 LVSNAT 模式中,Director(负载均衡器)需要两块网卡,Real Server(真实服务器)通常只需要一块内网网卡。
| 设备名称 | 角色 | 网卡名称 | VMware 网络模式 |
IP规划 (示例) |
说明 |
|---|---|---|---|---|---|
Director (LVS) |
负载均衡器 | ens160 |
NAT 模式 (VMnet8) |
VIP: 192.168.226.100 (实际IP: 192.168.226.18) |
对外网卡。负责接收客户端请求,并作为源IP 将响应发回给客户端。 |
Director (LVS) |
负载均衡器 | ens224 |
仅主机模式 (VMnet1) |
DIP: 192.168.195.10 | 对内网卡。负责将请求转发给后端的 RS,并接收 RS 返回的数据。 |
| Real Server 1 | 后端真实服务器 | ens160 |
仅主机模式 (VMnet1) |
RS1 IP: 192.168.195.11 网关: 192.168.195.10 |
提供实际服务。网关必须指向 Director 的 DIP。 |
| Real Server 2 | 后端真实服务器 | ens160 |
仅主机模式 (VMnet1) |
RS2 IP: 192.168.195.12网关: 192.168.195.10 |
提供实际服务。网关必须指向 Director 的 DIP。 |
| 客户端 (Client) | 访问请求方 | – | 与 VIP 连通即可 | 宿主机或另一台 NAT 模式虚拟机 | 通过访问 VIP(192.168.226.100)来测试负载均衡。 |
二、 配置操作三大核心步骤
1. VMware 层面设置
- 虚拟网络编辑器 (图1):
VMnet8(NAT模式):子网设为192.168.226.0,开启DHCP。VMnet1(仅主机模式):子网设为192.168.195.0,开启DHCP。
- 虚拟机设置 (图2):
- Director 虚拟机:添加两块网卡。“网络适配器”选 NAT,“网络适配器2”选仅主机。
- RS 虚拟机:只保留一块网卡,选“仅主机模式”。
2. Linux 系统层面配置(静态 IP)
- Director 节点:
- 修改
ens160配置文件:设置静态IP192.168.226.18(==实际ip不是DIP==),再添加一个vip:192.168.226.100 - 修改
ens224配置文件:设置静态IP192.168.195.10(不要配默认网关)。 - 添加 VIP:通过命令
ifconfig ens160:0 192.168.226.100 netmask 255.255.255.0 up或配置文件添加。
- 修改
- RS 节点:
- 修改网卡配置文件:设置静态
IP192.168.195.11和192.168.195.12。 - 关键: 将它们的
GATEWAY设置为192.168.195.10(即 Director 的 DIP)。
- 修改网卡配置文件:设置静态
3. Director 系统内核配置
-
开启
IP转发(NAT 模式必须):bash
echo 1 > /proc/sys/net/ipv4/ip_forward # 永久生效需写入 /etc/sysctl.conf
三、 核心逻辑总结(一句话记忆)
- VIP 走 NAT 模式:用来“对外”接收客户端的请求,并伪装成客户端与 RS 通信的源地址。
- DIP 走仅主机模式:用来“对内”和 RS 建立专属局域网,确保 Director 能直接找到 RS。
- RS 的网关必须指向 DIP:这是 NAT 模式回包的关键,确保 RS 处理完的数据能原路返回给 Director,再由 Director 发回给客户端。
- 别让两张网卡在同一网段:DIP 和 VIP 必须处于不同的网段(195 和 226),否则路由器会“精神分裂”,导致 NAT 转发失败。
1. 为什么一个网卡能有多个IP?
网卡(如 ens160)在物理层面只是一块硬件,但操作系统可以通过软件的方式,在这块硬件上绑定多个逻辑IP。
- 一个是主
IP(PrimaryIP):比如192.168.226.18,这是网卡自己的真实地址。 - 其余的是辅助
IP/ 别名(SecondaryIP/ Alias):比如192.168.226.100,这就是我们给LVS配置的 VIP。
2. 在您的架构中,ens160是怎么拥有两个IP 的?
在您的 Director(负载均衡器)上,ens160 这块网卡同时绑定了两个 226 网段的 IP:
- 实际
IP(192.168.226.18):这是 Director 用来和宿主机通信、或者您用来 SSH 登录管理的IP。 - VIP(192.168.226.100):这是绑定在
ens160上的虚拟IP。当客户端访问192.168.226.100时,数据包会进入ens160,LVS软件会拦截这个请求并进行转发。
在终端中看起来是这样的(通过 ip a 命令):
text
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:72:0b:3d brd ff:ff:ff:ff:ff:ff
inet 192.168.226.18/24 brd 192.168.226.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 192.168.226.100/24 scope global secondary ens160 <--- 这里多了一个 VIP
valid_lft forever preferred_lft forever
注意看最后一行,多了一个 secondary 标志,这就是同一个网卡上的第二个 IP。


ipvsadm作用
它是 Linux 内核中LVS(负载均衡)功能的“遥控器”和管理工具。
具体来说,它的作用只有以下三点:
- 配置虚拟服务(VIP):告诉系统对外提供服务的
IP和端口是什么(比如192.168.226.100:80)。 - 管理后端服务器(RS):把真实的服务器
IP绑定到这个 VIP 后面,让它们组成一个服务器集群。 - 查看和修改调度规则:决定用什么算法(比如轮询)把客户端的请求分发给后端的真实服务器,同时可以随时查看当前的连接状态或删除规则。
nmtui 的作用
nmtui 是 Linux 系统中用来配置网络的一个“图形化文本界面”工具。
(全称是 NetworkManager Text User Interface,即网络管理器的文本用户界面)
具体来说,它的核心作用有以下三点:
- 图形化配置网络:它提供了一个类似 BIOS 那样的蓝底菜单界面。您不需要去死记硬背繁琐的配置文件路径(比如
/etc/sysconfig/network-scripts/ifcfg-ens160),也不需要记复杂的nmcli长命令。直接用键盘方向键和回车,就能修改IP、网关、DNS。 - 轻松激活/停用网卡:可以在界面里直接启用或禁用某张网卡,修改完配置后能一键生效,不需要每次敲命令重启整个网络服务。
- 管理网络连接:可以添加新的网络连接(比如添加新的
VLAN、绑定网卡等),或者删除不需要的旧连接。




核心总结:为什么 RS 设置好后,MobaXterm 无法直接 SSH?
一句话结论:
因为您把 RS 的网卡配置成了“仅主机模式”,并设置了 195 内网网段 IP。这相当于把 RS 关进了一个没有外网入口的“密室”。而您的 Windows 宿主机(运行 MobaXterm 的电脑)默认处于 226 外网(NAT)网段,两者不在同一个局域网内,网络物理隔离,所以 MobaXterm 的连接请求找不到目标,必然失败。
详细原因拆解:
IP地址不匹配(根本原因):- 您的
MobaXterm所在宿主机:处于192.168.226.x网段(NAT模式)。 - 您的 RS 服务器:处于
192.168.195.11(仅主机模式)。 - 这就像您在北京打电话,要找的人在上海,但没有长途线路,根本找不到。
- 您的
VMware仅主机模式的特性:- “仅主机模式(
VMnet1)”设计的初衷,就是创建一个封闭的虚拟内网,用来模拟真实机房里的“内网隔离区”。默认情况下,它拒绝外部设备直接访问。
- “仅主机模式(
- 网卡名字无关:
- 无论这块网卡叫
ens160还是ens224,只要它插在“仅主机模式”的虚拟交换机上,并且配了 195 网段的IP,它就注定无法被外网直接访问。名字只是 Linux 分配的代号,决定连接能不能通的是“IP网段”和“网络模式”。
- 无论这块网卡叫
最终定论:
“RS 配好后 MobaXterm连不上”不是配置错了,而是成功实现了“后端服务器与外网隔离”的实验目标!
第一步:打开转发,配置 NAT(解决“怎么去”和“怎么回”的问题)
1. echo 1 >> /proc/sys/net/ipv4/ip_forward
- 作用:开启 Linux 系统的路由转发功能。
- 为什么需要:你的
server1作为调度器,当外部用户的请求打到它身上时,它不能自己处理,必须把数据包“转发”给后面的 RS 服务器。如果不写这个1(默认为0),系统内核会直接丢弃这些需要转发的数据包。
2. iptables -t nat -F
- 作用:清空 nat 表(Network Address Translation)原有的所有规则。
- 为什么需要:这是为了排除干扰,防止之前配置的旧 NAT 规则影响当前实验,相当于“清理桌面”。
3. iptables -t nat -A POSTROUTING -s 192.168.195.0/24 -o ens160 -j MASQUERADE
- 作用:配置源地址伪装(
SNAT),这是 NAT 模式的核心。 - 详细解释:
- 当后端的 RS 服务器(在
192.168.195.0/24网段)处理完请求,准备把网页数据发回给最终用户时,因为它们处于内网,无法直接连通外网。 - 这条命令的意思是:凡是来自
192.168.195.0/24网段的数据包,在从ens160这块网卡(通常是外网网卡)发出去的时候,把数据包的“发件人IP”偷偷改成server1自己的外网IP。 - 这样用户收到数据时,以为是
server1发的,从而实现完整的双向通信。
- 当后端的 RS 服务器(在
第二步:设置 wrr 策略和添加 RS 主机(解决“分给谁”的问题)
4. ipvsadm -A -t 192.168.226.10:80 -s wrr
- 作用:创建一个对外提供服务的“虚拟
IP”(VIP)。 - 参数拆解:
-A:添加一个虚拟服务 (Add Virtual Service)。-t 192.168.226.10:80:指定对外暴露的IP和端口(这就是用户最终访问的地址)。-s wrr:指定调度算法为 加权轮询 (Weighted Round Robin)。意思是按权重轮流分配,谁的权重高,谁接到的请求就多。
5. ipvsadm -a -t 192.168.226.10:80 -r 192.168.195.11:80 -m -w 1
6. ipvsadm -a -t 192.168.226.10:80 -r 192.168.195.12:80 -m -w 1
- 作用:把真正干活的服务器(RS)加入到这个虚拟服务里。
- 参数拆解:
-a:添加真实服务器 (Add Real Server)。-r 192.168.195.11:80:指定真实服务器的内网IP和端口。-m:指定使用 NAT 模式 (Masquerading)。-w 1:给这台服务器分配的权重是 1。因为两台都是 1,所以请求会1:1平分。
第三步:查看策略信息(验证成果)
7. ipvsadm -ln
- 作用:以数字格式(-n)列出(-l)当前的
LVS规则。 - 为什么需要:让你检查刚才配置的对不对。
输出结果
text
TCP 192.168.226.10:80 wrr
-> 192.168.195.11:80 Masq 1 0 0
-> 192.168.195.12:80 Masq 1 0 0
Masq说明是 NAT 模式。- 权重都是 1,且状态正常(后面的连接数为 0)。
==清空旧的nat规则防止干扰,开启系统内核的转发功能,配置SNAT源地址伪装实现NAT模式,最后把真实服务器(RS)加入到LVS虚拟服务中==


第一阶段:临时借用 NAT 网络(安装软件)
- 关机:先将作为 RS 的虚拟机(server2 或 server3)关机。
- 添加网卡:在
VMware的虚拟机设置里,添加一块新网卡,网络连接选择 “NAT 模式”。此时这台机器有了一块 NAT 网卡和一块仅主机网卡。
- 添加网卡:在
- 开机联网:启动虚拟机,确保系统能正常获取到 NAT 网卡分配的
IP并能 ping 通外网(比如ping www.baidu.com)。 - 安装
Nginx:执行安装命令yum install nginx -y


LVS 的DR案例

LVS之DR案例
# DS部署
[root@server1 ~]# ifconfig ens160:0 192.168.226.100 broadcast 192.168.226.255 netmask 255.255.255.0 up
[root@server1 ~]# route add -host 192.168.226.100 dev ens160:0
[root@server1 ~]# ipvsadm -A -t 192.168.226.100:80 -s wrr
[root@server1 ~]# ipvsadm -a -t 192.168.226.100:80 -r 192.168.226.20:80 -g -w 2
[root@server1 ~]# ipvsadm ‐a ‐t 192.168.226.100:80 ‐r 192.168.226.30:80 ‐g ‐w 1
# RS部署
[root@server2 ~]# cat rs.sh
#!/bin/bash
vip=192.168.226.100
ifconfig lo:0 $vip broadcast 192.168.226.255 netmask 255.255.255.255 up
route add -host $vip lo:0
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
在所有RS上部署!
不要在DS上直接进行测试,可能会因为网络的问题导致丢包
1. 如果绑在物理网卡上(灾难发生)
- 现象:RS 的物理网卡上有了 VIP,它就会认为“我是这个 VIP”。
ARP响应:当客户端在局域网里广播:“谁是 192.168.226.100?”时,RS 的物理网卡会立刻抢答:“我是!我的 MAC 地址是xx:xx…”。- 结果:客户端直接把请求发给了 RS。
DS(调度器)彻底被架空,负载均衡失效。而且DS也会抢答,导致局域网里有两个“192.168.226.100”,引发ARP冲突,网络时通时断,彻底瘫痪。
2. 绑在回环接口 lo 上(完美方案)
- 现象:RS 把 VIP 绑在
lo(内部对讲机)上。 - 对外(装聋作哑):因为 lo不是物理网卡,它接收不到外部局域网发来的
ARP广播。RS 假装不知道外面有人在找 VIP。 - 对内(负责干活):当
DS把请求转发过来时,数据包里写着的目标IP是 VIP。RS 拆开包一看:“目标IP是 192.168.226.100,我lo接口上正好有这个IP,这包是给我的!” 于是收下并处理。 - 配合
ARP抑制:为了双重保险,那四行echo命令还强制告诉内核:“如果别人问 VIP 的 MAC 地址,你要假装没听见(arp_ignore);如果要主动发ARP请求,千万别用 VIP 当源IP(arp_announce)。”
总结:
- 物理网卡绑 VIP = 对外广播“我是 VIP”,会引发混乱。
lo绑 VIP = 对外隐身,对内干活,完美承接DS转发来的包。
一、 网络拓扑与核心IP
DS(调度器):server1,物理IP为192.168.226.10,网卡名为ens160。- RS(真实服务器):
server2(IP:.20)和server3(IP:.30)。 - VIP(虚拟
IP):192.168.226.100。- 特点:
DS和所有 RS 身上都拥有这个 VIP,这是 DR 模式的核心。
- 特点:
二、 DS(调度器)部署详解原因
1. ifconfig ens160:0 192.168.226.100 broadcast 192.168.226.255 netmask 255.255.255.0 up
- 作用:在物理网卡
ens160上,绑定一个虚拟子接口ens160:0,并配置 VIP192.168.226.100。 - 为什么这么做:这是对外服务的唯一入口。客户端只知道这个 VIP,不知道后面的 RS。配置在子接口上是为了逻辑清晰,方便后续维护。
2. route add -host 192.168.226.100 dev ens160:0
- 作用:添加一条主机路由,强制去往 VIP 的流量从
ens160:0接口出去。 - 为什么这么做:Linux 系统默认的路由策略可能会把包发到别的网卡上。加这条路由是为了精准锁定,确保
DS能正确处理 VIP 的流量。
3. ipvsadm -A -t 192.168.226.100:80 -s wrr
- 作用:创建一个虚拟服务,监听
VIP:80,使用 加权轮询(wrr) 调度算法。 - 为什么这么做:建立
LVS的核心账本,告诉系统以后遇到访问192.168.226.100:80的请求,要按照权重轮流分配。
4. ipvsadm -a -t 192.168.226.100:80 -r 192.168.226.20:80 -g -w 2
5. ipvsadm -a -t 192.168.226.100:80 -r 192.168.226.30:80 -g -w 1
- 作用:把两台 RS 加入集群。
- 关键参数
-g:这是 DR 模式(Gatewaying) 的标志。它告诉LVS:只修改数据包的 目标 MAC 地址,不要修改IP,直接把包“扔”给后端真实服务器。 - 权重
-w:.20权重是 2,.30权重是 1。表示每 3 个请求,2 个给.20,1 个给.30。
==核心总结==:route add -host $vip lo:0 的本质
1. 本质定义:一个“流量拦截器”
这条路由指令并不是决定数据包最终的物理去向,而是在 Linux 内核的路由表中强行插入了一条规则:凡是目标 IP为 VIP 的数据包,一律不准直接走物理网卡,必须先进入本地回环接口 lo:0。
2. 核心作用:避免物理网络的“IP 冲突”
为什么要多此一举?因为 RS 在回包时,源 IP 必须是 VIP。如果这个包直接从物理网卡发出去,局域网内的交换机和网关会同时看到 LVS 和 RS 都在使用 VIP,引发严重的 ARP 冲突和网络瘫痪。这条命令把流量关进 lo:0(死胡同),完美避开了物理网络的干扰。
3. 底层机制:内核的“二次路由”与“偷梁换柱”
数据包进入 lo:0 后,并没有消失。Linux 内核的Netfilter/路由子系统会拦截这个包,发现它其实是发给外部客户端的。于是内核在内部自动完成“二次路由”:
- 重新查表,找到通往客户端的真正出口(通常是内网网关)。
- 把数据包重新定向到物理网卡,最终成功发送给网关,且不给局域网添乱。
4. 最终闭环:LVS-DR 高性能的基石
这条命令与 ifconfig lo:0 $vip 以及 ARP抑制参数配合,让 RS 实现了完美的“隐身收发”:
- 收得进:靠 VIP 绑在
lo:0上,接收LVS转发的请求。 - 发得出:靠这条主机路由,在内部绕行后从物理网卡直接把响应发给网关。
- 不冲突:全程不把 VIP 暴露在物理二层网络上,确保了
LVS调度器的绝对权威。
一句话概括:
route add -host $vip lo:0 就是给 RS 画了一条“内部单行道”,让 VIP 的流量在本地回环里绕个弯,从而安全、高效地完成响应,彻底解放了 LVS 的带宽压力。
2.如果没有 route add -host $vip lo:0 这条命令,回包会从哪走?
是从 RS 自己的物理网卡(比如ens160) 回去。
模拟一下没有这条路由时,RS1(真实服务器)的尴尬处境:
RS1收到了LVS发来的请求,准备把网页发给客户端。RS1构造响应包:源IP是 VIP(192.168.226.100),目标IP是客户端(192.168.226.1)。- Linux 系统要发包,会去查本机的路由表:“我要去 192.168.226.1,该从哪个网卡出去?”
- 因为 RS1 的物理网卡(ens160)正好在
192.168.226.0/24网段,系统会默认选择从物理网卡 ens160 发出去。 - 灾难发生了:这个包一旦从物理网卡发出去,源
IP就是192.168.226.100(VIP)。 - 局域网里的交换机和网关看到这个包,会瞬间懵圈:“VIP 不是绑在
LVS身上的吗?怎么RS1也在用这个IP发包?”。这会导致网络里出现IP地址冲突 /ARP混乱,整个LVS集群可能会直接瘫痪断网。
# 1. 定义 VIP 变量(方便后续调用)
vip=192.168.226.100
# 2. 在回环接口 lo:0 上绑定 VIP,子网掩码必须是 255.255.255.255
ifconfig lo:0 $vip broadcast 192.168.226.255 netmask 255.255.255.255 up
# 3. 添加一条去往 VIP 的主机路由,确保响应流量走 lo:0 返回
route add -host $vip lo:0
# 4. 配置 ARP 抑制参数(防止 RS 抢 LVS 的 VIP),分两行写入
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


三、 从“外网/内网”角度的最终对比总结
作为客户端,从网络架构看,区别天壤之别:
| 对比维度 | NAT 模式 | DR 模式 |
|---|---|---|
客户端看到的源IP |
永远是 VIP | 永远是 VIP |
LVS 在回包时的角色 |
绝对主力(必须经手回包并改源 IP) |
隐身人(响应包直接由 RS 发给网关) |
| 网络结构要求 | LVS和 RS 可以不在同一网段 |
LVS和 RS 必须在同一网段(同一个二层网络) |
| 外网到内网的流向 | 客户端->LVS->RS->LVS->客户端 |
客户端->LVS->RS->网关->客户端 |
| 谁负责流量瓶颈 | LVS(压力大,因为要处理所有进出流量) |
RS 和网关(LVS只处理很小的请求包) |
| 生产环境适用场景 | 适合并发量不太高的场景,或者内网服务器需要连接外网 | 互联网大厂主流,适合高并发、大流量的负载均衡 |
一句话帮您彻底记住:
- NAT 模式:
LVS是全包圆的苛刻包工头,所有活儿(进和出)都要经过它。
Keepalived+LVS+httpd实现网站负载均衡高可用
这套架构的目的是:让用户通过访问一个虚拟的IP(VIP),自动把请求分摊到两台真实服务器上;如果主调度器挂了,备用调度器会自动顶上,保证服务不中断。
# 设备详细:
server1(DS1):192.168.226.10
server2(DS2):192.168.226.20
server3(RS1):192.168.226.30
server4(RS2):192.168.226.40
VIP:192.168.226.100(单主模型)
#在server3和server4执行下面的脚本:
#!/bin/bash
yum install httpd net-tools -y
systemctl stop firewalld
setenforce 0
vip="192.168.226.100"
mask="255.255.255.255"
ifconfig lo:0 $vip broadcast $vip netmask $mask up
route add -host $vip lo:0
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
echo "<h1>This is RS1</h1>" > /var/www/html/index.html # 修改不同的主页以便测试!
systemctl start httpd
# 在server1和server2上配置keepalived(包含LVS的配置)
[root@server2 ~]# vim /etc/keepalived/keepalived.conf # 最好事先备份一下mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
! Configuration File for keepalived
global_defs {
router_id server1 # 设置lvs的id,一个网络中应该唯一
}
vrrp_instance VI_1 {
state MASTER # 指定Keepalived的角色
interface ens160 # 网卡
virtual_router_id 10 # 虚拟路由器ID,主备需要一样
priority 150 # 优先级越大越优,backup路由器需要设置比这小!
advert_int 1 # 检查间隔1s
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.226.100 # 定义虚拟IP地址,可以定义多个
}
}
# 定义虚拟主机,对外服务的IP和port
virtual_server 192.168.226.100 80 {
delay_loop 6 # 设置健康检查时间,单位是秒
lb_algo wrr # 负责调度算法
lb_kind DR # LVS负载均衡机制
persistence_timeout 0
protocol TCP
# 指定RS主机IP和port
real_server 192.168.226.30 80 {
weight 2
# 定义TCP健康检查
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.226.40 80 {
weight 1
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
}
第一部分:全局定义 (global_defs)
nginx
global_defs {
router_id server1
}
- 作用:定义这台
Keepalived节点的身份标识。 router_id server1:给这台机器起个名字。在同一个局域网的高可用集群中,每台机器的router_id必须唯一。- 注意:你在
server2上操作,如果server2是备机,改成router_id server2以防混淆
- 注意:你在
第二部分:VRRP 实例配置 (vrrp_instance VI_1)
这是Keepalived 实现故障自动切换(高可用)的核心部分。
nginx
vrrp_instance VI_1 {
state MASTER
interface ens160
virtual_router_id 10
priority 150
advert_int 1
authentication { ... }
virtual_ipaddress { 192.168.226.100 }
}
state MASTER:定义这台机器的初始角色是主节点。另一台备机(server2)这里必须写state BACKUP。interface ens160:指定虚拟IP(VIP)绑定在哪块物理网卡上。我的网卡名是ens160。virtual_router_id 10:虚拟路由组 ID。主备两台机器这个数字必须完全一致(都是 10),它们才能互相识别为同一组。priority 150:优先级。数字越大,越容易成为主节点(Master)。备机的优先级必须小于 150(比如设成 100)。当 Master 宕机不再发送心跳时,Backup 会根据优先级自动抢占成为新的 Master。advert_int 1:心跳广播间隔,1 秒一次。Master 每隔 1 秒告诉 Backup “我还活着”。authentication:主备通信的密码认证(密码是1111),主备必须一致。virtual_ipaddress { 192.168.226.100 }:最关键的配置。这就是对外提供服务的虚拟IP(VIP)。正常情况下,这个IP绑定在 Master 机器上;Master 宕机后,这个IP会瞬间“漂移”到 Backup 机器上。
第三部分:LVS 虚拟服务器与真实服务器配置 (virtual_server)
这部分是LVS 负载均衡 的核心。它告诉系统如何分发到达 VIP 的流量。
nginx
virtual_server 192.168.226.100 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
persistence_timeout 0
protocol TCP
virtual_server 192.168.226.100 80:定义了一个虚拟服务,监听 VIP 的 80 端口。delay_loop 6:健康检查间隔,每 6 秒检查一次后端服务器是否存活。lb_algo wrr:加权轮询(Weighted Round Robin)算法。意思是根据服务器的性能分配请求,性能好的(权重高)多分点请求。lb_kind DR:直接路由(Direct Routing)模式。这是LVS性能最高的模式(请求发给LVS,LVS只改 MAC 地址丢给后端服务器,后端服务器直接把结果返回给客户端,不再经过LVS)。persistence_timeout 0:会话保持时间。设为 0 表示不保持(同一个用户的请求可能分发给不同的后端服务器)。如果需要保持登录状态,可以设为 600(10分钟)。protocol TCP:指定服务协议。
真实服务器定义 (real_server)
在 virtual_server 内部,定义了两台后端真实提供 Web 服务的机器:
nginx
real_server 192.168.226.30 80 {
weight 2
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.226.40 80 {
weight 1
TCP_CHECK { ... }
}
real_server 192.168.226.30 80:第一台真实服务器的IP和端口。weight 2:权重为 2。real_server 192.168.226.40 80:第二台真实服务器的IP和端口。weight 1:权重为 1。- 分配比例:因为权重是
2:1,所以LVS会把 2/3 的请求发给 .30,1/3 的请求发给 .40。 TCP_CHECK:健康检查机制。LVS会每隔 6 秒(delay_loop)去尝试连接这两台机器的 80 端口。如果连不上(超时 10 秒,重试 3 次),LVS就会自动把这台挂掉的服务器从列表中剔除,不再往它上面发请求,直到它恢复正常。
核心警告:LVS-DR 模式的“隐藏要求”
配置里用了 lb_kind DR。配置完这个文件,结果发现访问 VIP 根本不通。因为 DR 模式必须在后端真实服务器(192.168.226.30 和 .40)上也做特殊配置:
- 抑制
ARP广播:必须在后端服务器的网卡上关闭ARP广播(修改内核参数arp_ignore和arp_announce)。否则,客户端寻找 VIP(.100)时,后端服务器会回应,导致客户端直接连到了后端服务器,绕过了调度器(这就失去了负载均衡的意义)。 - 绑定 VIP 到本地回环口:后端服务器需要在
lo:0上绑定 VIP192.168.226.100,这样它才能接收目标地址是 VIP 的数据包。
总结与避坑指南
- 主备配置差异:在
server2上配置时,必须修改:state BACKUP、priority 100(比 150 小)、router_id server2。其他的保持不变。 - 多台机器准备:
- 调度器(
LVS):192.168.226.20和192.168.226.10(主备) - 真实服务器(RS):
192.168.226.30和192.168.226.40(需要安装并启动Nginx/Apache,且配置好 DR 模式) - 虚拟
IP(VIP):192.168.226.100
- 调度器(
- 启动与测试:配置完成后,重启
Keepalived服务(systemctl restart keepalived)。此时在调度器上执行ip a,你应该能看到ens160上多出了一个192.168.226.100的IP。然后用浏览器访问http://192.168.226.100,测试负载均衡和高可用是否生效。
实践操作
📁 .bak 文件作用标准总结
1. 核心定义
.bak 是英文 backup 的缩写,意为“备份”。在 Linux 系统中,它通常作为文件的后缀名(如 keepalived.conf.bak),用来标识这是一个配置文件的副本。
2. 标准操作规范(最佳实践)
-
改前必做:修改任何系统或服务的关键配置文件(如
nginx.conf、keepalived.conf、/etc/fstab等)之前,必须先备份。 -
mv file file.bak:原文件被移走,需要手动新建空文件。若忘记新建,服务会直接报错- 日志与 VIP 生效(最关键的部分):
- 看日志区域,有几行非常关键的提示:
Sending gratuitous ARP on ens160 for 192.168.226.100:Keepalived 正在向局域网发送广播(ARP),告诉所有设备:“虚拟IP 192.168.226.100 现在在我这台机器上!” 这样客户端访问 .100 时,流量才会导过来。TCP connection to [192.168.226.40]:tcp:80 success和[192.168.226.30]:tcp:80 success:LVS 健康检查成功! 这说明你的后端真实服务器(RS: .30 和 .40)的 80 端口是通的,LVS 已经成功识别到了这两台服务器。
- 看日志区域,有几行非常关键的提示:
- IP 验证:
- 最后执行
ip a,你可以看到ens160网卡上不仅有原本的 IP192.168.226.10,还多出了 VIP192.168.226.100。这证明主机已经成功接管了虚拟IP。
- 最后执行
总结:第一张图代表主节点已经完美上线,正在对外提供服务,并且成功接管了虚拟
IP - 日志与 VIP 生效(最关键的部分):
BACKUP节点
- 日志解读:
- 备机的日志通常没有主机的“Sending gratuitous
ARP”那么活跃,因为它不需要发广播宣告主权。它此时正在默默接收主机发来的心跳包(VRRP广播)。
- 备机的日志通常没有主机的“Sending gratuitous
总结:第二张图代表备节点已经处于待命状态,配置正确,随时准备在主机故障时接管业务。





HAProxy介绍
HAProxy 是一款广泛使用的高性能开源软件,主要用于提供高可用性、负载均衡以及基于 TCP 和 HTTP 的应用代理。
解决lua环境
HAProxy 支持基于lua实现功能扩展,lua是一种小巧的脚本语言,于1993年由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 官网:www.lua.org
-
Lua 应用场景
- 游戏开发
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件,如MySQL Proxy
- 安全系统,如入侵检测系统
-
安装基础命令及编译依赖环境
[root@localhost ~]# yum -y install gcc readline-devel
[root@localhost ~]# wget http://www.lua.org/ftp/lua-5.3.5.tar.gz
[root@localhost ~]# tar xvf lua-5.3.5.tar.gz -C /usr/local/src
[root@localhost ~]# cd /usr/local/src/lua-5.3.5
[root@localhost lua-5.3.5]# make linux test
编译安装HAProxy
HAProxy2.0以上版本编译参数
[root@localhost ~]# yum -y install gcc openssl-devel pcre-devel systemd-devel
[root@localhost ~]# wget http://www.haproxy.org/download/2.1/src/haproxy-2.1.3.tar.gz
[root@localhost ~]# tar xvf haproxy-2.1.3.tar.gz -C /usr/local/src
[root@localhost ~]# cd /usr/local/src/haproxy-2.1.3/
[root@localhost haproxy-2.1.3]# cat README
- 参考INSTALL文件进行编译安装
[root@localhost haproxy-2.1.3]# make -j 2 ARCH=x86_64 TARGET=linux-glibc
USE_PCRE=1
USE_OPENSSL=1
USE_ZLIB=1
USE_SYSTEMD=1
USE_LUA=1
LUA_INC=/usr/local/src/lua-5.3.5/src/
LUA_LIB=/usr/local/src/lua-5.3.5/src/
[root@localhost haproxy-2.1.3]# make install PREFIX=/apps/haproxy
[root@localhost haproxy-2.1.3]# ln -s /apps/haproxy/sbin/haproxy /usr/sbin/
- 查看生成的文件
[root@localhost ~]# tree -C /apps/haproxy/
/apps/haproxy/
├── doc
│ └── haproxy
│ ├── 51Degrees-device-detection.txt
│ ├── architecture.txt
│ ├── close-options.txt
│ ├── configuration.txt
│ ├── cookie-options.txt
│ ├── DeviceAtlas-device-detection.txt
│ ├── intro.txt
│ ├── linux-syn-cookies.txt
│ ├── lua.txt
│ ├── management.txt
│ ├── netscaler-client-ip-insertion-protocol.txt
│ ├── network-namespaces.txt
│ ├── peers.txt
│ ├── peers-v2.0.txt
│ ├── proxy-protocol.txt
│ ├── regression-testing.txt
│ ├── seamless_reload.txt
│ ├── SOCKS4.protocol.txt
│ ├── SPOE.txt
│ └── WURFL-device-detection.txt
├── sbin
│ └── haproxy
└── share
└── man
└── man1
└── haproxy.1
6 directories, 22 files
验证HAProxy版本
[root@localhost ~]# which haproxy
/usr/sbin/haproxy
[root@localhost ~]# haproxy -v
HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2021.
Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html
[root@localhost ~]# haproxy -vv
HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2021.
Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html
Build options :
TARGET = linux-glibc
CPU = generic
CC = gcc
CFLAGS = -m64 -march=x86-64 -O2 -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-old-style-declaration -Wno-ignored-qualifiers -Wno-clobbered -Wno-missing-field-initializers -Wtype-limits
OPTIONS = USE_PCRE=1 USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_SYSTEMD=1
Feature list : +EPOLL -KQUEUE -MY_EPOLL -MY_SPLICE +NETFILTER +PCRE -PCRE_JIT -PCRE2 -PCRE2_JIT +POLL -PRIVATE_CACHE +THREAD -PTHREAD_PSHARED -REGPARM -STATIC_PCRE -STATIC_PCRE2 +TPROXY +LINUX_TPROXY +LINUX_SPLICE +LIBCRYPT +CRYPT_H -VSYSCALL +GETADDRINFO +OPENSSL +LUA +FUTEX +ACCEPT4 -MY_ACCEPT4 +ZLIB -SLZ +CPU_AFFINITY +TFO +NS +DL +RT -DEVICEATLAS -51DEGREES -WURFL +SYSTEMD -OBSOLETE_LINKER +PRCTL +THREAD_DUMP -EVPORTS
Default settings :
bufsize = 16384, maxrewrite = 1024, maxpollevents = 200
Built with multi-threading support (MAX_THREADS=64, default=4).
Built with OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
Running on OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2
Built with Lua version : Lua 5.3.5
Built with network namespace support.
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
Built with PCRE version : 8.32 2012-11-30
Running on PCRE version : 8.32 2012-11-30
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.7
Running on zlib version : 1.2.7
Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Available polling systems :
epoll : pref=300, test result OK
poll : pref=200, test result OK
select : pref=150, test result OK
Total: 3 (3 usable), will use epoll.
Available multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
h2 : mode=HTTP side=FE|BE mux=H2
fcgi : mode=HTTP side=BE mux=FCGI
<default> : mode=HTTP side=FE|BE mux=H1
<default> : mode=TCP side=FE|BE mux=PASS
Available services : none
Available filters :
[SPOE] spoe
[CACHE] cache
[FCGI] fcgi-app
[TRACE] trace
[COMP] compression
HAProxy启动脚本
[root@localhost ~]# vim /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
[Service]
ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
[Install]
WantedBy=multi-user.target
- 默认缺少配置文件,无法启动
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start haproxy
Job for haproxy.service failed because the control process exited with error code. See "systemctl status haproxy.service" and "journalctl -xe" for details.
[root@localhost ~]# tail /var/log/messages
Jul 13 10:39:33 localhost systemd: Starting HAProxy Load Balancer...
Jul 13 10:39:33 localhost haproxy: [ALERT] 193/103933 (8156) : Cannot open configuration file/directory /etc/haproxy/haproxy.cfg : No such file or directory
Jul 13 10:39:33 localhost systemd: haproxy.service: control process exited, code=exited status=1
Jul 13 10:39:33 localhost systemd: Failed to start HAProxy Load Balancer.
Jul 13 10:39:33 localhost systemd: Unit haproxy.service entered failed state.
Jul 13 10:39:33 localhost systemd: haproxy.service failed.
配置文件
- 查看配置文件范例
[root@localhost ~]# tree -C /usr/local/src/haproxy-2.1.3/examples/
/usr/local/src/haproxy-2.1.3/examples/
├── acl-content-sw.cfg
├── content-sw-sample.cfg
├── errorfiles
│ ├── 400.http
│ ├── 403.http
│ ├── 408.http
│ ├── 500.http
│ ├── 502.http
│ ├── 503.http
│ ├── 504.http
│ └── README
├── haproxy.init
├── option-http_proxy.cfg
├── socks4.cfg
├── transparent_proxy.cfg
└── wurfl-example.cfg
1 directory, 15 files
- 创建自定义的配置文件
[root@localhost ~]# mkdir /etc/haproxy
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
global
maxconn 100000
chroot /apps/haproxy
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
#uid 99
#gid 99
user haproxy
group haproxy
daemon
#nbproc 4
#cpu-map 1 0
#cpu-map 2 1
#cpu-map 3 2
#cpu-map 4 3
pidfile /var/lib/haproxy/haproxy.pid
log 127.0.0.1 local2 info
defaults
option http-keep-alive
option forwardfor
maxconn 100000
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
mode http
bind 0.0.0.0:9999
stats enable
log global
stats uri /haproxy-status
stats auth admin:123456
listen web_port
bind 192.168.226.10:80
mode http
log global
server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
启动haproxy
[root@localhost ~]# mkdir /var/lib/haproxy
[root@localhost ~]# useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy
[root@localhost ~]# systemctl enable --now haproxy
编译安装HAProxy
[root@server1 haproxy-2.1.3]# make -j 2 ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_SYSTEMD=1 USE_LUA=1 LUA_INC=/usr/local/src/lua-5.3.5/src/ LUA_LIB=/usr/local/src/lua-5.3.5/src/
HAProxy中的bind用法
1. 监听所有 IP 的指定端口
frontend http_front
bind *:80 # 监听所有 IPv4 地址的 80 端口
bind :::80 # 监听所有 IPv6 地址的 80 端口(双栈支持)
bind *:443 ssl # 监听 443 端口并启用 SSL(需配合证书配置)
2. 监听特定 IP 地址
frontend web_front
bind 192.168.1.100:80 # 仅通过 192.168.1.100 接收 80 端口请求
bind 203.0.113.5:443 # 仅通过公网 IP 203.0.113.5 接收 HTTPS 请求
3. 监听端口范围
frontend app_front
bind *:8000-8010 # 监听 8000 到 8010 之间的所有端口
4. 配置 SSL/TLS 加密(HTTPS)
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
# 说明:
# - ssl:启用 SSL 加密
# - crt:指定 PEM 格式的证书文件(包含证书和私钥,需合并为一个文件)
5. 限制协议类型
frontend tcp_front
bind *:8080 proto tcp # 仅接收 TCP 协议(默认)
mode tcp # 配合 mode 指令使用
frontend udp_front
bind *:53 proto udp # 接收 UDP 协议(如 DNS 服务)
mode udp
6. 配置监听队列与连接限制
frontend high_traffic_front
bind *:80 backlog 10000 maxconn 5000
# 监听队列大小 10000,最大并发连接 5000
7. 绑定到特定网络接口
frontend lan_front
bind eth0:80 # 仅通过局域网网卡 eth0 接收 80 端口请求
8. 结合 listen 块使用
listen web_service
bind *:80
mode http
server web1 192.168.1.101:80 check
server web2 192.168.1.102:80 check
HAProxy的统计报告页面(Stats Page)

简单来说,它的作用和意义如下:
- 是什么:它是 HAProxy 自带的一个内置可视化网页,用来展示当前负载均衡器的实时运行数据和健康状态。
- 干啥的:用来监控和排错。通过它,你可以直观地看到有哪些前端(
Frontend)在接收流量、后端(Backend)有哪些服务器、这些服务器当前是正常的(绿色 UP)还是故障的(红色 DOWN)、连接数有多少、流量有多大等。
HAProxy的配置文件内容
文件路径为 /etc/haproxy/haproxy.cfg
HAProxy是一款广泛使用的高性能开源软件,主要用于提供高可用性、负载均衡以及基于 TCP 和 HTTP 的应用代理。
1. global 段(全局配置)
这一部分主要设置 HAProxy 进程级别的参数,影响整个服务的运行。
maxconn 100000: 设定每个 HAProxy 进程可接受的最大并发连接数为 100,000。chroot /apps/haproxy: 将 HAProxy 的工作目录切换到/apps/haproxy。这是一种安全措施(沙盒机制),即使 HAProxy 被攻破,攻击者也只能访问该目录下的文件。stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin: 定义一个 Unix Socket 用于管理。权限设为600(仅所有者读写),权限级别为admin(允许执行所有管理命令)。#uid 99和#gid 99:这两行被注释掉了(前面有#)。原本用于指定运行 HAProxy 的用户 ID 和组 ID。user haproxy和group haproxy: 指定 HAProxy 进程运行的用户和组为haproxy。出于安全考虑,不应该使用 root 用户运行。daemon: 让 HAProxy 以守护进程(后台)模式运行。#nbproc 4及随后的#cpu-map ...: 这些都被注释掉了。原本用于设置启动 4 个 HAProxy 进程,并将它们绑定到不同的 CPU 核心上(多核优化)。pidfile /var/lib/haproxy/haproxy.pid: 指定存放 HAProxy 进程 ID (PID) 的文件路径。log 127.0.0.1 local2 info: 配置日志输出。将日志发送到本地回环地址(127.0.0.1)的local2设施(Facility),日志级别为info。
2. defaults 段(默认配置)
这一部分为后续的 listen、frontend 和 backend 段提供默认参数。
option http-keep-alive: 开启 HTTP 长连接(Keep-Alive),允许在同一个 TCP 连接上发送多个 HTTP 请求,减少延迟。option forwardfor: 如果后端服务器需要记录客户端的真实 IP 地址,HAProxy 会在 HTTP 请求头中插入X-Forwarded-For字段。maxconn 100000: 设定每个前端(frontend)或监听器(listen)的最大并发连接数。mode http: 设置默认的工作模式为 HTTP(七层代理)。其他模式还有tcp(四层代理)。timeout connect 300000ms: 连接后端服务器的超时时间设为 300,000 毫秒(即 5 分钟)。timeout client 300000ms: 客户端连接的空闲超时时间设为 300,000 毫秒。timeout server 300000ms: 后端服务器连接的空闲超时时间设为 300,000 毫秒。
3. listen stats 段(统计监控配置)
这一部分定义了一个用于查看 HAProxy 运行状态和统计信息的 Web 页面。
mode http: 该监听器工作在 HTTP 模式。bind 0.0.0.0:9999: 监听所有网卡的 9999 端口。stats enable: 启用统计报告功能。log global: 使用全局配置中定义的日志设置。stats uri /haproxy-status: 设置访问统计页面的 URL 路径为/haproxy-status(例如:http://<IP>:9999/haproxy-status)。stats auth admin:123456: 设置访问统计页面的认证信息,用户名为admin,密码为123456。
4. listen web_port 段(业务负载均衡配置)
这一部分定义了一个具体的负载均衡监听器,用于转发 Web 流量。
bind 192.168.226.10:80: 监听本机的192.168.226.10地址的 80 端口(HTTP 标准端口)。mode http: 工作在 HTTP 模式。log global: 使用全局日志设置。server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5: 定义后端服务器。- 名称:
web1 - 地址:
127.0.0.1:8080(本地回环地址的 8080 端口)。 check: 开启健康检查。inter 3000: 每 3000 毫秒(3秒)检查一次。fall 2: 如果连续 2 次检查失败,则认为该服务器不可用(下线)。rise 5: 如果连续 5 次检查成功,则认为该服务器恢复正常(上线)。
- 名称:
总结
这个配置文件的逻辑是:
- 全局:设置基本的安全、日志和性能参数。
- 默认:设定通用的 HTTP 超时和代理选项。
- Stats:在 9999 端口提供一个带密码保护的监控页面。
- 业务:在
192.168.226.10的 80 端口对外提供服务,将流量转发给本地8080端口上的一个 Web 服务(web1),并带有健康检查机制。
- 业务:在
日志配置
HAProxy配置
# 在global配置项定义
log 127.0.0.1 local{1-7} info #基于syslog记录日志到指定设备,级别有(err、warning、info、debug)
listen web_port
bind 127.0.0.1:80
mode http
log global #开启当前web_port的日志功能,默认不记录日志
server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
# systemctl restart haproxy
Rsyslog配置
[root@server1 ~]# vim /etc/rsyslog.conf
$ModLoad imudp
$UDPServerRun 514
$ModLoad imtcp
$InputTCPServerRun 514
..........
local3.* /var/log/haproxy.log
[root@server1 ~]# systemctl restart rsyslog.service
[root@server1 ~]# vim /etc/haproxy/haproxy.cfg
global
log 127.0.0.1 local3 info
listen web_port_81
log global
[root@server1 ~]# systemctl restart haproxy.service
[root@server1 ~]# tail -f /var/log/haproxy.log
将 HAProxy 的日志通过 Rsyslog 记录到本地文件中。简单来说:HAProxy配置中的 local3 是“发送方”的标签,而Rsyslog 中的两行是“接收方”的规则。 它们必须配合使用才能让日志正常落盘。这台服务器同时通过 UDP 和TCP 协议在 514 端口接收外部发来的日志
1. HAProxy 配置文件中的 log 127.0.0.1 local3 info
在 global 段中,你修改了日志配置:
haproxy
log 127.0.0.1 local3 info
127.0.0.1:HAProxy本身不直接将日志写入硬盘文件(为了提高性能和避免 I/O 阻塞),而是将日志发送给系统的日志服务(Rsyslog)。这里指定发送到本机的Rsyslog服务。local3: 这是 Syslog 的设施(Facility)。Linux 系统将日志来源分类为不同的“设施”,如auth(认证)、cron(定时任务)、mail(邮件)等。local0到local7是预留给用户自定义程序使用的。这里 HAProxy 选择了local3作为自己的专属标签。info: 这是日志级别(Level)。表示记录info级别及更高级别(如 warning, error 等)的日志。- 意义:告诉
HAProxy:“把你产生的日志,打上local3的标签,发送给本地的Rsyslog服务
2. Rsyslog 配置文件中的两行
在 /etc/rsyslog.conf 文件的 #### RULES #### 部分下方(大约第 68-69 行),你增加了:
rsyslog
local3.* /var/log/haproxy.log
local3.* ~
-
local3.*: 这里的*代表所有级别(包括 info, warning, error 等)。意思是匹配所有带有local3标签的日志消息。 -
/var/log/haproxy.log: 动作指令。意思是:“将所有匹配到的日志,写入到/var/log/haproxy.log这个文件中”。 -
local3.* ~: 这里的波浪号~是 Rsyslog 中的一个特殊动作,代表 丢弃(Discard)。 -
意义:
- 第一行定义了日志的存储位置。
- 第二行非常关键:因为
Rsyslog的规则是从上往下匹配的,如果不加~,这些local3的日志不仅会写入/var/log/haproxy.log,还会继续向下匹配,最终被写入系统的/var/log/messages中。加上~就是为了防止日志重复记录,告诉Rsyslog:“把这些local3的日志存到指定文件后,就不要再往下传递处理了。”
3. ModLoad imudp (加载 UDP 模块)
ModLoad: 这是 Rsyslog 的指令,意思是“加载模块”(Module Load)。imudp: 代表 Input ModuleUDP(UDP输入模块)。- 意义:
Rsyslog默认情况下只接收本机系统产生的日志(通过 Unix Socket 或本地机制)。==如果你想让它接收通过网络传过来的日志(比如另一台服务器发来的),你必须加载相应的网络接收模块==。这行代码就是告诉Rsyslog:“准备好接收UDP协议的网络日志”。
4. UDPServerRun 514 (在 514 端口运行UDP 服务)
UDPServerRun: 这是配合imudp模块使用的指令,意思是“启动UDP服务器并监听”。514: 这是 Syslog 协议的标准端口号。- 意义: 告诉
Rsyslog:“在 514 端口上监听,等待其他设备发送UDP日志过来”。
因为写的是 127.0.0.1(本机),所以 HAProxy 是通过本地内部机制把日志交给Rsyslog 的,并不需要走网络UDP。

总结
这是一个典型的 “谁产生,谁打标;谁接收,谁分流” 的流程:
HAProxy产生日志,并贴上local3的标签发送给Rsyslog。- Rsyslog 接收日志,看到标签是
local3。 - 根据
Rsyslog的规则,Rsyslog把带有local3的日志提取出来,写入到/var/log/haproxy.log文件中,并阻止它们混入系统的常规日志里。


因为 ModLoad 和 UDPServerRun 是两个完全不同的指令,它们之间必须用换行(或者至少是空格)分隔开,否则系统会把它们当成一个整体,导致识别失败。

frontend+backend配置实例

haproxy配置文件
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
global
maxconn 100000
chroot /apps/haproxy
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
#uid 99
#gid 99
user haproxy
group haproxy
daemon
#nbproc 4
#cpu-map 1 0
#cpu-map 2 1
#cpu-map 3 2
#cpu-map 4 3
pidfile /var/lib/haproxy/haproxy.pid
log 127.0.0.1 local3 info
defaults
option http-keep-alive
option forwardfor
maxconn 100000
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
mode http
bind 0.0.0.0:9999
stats enable
log global
stats uri /haproxy-status
stats auth admin:123456
frontend test-http
bind *:80
mode tcp
use_backend test-http-nodes
backend test-http-nodes
mode tcp
default-server inter 1000 weight 6
server web1 192.168.226.20:80 check inter 3000 fall 3 rise 5 weight 2 addr 192.168.226.20 port 3306
server web2 192.168.226.30:80 check inter 3000 fall 3 rise 5
[root@haproxy ~]# systemctl restart haproxy

一、 Frontend(前端模块):负责“接客”
代码位置: 第 41 – 44 行
haproxy
frontend test-http
bind *:80
mode tcp
use_backend test-http-nodes
前端模块的作用是定义 HAProxy 对外的入口,即监听哪个端口、接收什么流量,并把流量转发给谁。
frontend test-http:- 定义了一个名为
test-http的前端。名字是自定义的,方便管理。
- 定义了一个名为
bind *:80:- 让 HAProxy 监听本机所有网卡(
*)的 80 端口。任何发往这台机器 80 端口的请求都会被这个前端捕获。
- 让 HAProxy 监听本机所有网卡(
mode tcp:- 重点注意:这里设置的是 TCP 模式(四层代理)。
- 这意味着 HAProxy 只是单纯地转发 TCP 数据包,不会去解析 HTTP 协议的内容(比如它看不懂 HTTP 头部,不知道 URL 是什么,也看不到 Cookie)。它只认 IP 和端口。
use_backend test-http-nodes:- 这是核心动作指令:告诉前端,“所有发到我这里的流量,请全部转交给名为
test-http-nodes的后端去处理。” - (注:因为这里是全局转发,没有写复杂的
ACL规则。如果是 HTTP 模式,这里通常配合acl规则实现根据域名或路径分发。)
- 这是核心动作指令:告诉前端,“所有发到我这里的流量,请全部转交给名为
二、 Backend(后端模块):负责“干活”
代码位置: 第 47 – 52 行
haproxy
backend test-http-nodes
mode tcp
default-server inter 1000 weight 6
server web1 192.168.226.20:80 check inter 3000 fall 3 rise 5 weight 2 addr 192.168.226.20 port 3306
server web2 192.168.226.30:80 check inter 3000 fall 3 rise 5
后端模块的作用是定义真实的后端服务器集群,以及如何对它们进行负载均衡和健康检查。
-
backend test-http-nodes:- 定义了一个名为
test-http-nodes的后端。这个名字必须和前面前端里use_backend指定的名字完全一致,这样才能对接上。
- 定义了一个名为
-
mode tcp:- 同样设置为 TCP 模式,必须和前端的模式保持一致(或兼容)。
-
default-server inter 1000 weight 6:- 这是给下面所有
server设置的默认参数。 - 意思是:如果在下面的
server行里没有单独写健康检查间隔(inter),默认就是 1000 毫秒检查一次;如果没有单独写权重(weight),默认权重是 6。
- 这是给下面所有
-
server web1 ...(第一台后端服务器):web1: 服务器名称。192.168.226.20:80: 声明服务器地址是192.168.226.20,端口是80。这是 HAProxy 尝试连接的初始目标。check: 开启对该服务器的健康检查。inter 3000: 覆盖了默认值。实际检查间隔是 3000 毫秒(3秒)。fall 3: 连续失败 3 次,判定该服务器宕机(剔除)。rise 5: 连续成功 5 次,判定该服务器恢复(重新加入集群)。weight 2: 覆盖了默认值。该服务器的权重是 2(数值越小,分配到的流量比例越小)addr 192.168.226.20 port 3306: 这是这行代码最大的疑点/坑点!- 在 HAProxy 中,如果在
server名字后面写了 IP 和端口,再使用addr和port参数,HAProxy 会以后面的addr和port为准! - 也就是说,
HAProxy虽然看到你写了:80,但最终它会去连接192.168.226.20的 3306 端口(通常是MySQL数据库端口),而不是 80 端口。
-
server web2 ...(第二台后端服务器):- 地址是
192.168.226.30:80。 - 健康检查参数和
web1一样(除了权重未指定,会继承默认的weight 6)。
- 地址是
整体工作流:
- 客户端访问
192.168.226.10:80。 - 被
frontend test-http捕获。 - 前端把请求转给
backend test-http-nodes。 - 后端中有两台服务器
web1和web2。HAProxy会根据权重和健康状态,在四层(TCP)将请求分发给这两台服务器。

核心结论:*:80 的绑定逻辑
可以用一句话精准概括:
当 HAProxy 配置了
bind *:80时,只要目标 IP 是这台 HAProxy 服务器自身拥有的(配置在网卡上的),无论通过哪个 IP 访问其 80 端口,HAProxy 都会接管并处理该请求。
详细拆解说明:
1. 什么是“HAProxy 上有的 IP”?
就是指在这台 HAProxy 机器的操作系统里(通过 ip addr 或 ifconfig 命令能看到的)实际配置的 IP 地址。通常包括:
- 对外业务 IP:例如
192.168.226.10(外部浏览器和客户端用它来访问)。 - 本机回环 IP:例如
127.0.0.1(在本机内部执行curl localhost时使用)。 - 如果机器上还有其他虚拟网卡(如 Docker 网桥 IP
172.17.0.1),也包含在内。
2. * 号的作用范围:
* 是通配符,代表“这台机器上的所有本地 IP”。HAProxy 会在所有这些 IP 的 80 端口上竖起耳朵监听。
3. 界限非常明确(仅限于此):
如果外部访问的 IP不是这台 HAProxy机器上配置的IP(比如您去访问 192.168.226.99:80,而该 IP 并不在HAProxy 的网卡上),这个网络请求根本不会到达HAProxy机器,自然也就无法被 *:80 接管,会直接超时或报错。
4. 端口限定(只限 80):
*:80 仅仅接管 80 端口,绝对不会暴露机器上的其他端口(如 SSH 的 22,数据库的 3306)。
一句话实战总结:
在您的实验环境中,写成 bind *:80,就相当于开启了“全频道接收”模式。无论是别人从局域网通过 192.168.226.10:80 找过来,还是您自己在机器里通过 127.0.0.1:80 找过来,HAProxy 都会一视同仁



listen替代frontend+backend
使用listen替换上面的frontend和backend的配置方式,可以简化设置,通常只用于TCP协议的应用
listen web_port_81
bind *:81
mode http
option forwardfor
# 这个选项的作用是启用或禁用将原始客户端的IP地址添加到HTTP请求的X-Forwarded-For头部中。
server web1 192.168.226.20:80 check inter 3000 fall 3 rise 5
server web2 192.168.226.30:80 check inter 3000 fall 3 rise 5
[root@haproxy ~]# systemctl restart haproxy



HAProxy调度算法
HAProxy通过固定参数balance指明对后端服务器的调度算法,该参数可以配置在listen或backend选项中。
HAProxy的调度算法分为静态和动态调度算法,但是有些算法可以根据参数在静态和动态算法中相互转换。
官方文档:http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4-balance
静态算法
静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、链接数和响应速度等,且无法实时修改权重,只能靠重启HAPr
动态算法
动态算法:基于后端服务器状态进行调度适当调整,优先调度至当前负载较低的服务器,且权重可以在haproxy运行时动态调整无需重启。
==算法很多不列举了,需要自己根据情况选择==
listen web_host
bind 192.168.226.10:80
mode http
log global
balance static-rr #就是在 balance 后面跟上算法名称
server web1 192.168.226.20:80 weight 1 check inter 3000 fall 2 rise 5
server web2 192.168.226.30:80 weight 2 check inter 3000 fall 2 rise 5