[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 解析为代理服务器的 IP 192.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四层负载均衡端口转发
请求负载均衡 5555 ---> 192.168.226.20:22;
请求负载均衡 6666 ---> 192.168.226.30:3306;
[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 只处理入站请求,不处理出站响应,吞吐量极大提升)。
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 的压力。
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 配置文件:设置静态IP 192.168.226.18(==实际ip不是DIP==),再添加一个vip:192.168.226.100
修改 ens224 配置文件:设置静态IP 192.168.195.10(不要配默认网关 )。
添加 VIP:通过命令 ifconfig ens160:0 192.168.226.100 netmask 255.255.255.0 up 或配置文件添加。
RS 节点 :
修改网卡配置文件:设置静态IP 192.168.195.11 和 192.168.195.12。
关键: 将它们的 GATEWAY 设置为 192.168.195.10(即 Director 的 DIP)。
3. Director 系统内核配置
三、 核心逻辑总结(一句话记忆)
VIP 走 NAT 模式 :用来“对外”接收客户端的请求,并伪装成客户端与 RS 通信的源地址。
DIP 走仅主机模式 :用来“对内”和 RS 建立专属局域网,确保 Director 能直接找到 RS。
RS 的网关必须指向 DIP :这是 NAT 模式回包的关键,确保 RS 处理完的数据能原路返回给 Director,再由 Director 发回给客户端。
别让两张网卡在同一网段 :DIP 和 VIP 必须处于不同的网段(195 和 226),否则路由器会“精神分裂”,导致 NAT 转发失败。
1. 为什么一个网卡能有多个IP?
网卡(如 ens160)在物理层面只是一块硬件,但操作系统可以通过软件的方式,在这块硬件上绑定多个逻辑IP。
一个是主 IP(Primary IP) :比如 192.168.226.18,这是网卡自己的真实地址。
其余的是辅助IP/ 别名(Secondary IP/ 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 T ext U ser I nterface,即网络管理器的文本用户界面)
具体来说,它的核心作用有以下三点:
图形化配置网络 :它提供了一个类似 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 发的,从而实现完整的双向通信。
第二步:设置 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,并配置 VIP 192.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 上绑定 VIP 192.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 网卡上不仅有原本的 IP 192.168.226.10,还多出了 VIP 192.168.226.100 。这证明主机已经成功接管了虚拟IP。
总结 :第一张图代表主节点已经完美上线,正在对外提供服务,并且成功接管了虚拟 IP
日志解读 :
备机的日志通常没有主机的“Sending gratuitous ARP”那么活跃,因为它不需要发广播宣告主权。它此时正在默默接收主机发来的心跳包(VRRP广播)。
总结 :第二张图代表备节点已经处于待命状态,配置正确,随时准备在主机故障时接管业务。
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
[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
[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 : 代表 I nput M odule UDP (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 端口的请求都会被这个前端捕获。
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