目录
3.2.2.4 Proxies配置-listen(简化配置)
1.负载均衡介绍
1.1 什么是负载均衡

1.2 为什么用负载均衡
- Web服务器的动态水平扩展-->对用户无感知
- 增加业务并发访问及处理能力-->解决单服务器瓶颈问题
- 节约公网IP地址-->降低IT支出成本
- 隐藏内部服务器IP-->提高内部服务器安全性
- 配置简单-->固定格式的配置文件
- 功能丰富-->支持四层和七层,支持动态下线主机
- 性能较强-->并发数万甚至数十万
1.3 负载均衡类型
1.3.1 四层负载均衡
- 通过ip+port决定负载均衡的去向。
- 对流量请求进行NAT处理,转发至后台服务器。
- 记录tcp、udp流量分别是由哪台服务器处理,后续该请求连接的流量都通过该服务器处理。
- 支持四层的软件:
-
lvs :重量级四层负载均衡器。
-
Nginx:轻量级四层负载均衡器,可缓存。( nginx 四层是通过 upstream 模块)
-
Haproxy:模拟四层转发。
-
1.3.2 七层负载均衡
- 通过虚拟URL或主机IP进行流量识别,根据应用层信息进行解析,决定是否需要进行负载均衡。
- 代理后台服务器与客户端建立连接,如nginx可代理前后端,与前端客户端tcp连接,与后端服务器建立tcp连接。
- 支持7层代理的软件:
- Nginx:基于http协议(nginx七层是通过proxy_pass)
- Haproxy:七层代理,会话保持、标记、路径转移等。
1.3.3 四层和七层的区别
- 分层位置:四层负载均衡在传输层及以下,七层负载均衡在应用层及以下。
- 性能 :四层负载均衡架构无需解析报文消息内容,在网络吞吐量与处理能力上较高;七层可支持解析应用层报文消息内容,识别URL、Cookie、HTTP header等信息。
- 原理 :四层负载均衡是基于ip+port;七层是基于虚拟的URL或主机IP等。
- 功能类比:四层负载均衡类似于路由器;七层类似于代理服务器。
- 安全性:四层负载均衡无法识别DDoS攻击;七层可防御SYN Cookie/Flood攻击。
举个通俗的例子说明:四层负载均衡就像银行的自助排号机,每一个到银行的客户,根据排号机的顺序,选择对应的窗口接受服务;而七层负载均衡像银行大堂经理,先确认客户需要办理的业务,再安排排号。这样办理理财、存取款等业务的客户,会去不同的地方排队,加快了业务办理流程。
2.haproxy简介
2.1 haproxy主要特性
- 高性能负载均衡: HAProxy通过优化的事件驱动引擎,能够以最小的系统资源开销处理大量并发请求。它支持多种负载均衡算法,如轮询、最少连接、源IP哈希等,可根据实际业务需求灵活配置
- 健康检查与故障恢复: HAProxy具备完善的后端服务器健康检查机制,可以根据响应时间、错误率等因素自动剔除不健康的后端节点,并在节点恢复时重新将其加入到服务池中,确保服务连续性
- 会话保持与亲和性: 为了保证用户的会话一致性,HAProxy支持基于cookie或源IP地址的会话保持功能,确保同一客户端的请求被转发到同一台后端服务器进行处理
- 安全性与SSL卸载: HAProxy支持SSL/TLS加密传输,可对HTTPS流量进行解密并透明地分发至后端服务器,同时也能终止SSL连接以减轻服务器的加密计算压力
- 高级路由与策略: 根据HTTP请求头、URL路径、内容类型等条件,HAProxy可以执行复杂的路由规则和ACL策略,使得负载均衡更加智能化和精准化
- 日志记录与监控: HAProxy提供丰富的日志记录选项,可通过syslog、CSV格式输出等方式收集统计数据,便于运维人员实时监控系统状态和性能指标
2.2 haproxy的优点与缺点
优点:
- 高性能:Haproxy是专门为高性能设计的,具有低延迟和并发处理能力。
- 灵活:Haproxy具有丰富的配置选项,可以满足各种复杂的负载均衡需求。
- 可扩展性:Haproxy支持插件扩展,可以轻松地集成到现有的系统中。
- 安全性:Haproxy提供了SSL/TLS加密、访问控制列表(ACL)等安全功能。
缺点:
- 配置复杂:Haproxy的配置可能比其他一些负载均衡器更复杂,需要一定的学习和经验。
- 成本较高:Haproxy是一款商业软件,需要购买许可证才能使用。
3.haproxy的安装和服务信息
3.1 实验环境
主机名 | IP | 角色 |
haproxy | NAT:172.25.254.100 | haproxy |
RS1 | NAT:172.25.254.10 | 真实服务器 |
RS2 | NAT:172.25.254.20 | 真实服务器 |
3.1.1 haproxy最简单配置
(1)所有主机关闭防火墙和selinux(包括haproxy)
命令:
# 关闭防火墙
systemctl disable --now firewalld.service
# 关闭selinux
grubby --update-kernel ALL --args selinux=0
(2)两台RS安装nginx,模拟提供服务
RS1
[root@RS1 ~]# dnf install nginx -y
# 生成默认测试页
[root@RS1 ~]# echo "RS1 - 172.25.254.10" > /usr/share/nginx/html/index.html
# 开启服务
[root@RS1 ~]# systemctl enable --now nginx.service
RS2
[root@RS2 ~]# dnf install nginx -y
[root@RS2 ~]# echo "RS2 - 172.25.254.20" > /usr/share/nginx/html/index.html
[root@RS2 ~]# systemctl enable --now nginx.service
访问测试:
(3)安装haproxy并配置相应参数
[root@haproxy ~]# dnf install haproxy -y
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
**********上面内容省略**********
#---------------------------------------------------------------------
# custom web cluster
#---------------------------------------------------------------------
# 设定前端
frontend webcluster
bind *:80
mode http
balance roundrobin
use_backend webserver
# 设定后端
backend webserver
server web1 172.25.254.10:80
server web2 172.25.254.20:80
# 前后端合并
#listen webcluster
# bind *:80
# mode http
# balance roundrobin
# server web1 192.168.0.10:80
# server web2 192.168.0.20:80
开启服务
[root@haproxy ~]# systemctl enable --now haproxy.service
两种写法挑一种即可,效果一致,两种都写的记得把其中一种注释掉
开启服务后查看端口,可以看到80端口已开启:
(4)测试
在本地终端进行访问测试
命令:
# 连续访问10次
for i in {1..10};do curl 172.25.254.100;done
3.2 haproxy的基本配置信息
启动文件: /lib/systemd/system/haproxy.service
主配置目录: /etc/haproxy/
主配置文件: /etc/haproxy/haproxy.cfg
子配置目录: /etc/haproxy/conf.d
HAProxy 的配置文件haproxy.cfg由两大部分组成,分别是:
global:全局配置段
- 进程及安全配置相关的参数
- 性能调整相关参数
- Debug参数
proxies:代理配置段
- defaults:为frontend, backend, listen提供默认配置
- frontend:前端,相当于nginx中的server {}
- backend:后端,相当于nginx中的upstream {}
- listen:同时拥有前端和后端配置,配置简单,生产推荐使用
3.2.1 global配置
3.2.1.1 global 配置参数说明
参数名 | 数值 | 功能 |
---|---|---|
log | 127.0.0.1 local2 | 定义全局的syslog服务器,最多定义两个,需要开启UDP协议 |
chroot | /var/lib/haproxy | 指定运行目录 |
pidfile | /var/run/haproxy.pid | 指定pid文件 |
maxconn | 4000 | 指定最大连接数 |
user | haproxy | 指定haproxy的运行用户 |
group | haproxy | 指定haproxy的运行组 |
stats socket | /var/lib/haproxy/stats | 指定haproxy的套接字文件 |
nbproc | 2 | 指定Haproxy的work进程数量,默认是1个 |
cpu-map | 1 0 | 指定第1个work进程绑定第1个cpu核心 |
cpu-map | 2 1 | 指定第2个work进程绑定第2个cpu核心 |
nbthread(与nbproc互斥) | 2 | 指定haproxy的线程数量,默认每个进程一个线程 |
maxsslconn | 100000 | 每个haproxy进程ssl最大连接数,用于haproxy |
maxconnrate | 100 | 指定每个客户端每秒建立连接的最大数量 |
3.2.1.2 多进程和线程
(1)开启多进程
nbproc 2 # 启用多进程
# 进程和cpu核心绑定防止cpu抖动从而减少系统资源消耗
cpu-map 1 0 # 1 表示第一个进程,0表示第一个cpu核心
cpu-map 2 1 # 2 表示第二个进程,1表示第二个cpu核心
编辑完配置文件后记得重启服务
[root@haproxy ~]# systemctl restart haproxy.service
最后两行不要乱添,因为作者的虚拟机的CPU有两个核心所以可以这么写,自己做的时候看看自己的虚拟机配置
查看多进程结果:
[root@haproxy ~]# pstree -p | grep haproxy
(2)开启多线程
nbthread 2 # 启用多线程
老样子,记得重启服务
[root@haproxy ~]# systemctl restart haproxy.service
查看多线程结果:
[root@haproxy ~]# pstree -p | grep haproxy
|-haproxy(27963)---haproxy(27965)---{haproxy}(27966)
[root@haproxy ~]# cat /proc/27965/status
3.2.2 proxies配置
参数 |
类型
|
作用
|
defaults
|
proxies
|
默认配置项,针对以下的 frontend 、 backend 和 listen 生效,可以多个name也可以没有 name。
|
frontend
|
proxies
|
前端servername,类似于Nginx的一个虚拟主机 server和LVS服务集群。
|
backend
|
proxies
|
后端服务器组,类似于nginx的upstream和LVS中的RS服务器。
|
listen
|
proxies
|
将frontend和backend合并在一起配置,相对于frontend和backend配置更简洁,生产常用
|
3.2.2.1 Proxies配置-defaults
配置名 | 数值 | 功能 |
---|---|---|
mode | http |
HAProxy实例使用的连接协议
|
log | global | 指定日志地址和记录日志的设备 此处的 global 表示使用 global配置段中设定的log值。 |
option | httplog | 日志记录选项 此处的 httplog 表示记录与 HTTP 会话相关的各种属性值
|
option | dontlognull | dontlognull 表示不记录空会话连接日志 |
option http-server-close | 等待客户端完整HTTP请求的时间,此处等待10s | |
option forwardfor | except 127.0.0.0/8 | 透传客户端真实IP到后端Web服务器;except 127.0.0.0/8:排除内网IP |
option | redispatch |
当server id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发(默认只重试一次)
|
option | http-keep-alive | 启用HTTP长连接支持,即会话保持 |
retries | 3 | 连接后端服务器失败时的重试次数 |
timeout http-request | 10s | 等待客户端请求完全被接收和处理的最长时间 |
timeout queue | 1m | 请求在队列中等待的最长时间 |
timeout connect | 10s | 连接后端服务器的超时时间 |
timeout client | 1m | 客户端空闲超时时间,即允许客户端处于既不接收也不发送数据的非活动时间 |
timeout server | 1m | 服务器空闲超时时间,即允许服务器处于既不接收也不发送数据的非活动时间 |
timeout http-keep-alive | 10s | HTTP长连接的空闲超时时间 |
timeout check | 10s | 后端服务器健康检查的超时时间 |
maxconn | 3000 | 每个进程允许的最大并发连接数 |
default-server | inter 1000 weight 3 | 为所有后端服务器设置默认参数 inter 1000 表示健康检查间隔为 1秒 weight 3 表示默认权重为 3 |
3.2.2.2 Proxies配置-frontend
frontend配置参数:
bind :指定 HAProxy 的监听地址,可以是 IPV4 或 IPV6 ,可以同时监听多个 IP 或端口,可同时用于 listen 字段中# 格式:bind [<address>]:<port_range> [, ...] [param*]# 注意:如果需要绑定在非本机的 IP ,需要开启内核参数: net.ipv4.ip_nonlocal_bind=1backlog <backlog> # 针对所有 server 配置 , 当前端服务器的连接数达到上限后的后援队列长度(注:不支持backend)
frontend配置示例:
因为之前在做 haproxy最简单配置 时已经配置过,这里直接截图
3.2.2.3 Proxies配置-backend
- 定义一组后端服务器,backend服务器将被frontend进行调用。
- 注意: backend 的名称必须唯一,并且必须在listen或frontend中事先定义才可以使用,否则服务无法启动
配置名 | 功能 |
mode http|tcp
|
指定负载协议类型 , 和对应的 frontend 必须一致
|
option
|
配置选项
|
server
|
定义后端 real server, 必须指定 IP 和端口
|
server配置
配置项 | 描述 |
---|---|
check | 对指定 real 进行健康状态检查。若不添加此设置,默认不开启检查功能。若 check 后无其他配置,也可启用检查功能。 默认对相应的后端服务器 IP 和端口,利用 TCP 连接进行周期性健康性检查。注意必须指定端口才能实现健康性检查。 |
addr <IP> | 可指定的健康状态监测 IP,可以是专门的数据网段,减少业务网络的流量。 |
port <num> | 指定的健康状态监测端口。 |
inter <num> | 健康状态检查间隔时间,默认 2000 ms。 |
fall <num> | 后端服务器从线上转为线下的检查的连续失效次数,默认为 3。 |
rise <num> | 后端服务器从下线恢复上线的检查的连续有效次数,默认为 2。 |
weight <weight> | 默认为 1,最大值为 256,0(状态为蓝色)表示不参与负载均衡,但仍接受持久连接。 |
backup | 将后端服务器标记为备份状态,只在所有非备份主机 down 机时提供服务,类似 Sorry Server。 |
disabled | 将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接,状态为深黄色,优雅下线,不再接受新用户的请求。 |
redirect prefix
http://www.baidu.com/
|
将请求临时(302)重定向至其它 URL,只适用于 http 模式。 |
maxconn <maxconn> | 当前后端 server 的最大并发连接数。 |
backend配置示例:
3.2.2.4 Proxies配置-listen(简化配置)

3.3 socat 工具
对服务器动态权重和其它状态可以利用 socat工具进行调整,Socat 是 Linux 下的一个多功能的网络工具,名字来由是Socket CAT,相当于netCAT的增强版.Socat 的主要特点就是在两个数据流之间建立双向通道,且支持众多协议和链接方式。如 IP、TCP、 UDP、IPv6、Socket文件等
该工具系统默认没有,需要安装
[root@haproxy ~]# dnf install socat -y
使用前还需要修改配置文件(修改完后记得重启服务):
3.3.1 实例
以下实验除了最后一个,都不必重启服务,因为socat全部都是用热处理(类似游戏的不停机更新)
(1)查看集群状态
[root@haproxy ~]# echo "show servers state" | socat stdio /var/lib/haproxy/stats
(2)查看集群权重
[root@haproxy ~]# echo get weight webserver/web1 | socat stdio /var/lib/haproxy/stats
[root@haproxy ~]# echo get weight webserver/web2 | socat stdio /var/lib/haproxy/stats
(3)修改集群权重
修改web1的权重为2
[root@haproxy ~]# echo "set weight webserver/web1 2 " | socat stdio /var/lib/haproxy/stats
在测试端查看效果:
此时再次查看集群权重:
(4)上下线真实服务器
下线web1后端服务器
[root@haproxy ~]# echo "disable server webserver/web1 " | socat stdio /var/lib/haproxy/stats
在测试端查看效果,此时访问的全是web2:
再次将web1服务器上线
[root@haproxy ~]# echo "enable server webserver/web1 " | socat stdio /var/lib/haproxy/stats
再次测试:
(5)针对多进程处理方法
如果开启多进程那么我们在对进程的sock文件进行操作时其对进程的操作时随机的
如果需要指定操作进程那么需要用多soct文件方式来完成
global
# to have these messages end up in /var/log/haproxy.log you will
# need to:
#
# 1) configure syslog to accept network log events. This is done
# by adding the '-r' option to the SYSLOGD_OPTIONS in
# /etc/sysconfig/syslog
#
# 2) configure local2 events to go to the /var/log/haproxy.log
# file. A line like the following can be added to
# /etc/sysconfig/syslog
#
# local2.* /var/log/haproxy.log
#
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
nbproc 2
cpu-map 1 0
cpu-map 2 1
# turn on stats unix socket
stats socket /var/lib/haproxy/stats1 mode 600 level admin process 1
stats socket /var/lib/haproxy/stats2 mode 600 level admin process 2
# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM
修改完配置文件后重启服务
[root@haproxy ~]# systemctl restart haproxy.service
查看结果
测试,可以看到原来的文件 stats 已经无法使用,但是新的stats1和stats2可以使用:
4.haproxy的算法
HAProxy通过固定参数 balance 指明对后端服务器的调度算法
balance参数可以配置在listen或backend选项中。
HAProxy的调度算法分为静态和动态调度算法
有些算法可以根据参数在静态和动态算法中相互转换。
4.1 静态算法
静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时修改权重( 只能为 0 和 1, 不支持其它值 ) ,只能靠重启 HAProxy 生效。
4.1.1 static-rr
- 不支持运行时利用socat进行权重的动态调整(只支持0和1,不支持其它值)
- 不支持端服务器慢启动
- 其后端主机数量没有限制,相当于LVS中的 wrr
慢启动是指在服务器刚刚启动上不会把他所应该承担的访问压力全部给它,而是先给一部分,当没问题后在给一部分
示例:
重启服务后测试:
4.1.2 first
- 根据服务器在列表中的位置,自上而下进行调度
- 其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务
- 其会忽略服务器的权重设置
- 不支持用socat进行动态修改权重,可以设置0和1,可以设置其它值但无效
示例:
重启服务后测试:
4.2 动态算法
- 基于后端服务器状态进行调度适当调整,
- 新请求将优先调度至当前负载较低的服务器
- 权重可以在haproxy运行时动态调整无需重启
4.2.1 roundrobin
- 基于权重的轮询动态调度算法,
- 支持权重的运行时调整,不同于 lvs 中的 rr 轮训模式,
- HAProxy 中的 roundrobin 支持慢启动 ( 新加的服务器会逐渐增加转发数 ) ,
- 其每个后端 backend 中最多支持 4095 个 real server ,
- 支持对 real server 权重动态调整,
- roundrobin 为默认调度算法 , 此算法使用广泛
优先把流量给权重高且负载小的主机,以负载为主
4.2.2 leastconn
- leastconn加权的最少连接的动态
- 支持权重的运行时调整和慢启动(相当于LVS的wlc),即:根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接)【当两个主机的连接数都差不多的时候给权重高的,权重是次考虑的】
- 比较适合长连接的场景使用,比如:MySQL等场景。
4.3 其他算法
其它算法即可作为静态算法,又可以通过选项成为动态算法
4.3.1 source
源地址 hash ,基于用户源地址 hash 并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一 个后端web 服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过hash-type 支持的选项更改这个算法一般是在不插入 Cookie 的 TCP模式下使用,也可给拒绝会话cookie 的客户提供最好的会话粘性,适用于 session 会话保持但不支持 cookie和缓存的场景源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash
示例:
重启服务后测试:
4.3.1.1 map-base 取模法
map-based :取模法,对 source 地址进行 hash 计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。
此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度
缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果 整体改变 , hash-type 指定的默值为此算法
所谓取模运算,就是计算两个数相除之后的余数, 10%7=3, 7%4=3
map-based 算法:基于权重取模, hash(source_ip)% 所有后端服务器相加的总权重
比如当源hash值时1111,1112,1113,三台服务器a b c的权重均为1,
即abc的调度标签分别会被设定为 0 1 2(1111%3=1,1112%3=2,1113%3=0)
1111 ----- > nodeb
1112 ------> nodec
1113 ------> nodea
如果a下线后,权重数量发生变化
1111%2=1,1112%2=0,1113%2=1
1112和1113被调度到的主机都发生变化,这样会导致会话丢失
4.3.1.2 一致性hash
一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动hash (o) mod n
该hash算法是动态的,支持使用socat等工具进行在线权重调整,支持慢启动
算法
- key1=hash(source_ip)%(2^32)
- keyA=hash(后端服务器虚拟ip)%(2^32) [0—4294967295]
- 将key1和keyA都放在hash环上,将用户请求调度到离key1最近的keyA对应的后端服务器
hash 环偏斜问题
增加虚拟服务器 IP 数量,比如:一个后端服务器根据权重为 1 生成 1000 个虚拟 IP ,再 hash 。而后端服务器权 重为2 则生成 2000 的虚拟 IP ,再 bash, 最终在 hash 环上生成 3000 个节点,从而解决 hash 环偏斜问题
hash对象
Hash对象到后端服务器的映射关系
一致性hash示意图
后端服务器在线与离线的调度方式
4.3.2 uri
基于对用户请求的 URI 的左半部分或整个 uri 做 hash ,再将 hash 结果对总权重进行取模后
根据最终结果将请求转发到后端指定服务器
适用于后端是缓存服务器场景
默认是静态算法,也可以通过 hash-type 指定 map-based 和 consistent ,来定义使用取模法还是一致性hash
注意:此算法基于应用层,所以只支持 mode http ,不支持 mode tcp
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分: /<path>;<params>
整个 uri : /<path>;<params>?<query>#<frag>
4.3.3 url_param
url_param 对用户请求的 url 中的 params 部分中的一个参数 key 对应的 value 值作 hash 计算,并由服务器总权重相除以后派发至某挑出的服务器, 后端搜索同一个数据会被调度到同一个服务器,多用与电商
通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server
如果无没 key ,将按 roundrobin 算法
4.3.4 hdr
针对用户每个 http 头部 (header) 请求中的指定信息做 hash ,
此处由 name 指定的 http 首部将会被取出并做 hash 计算,
然后由服务器总权重取模以后派发至某挑出的服务器,如果无有效值,则会使用默认的轮询调度。
4.4 算法总结
# 静态
static-rr--------->tcp/http
first------------->tcp/http
# 动态
roundrobin-------->tcp/http
leastconn--------->tcp/http
# 以下静态和动态取决于 hash_type 是否 consistent
source------------>tcp/http
uri--------------->http
url_param--------->http
hdr--------------->http
各算法使用场景
first #使用较少
static-rr #做了 session 共享的 web 集群
roundrobin #做了 session 共享的 web 集群
leastconn #数据库
source #基于客户端公网IP的会话保持
Uri--->http #缓存服务器, CDN 服务商,蓝汛、百度、阿里云、腾讯
url_param--->http # 可以实现 session 保持
hdr #基于客户端请求报文头部做下一步处理
5.高级功能及配置
5.1 基于cookie的会话保持
cookie value:为当前server指定cookie值,实现基于cookie的会话黏性,相对于基于 source 地址hash调度算法对客户端的粒度更精准,但同时也加大了haproxy负载,目前此模式使用较少,已经被session共享服务器代替
注意:不支持 tcp mode,使用 http mode
配置选项
cookie name [ rewrite | insert | prefix ][ indirect ] [ nocache ][ postonly ] [preserve ][ httponly ] [ secure ][ domain ]* [ maxidle <idle> ][ maxlife ]
name : #cookie 的 key 名称,用于实现持久连接
insert : # 插入新的 cookie, 默认不插入 cookie
indirect : # 如果客户端已经有 cookie, 则不会再发送 cookie 信息
nocache: # 当 client 和 hapoxy 之间有缓存服务器(如: CDN )时,不允许中间缓存器缓存 cookie: #因为这会导致很多经过同一个 CDN 的请求都发送到同一台后端服务器
这里使用listen简化配置:
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
balance roundrobin
cookie WEBCOOKIE insert nocache indirect
server web1 172.25.254.10:80 cookie servera check inter 3s fall 3
server web2 172.25.254.20:80 cookie serverb check inter 3s fall 3
重启服务
[root@haproxy ~]# systemctl restart haproxy.service
测试:
浏览器访问haproxy的ip,网页刷新数次后依旧访问的是RS1
5.2 HAProxy状态页
状态页配置项
stats enable # 基于默认的参数启用 stats page
stats hide-version # 将状态页中 haproxy 版本隐藏
stats refresh <delay> # 设定自动刷新时间间隔,默认不自动刷新
stats uri <prefix> # 自定义 stats page uri ,默认值: /haproxy?stats
stats auth <user>:<passwd> # 认证时的账号和密码,可定义多个用户 , 每行指定一个用户
# 默认: no authentication
stats admin { if | unless } <cond> # 启用 stats page 中的管理功能
启用状态页
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen haproxystatus
mode http
bind *:9999
stats enable
stats refresh 3
log global
stats uri /status
stats auth guanai:guanai
重启服务
[root@haproxy ~]# systemctl restart haproxy.service
测试:
此时关闭RS1上的nginx
等状态页刷新,可以看到web1变红了:
重新上线
恢复正常:
5.3 IP透传
web服务器中需要记录客户端的真实IP地址,用于做访问统计、安全防护、行为分析、区域排行等场景。
5.3.1 七层IP透传
七层IP透传只要打开forwardfor该参数即可,会自动透传过来
访问测试:
在RS上查看日志,可以看到,访问端的真实IP透过来了:
5.3.2 四层IP透传
想要看到效果需要修改该参数,加“send-proxy”是为了让haproxy走四层走得更彻底(记得重启服务)
编辑/etc/nginx/nginx.conf文件
[root@RS1 ~]# vim /etc/nginx/nginx.conf
启用此项,将无法直接访问此网站,只能通过四层代理访问
重启nginx和haproxy服务后再次访问:
再次查看日志,可以看到,访问端的真实IP透不过来了:
(第一行命令用于清除日志,在访问测试之前做,避免之前七层IP透传产生的日志影响)
解决办法:
在/etc/nginx/nginx.conf文件加入该参数(两个RS都要做)
编辑/etc/nginx/nginx.conf文件
[root@RS1 ~]# vim /etc/nginx/nginx.conf
'"$proxy_protocol_addr"'
编辑完后重启服务
[root@RS2 ~]# systemctl restart nginx.service
再次查看日志,对比两次访问结果的日志,可以看到,访问端的真实IP透过来了:
5.4 ACL
访问控制列表ACL,Access Control Lists)
是一种基于包过滤的访问控制技术
它可以根据设定的条件对经过服务器传输的数据包进行过滤(条件匹配)即对接收到的报文进行匹配和过滤,基于请求报文头部中的源地址、源端口、目标地址、目标端口、请求方法、URL、文件后缀等信息内容进行匹配并执行进一步操作,比如允许其通过或丢弃。
在做之前把之前做四层IP透传的配置改回来,不然会访问失败(改完记得重启服务)
80后面的这段配置删掉,要不然
5.4.1 ACL配置选项
# 用 acl 来定义或声明一个 aclacl <aclname> <criterion> [flags] [operator] [<value>]acl 名称 匹配规范 匹配模式 具体操作符 操作对象类型
5.4.1.1 ACL-Name 名称
acl test path_end -m sub /a#ACL 名称,可以使用大字母 A-Z 、小写字母 a-z 、数字 0-9 、冒号:、点 . 、中横线和下划线,并且严格区分大小写,比如:my_acl 和 My_Acl 就是两个完全不同的 acl5.8.1.2 ACL-criterion
5.4.1.2 ACL-criterion 匹配规范
定义ACL匹配规范,即:判断条件
hdr string ,提取在一个 HTTP 请求报文的首部hdr ( [<name> [ , <occ>]] ):完全匹配字符串 ,header 的指定信息, <occ> 表示在多值中使用的值的出现次数hdr_beg ( [<name> [ , <occ>]] ):前缀匹配, header 中指定匹配内容的 beginhdr_end ( [<name> [ , <occ>]] ):后缀匹配, header 中指定匹配内容 endhdr_dom ( [<name> [ , <occ>]] ):域匹配, header 中的 dom ( host )hdr_dir ( [<name> [ , <occ>]] ):路径匹配, header 的 uri 路径hdr_len ( [<name> [ , <occ>]] ):长度匹配, header 的长度匹配hdr_reg ( [<name> [ , <occ>]] ):正则表达式匹配,自定义表达式 (regex) 模糊匹配hdr_sub ( [<name> [ , <occ>]] ):子串匹配, header 中的 uri 模糊匹配 模糊匹配 c 报文中 a/b/c 也会匹配# 示例:hdr(<string>) 用于测试请求头部首部指定内容hdr_dom(host) 请求的 host 名称,如 www.guanai.comhdr_beg(host) 请求的 host 开头,如 www. img. video. download. ftp.hdr_end(host) 请求的 host 结尾,如 .com .net .cn# 示例:acl bad_agent hdr_sub(User-Agent) -i curl wgethttp-request deny if bad_agent# 有些功能是类似的,比如以下几个都是匹配用户请求报文中 host 的开头是不是 wwwacl short_form hdr_beg(host) www.acl alternate1 hdr_beg(host) -m beg www.acl alternate2 hdr_dom(host) -m beg www.acl alternate3 hdr(host) -m beg www.base : string# 返回第一个主机头和请求的路径部分的连接,该请求从主机名开始,并在问号之前结束 , 对虚拟主机有用<scheme>://<user>:<password>@ #<host>:<port>/<path>;<params>#?<query>#<frag>base : exact string matchbase_beg : prefix matchbase_dir : subdir matchbase_dom : domain matchbase_end : suffix matchbase_len : length matchbase_reg : regex matchbase_sub : substring matchpath : string# 提取请求的 URL 路径,该路径从第一个斜杠开始,并在问号之前结束(无主机部分)<scheme>://<user>:<password>@<host>:<port> #/<path>;<params>#?<query>#<frag>path : exact string matchpath_beg : prefix match # 请求的 URL 开头,如 /static 、 /images 、 /img 、 /csspath_end : suffix match # 请求的 URL 中资源的结尾,如 .gif .png .css .js .jpg .jpegpath_dom : domain matchpath_dir : subdir matchpath_len : length matchpath_reg : regex matchpath_sub : substring match# 示例:path_beg -i /haproxy-status/path_end .jpg .jpeg .png .gifpath_reg ^/images.*\.jpeg $path_sub imagepath_dir jpegspath_dom timingleeurl : string# 提取请求中的整个 URL 。url : exact string matchurl_beg : prefix matchurl_dir : subdir matchurl_dom : domain matchurl_end : suffix matchurl_len : length matchurl_reg : regex matchurl_sub : substring matchdst # 目标 IPdst_port # 目标 PORTsrc # 源 IPsrc_port # 源 PORT# 示例:acl invalid_src src 10 .0.0.7 192 .168.1.0/24acl invalid_src src 172 .16.0.0/24acl invalid_port src_port 0 :1023status : integer # 返回在响应报文中的状态码# 七层协议acl valid_method method GET HEADhttp-request deny if ! valid_method
5.4.1.3 ACL-flags 匹配模式
ACL匹配模式:
-i 不区分大小写
-m 使用指定的正则表达式匹配方法
-n 不做DNS解析
-u 禁止acl重名,否则多个同名ACL匹配或关系
5.4.1.4 ACL-operator 具体操作符
ACL 操作符:
整数比较: eq 、 ge 、 gt 、 le 、 lt字符比较:- exact match (-m str) : 字符串必须完全匹配模式- substring match (-m sub) : 在提取的字符串中查找模式,如果其中任何一个被发现, ACL 将匹配- prefix match (-m beg) : 在提取的字符串首部中查找模式,如果其中任何一个被发现, ACL 将匹配- suffix match (-m end) : 将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则 ACL 进行匹配- subdir match (-m dir) : 查看提取出来的用斜线分隔( “/" )的字符串,如其中任一个匹配,则 ACL进行匹配- domain match (-m dom) : 查找提取的用点( “." )分隔字符串,如果其中任何一个匹配,则 ACL 进行匹配
5.4.1.5 ACL-value 操作对象
value的类型
The ACL engine can match these types against patterns of the following types :- Boolean # 布尔值- integer or integer range # 整数或整数范围,比如用于匹配端口范围- IP address / network #IP 地址或 IP 范围 , 192.168.0.1 ,192.168.0.1/24- string--> www.guanai.comexact # 精确比较substring # 子串suffix # 后缀比较prefix # 前缀比较subdir # 路径, /wp-includes/js/jquery/jquery.jsdomain # 域名, www.guanai.com- regular expression # 正则表达式- hex block #16 进制
5.4.2 多个ACL的组合调用方式
多个ACL的逻辑处理
与:隐式(默认)使用或:使用 “or" 或 “||" 表示否定:使用 "!" 表示
多个ACL调用方式:
# 例子:if A B # 与关系, ACL 中 A 和 B 都要满足为 true ,默认为与if A || B # 或, ACL 中 A 或者 B 满足一个为 trueif ! A # 非,取反,不满足 ACL 才为 true
5.4.3 ACL示例
5.4.3.1 域名匹配
这里使用前后端的方式
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
# 基于域名www.loving_eyes.com匹配
acl web_host hdr_dom(host) www.loving_eyes.com
# 如果符合ACL规则:web_host,使用服务器webserver1
use_backend webserver1 if web_host
# 默认使用服务器webserver2
default_backend webserver2
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试机做本地域名解析,不然访问域名会不成功:
测试机ip
[root@webservera html]# vim /etc/hosts
测试:
可以看到访问ip走的是RS2,访问域名走的是RS1
5.4.3.2 基于源IP或子网调度访问
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
#基于172.25.254.111这个IP和192.168.0.0/24这个网段进行匹配
acl ip_test src 172.25.254.111 192.168.0.0/24
use_backend webserver1 if ip_test
default_backend webserver2
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试:
可以看到测试机符合ACL走的是RS1,本地终端不符合ACL走的是RS2
5.4.3.3 基于源地址的访问控制
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
#acl web_host hdr_dom(host) www.loving_eyes.com
acl ip_test src 172.25.254.111 192.168.0.0/24
acl bad_ip src 172.25.254.1
use_backend webserver1 if ip_test
default_backend webserver2
# 符合bad_ip策略的源地址被拒绝以http的方式访问haproxy
http-request deny if bad_ip
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试:
测试机依旧可以访问,本地终端访问被拒绝了
5.4.3.4 匹配浏览器类型
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
#acl web_host hdr_dom(host) www.loving_eyes.com
acl ip_test src 172.25.254.111 192.168.0.0/24
#acl bad_ip src 172.25.254.1
acl user_agent_black hdr_sub(User-Agent) -i curl wget
use_backend webserver1 if ip_test
default_backend webserver2
# curl 和 wget命令都不能访问haproxy
http-request deny if user_agent_black
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试:
测试机使用curl 和wget都访问失败
5.4.3.5 基于文件后缀名实现动静分离
为了更好的看到效果,我们加一些配置
在RS2上安装php,并编写默认测试页
[root@RS2 ~]# dnf install php -y
[root@RS2 ~]# vim /usr/share/nginx/html/index.php
<?php
phpinfo();
?>
[root@RS2 ~]# systemctl restart nginx.service
haproxy主机上安装httpd,并修改端口
[root@haproxy ~]# dnf install httpd -y
# 生成默认测试页
[root@haproxy ~]# echo "欢迎来到关爱的默认页面" > /var/www/html/index.html
# 修改端口(不然服务起不来)
[root@haproxy ~]# vim /etc/httpd/conf/httpd.conf
**********上面内容省略**********
listen 8080
**********下面内容省略**********
# 开启服务
[root@haproxy ~]# systemctl enable --now httpd
修改haproxy配置
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
acl url_static path_end -i .jpg .png .css .js .html
acl url_php path_end -i .php
# 静态页面访问RS1
use_backend webserver1 if url_static
# 动态页面访问RS2
use_backend webserver2 if url_php
# 什么都不做访问默认页面
default_backend defaultserver
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
backend defaultserver
server web1 172.25.254.100:8080 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试 (在本地浏览器访问效果更明显):
默认页面:
静态页面:
动态页面(太长了,就只截开头了):
5.4.3.6 匹配访问路径实现动静分离
再加一些配置:
在RS上加一些默认测试页
# 两个RS上都要做
[root@RS1 ~]# mkdir /usr/share/nginx/html/api/
[root@RS1 ~]# echo "RS1 - api" > /usr/share/nginx/html/api/index.html
[root@RS1 ~]# mkdir /usr/share/nginx/html/static/
[root@RS1 ~]# echo "RS1 - static" > /usr/share/nginx/html/static/index.html
可以看到,配置没问题:
修改haproxy配置
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
bind *:80
mode http
acl url_static path_end -m sub /static /images /javascript
acl acl_app path_beg -m sub /api
use_backend webserver1 if url_static
use_backend webserver2 if acl_app
default_backend defaultserver
backend webserver1
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
backend webserver2
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
backend defaultserver
server web1 172.25.254.100:8080 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试:
5.5 自定义HAProxy 错掉误界面
5.5.1 基于自定义的错误页面文件
将之前做的ACL注释(或者删掉)掉,保证一个干净的实验环境:
将RS2的php页面删除,因为php页面访问优先级更高,会影响后面的测试效果
[root@RS2 ~]# rm -fr /usr/share/nginx/html/index.php
先查看haproxy的几个状态码页面放在哪
[root@haproxy ~]# rpm -qa | grep haproxy
[root@haproxy ~]# rpm -ql haproxy-2.4.17-3.el9.x86_64 | grep http$
创建一个目录存放我们自己的状态码页面
[root@haproxy ~]# mkdir /etc/haproxy/errorpage/
[root@haproxy ~]# vim /etc/haproxy/errorpage/503.http
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache
Connection: close
Content-Type: text/html;charset=UTF-8
<html><body><h1>真是肯尼迪开敞篷车</h1>
真令人摸不着头脑(bushi)
</body></html>
修改haproxy配置,在defaultsl那栏加,修改完后重启服务
# 将503状态页设定为我们自己编写的503状态页
errorfile 503 /etc/haproxy/errorpage/503.http
关闭RS上的nginx服务
[root@RS1 ~]# systemctl stop nginx.service
[root@RS2 ~]# systemctl stop nginx.service
在浏览器测试结果:
5.5.2 基于http重定向错误页面
将之前的配置注释掉,添加下面这行配置
# 如果页面处于503状态,将页面重定向到百度首页
errorloc 503 https://www.baidu.com
[root@haproxy ~]# systemctl restart haproxy.service
刷新我们刚才的自定义503页面,重定向到百度:
重新启动RS上的nginx,重新访问haproxy,恢复正常:
5.6 HAProxy 四层负载
针对除HTTP以外的TCP协议应用服务访问的应用场景
MySQLRedisMemcacheRabbitMQ
四层负载示例(对MySQL进行四层负载)
(1)RS安装MySQL并编辑配置文件(两个RS都做)
[root@RS1 ~]# dnf install mariadb-server -y
[root@RS1 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=10 # 添加server-id,方便后续看测试效果
# 开启服务
[root@RS1 ~]# systemctl start mariadb.service
查看一下安装结果:
(2)对RS上的数据库进行授权
#授权liao用户能在所有的客户端(包括本地和远程)对所有库的所有表使用所有的功能(增删改查等)
# -e 表示直接执行这条命令; identified by 表示该用户的认证密码为‘liao’
[root@RS1 ~]# mysql -e "grant all on *.* to liao@'%' identified by 'liao';"
[root@RS2 ~]# mysql -e "grant all on *.* to liao@'%' identified by 'liao';"
(3)编辑haproxy配置文件,做四层负载
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen sqlcluster
bind *:3306
mode tcp
balance roundrobin
server db1 172.25.254.10:3306 check inter 3s fall 3
server db2 172.25.254.20:3306 check inter 3s fall 3
[root@haproxy ~]# systemctl restart haproxy.service
(4)测试机访问
挑一台能跟haproxy主机访问的主机做测试机,安装MySQL进行测试
[root@webservera ~]# dnf install mariadb-server -y
[root@webservera ~]# mysql -uliao -pliao -h172.25.254.100 -e "select @@server_id"
5.7 HAProxy https 实现
haproxy可以实现https的证书安全,从用户到haproxy为https,从haproxy到后端服务器用http通信,但基于性能考虑,生产中证书都是在后端服务器比如nginx上实现
5.7.1 证书制作
# 查看selinux状态(记得关闭selinux)
[root@haproxy ~]# getenforce
Disabled
# 建立目录存放证书
[root@haproxy ~]# mkdir /etc/haproxy/certs/
# 制作证书
[root@haproxy ~]# openssl req -newkey rsa:2048 -nodes -sha256 -keyout /etc/haproxy/certs/liao.key -x509 -days 365 -out /etc/haproxy/certs/liao.crt
将证书和密钥存放到一个文件(liao.pem)里
5.7.2 将证书导入haproxy配置
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster-80
bind *:80
mode http
balance roundrobin
redirect scheme https if !{ ssl_fc }
use_backend webserver
frontend webcluster-443
bind *:443 ssl crt /etc/haproxy/certs/liao.pem
mode http
balance roundrobin
use_backend webserver
backend webserver
server web1 172.25.254.10:80 check inter 3s fall 3 rise 3
server web2 172.25.254.20:80 check inter 3s fall 3 rise 3
[root@haproxy ~]# systemctl restart haproxy.service
测试:浏览器访问haproxy(不指定端口)
刷新网页后访问RS2