1、Haproxy基础概念

1.1 什么是Haproxy

haproxy 虽然名称中包含 HA ,但它不提供任何 HA 高可用的功能,它仅提供 proxy 代理的功能;

但是 haproxy 它提供对后端节点的状态进行检测,⼀但后端节点出现故障, haproxy 会将请求进行重新分发,这也是它将自⼰称为 haproxy 的原因。

1.2 Haproxy应用场景

  • Haproxy 支持http、https协议的反向代理

  • Haproxy 支持动态程序的反向代理

  • Haproxy 支持基于 tcp 协议的反向代理(例如:代理MySQL、SSH、Redis)

1.3 Haproxy性能指标

衡量负载均衡性能,可以从三个因素来评估负载均衡器的性能:

  • 会话率:会话建立的速率,在1秒内能建立多少连接

  • 会话并发能⼒:整体服务器的会话并发能⼒,能同时支撑多少个并发的链接

  • 数据率:在所有会话基础上,数据传输速率或数据传输效率

经过官方测试统计, haproxy 单位时间处理的最⼤请求数为 20000 个,可以同时维护 40000-50000 个并发连接,最⼤数据处理与数据交换能⼒为 10Gbps (那么 10Gbps 实际速率= 10/8=1.25GBps ,下载速率每秒 1.25GB )。综合上述, haproxy 是性能优越的负载均衡、反向代理服务器。

2、Haproxy服务部署

2.1 使用系统rpm安装

1、在Centos7系统中默认提供的 Haproxy 版本是 1.5.18 比较旧,很多新功能都不支持

[root@proxy ~]# yum install haproxy -y

# 验证haproxy版本
[root@proxy ~]# haproxy -v
HA-Proxy version 1.5.18 2016/05/10
Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>

2、在RockyLinux9系统默认提供的 Haproxy 的版本是 2.4 TLS

[root@proxy ~]# dnf install haproxy -y

# 验证haproxy版本
[root@proxy ~]# haproxy -v
HAProxy version 2.4.22-f8e3218 2023/02/14 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-2.4.22.html
Running on: Linux 5.14.0-427.13.1.el9_4.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 1 19:11:28 UTC 2024 x86_64

2.2 源码安装Haproxy

1、安装依赖环境

[root@proxy ~]# yum install lua gcc make readline-devel openssl-devel zlib-devel pcre-devel systemd-devel wget -y

2、通过源码方式安装lua脚本[可选],下载地址: https://www.lua.org/download.html

📎lua-5.4.6.tar.gz

[root@proxy ~]# wget https://www.lua.org/ftp/lua-5.4.6.tar.gz
[root@proxy ~]# tar xf lua-5.4.6.tar.gz -C /usr/local/
[root@proxy ~]# cd /usr/local/lua-5.4.6/
[root@haproxy lua-5.4.6]# make all test
[root@haproxy lua-5.4.6]# ln -s /usr/local/lua-5.4.6/ /usr/local/lua

3、源码编译Haproxy

📎haproxy-2.8.7.tar.gz

[root@proxy ~]# wget https://www.haproxy.org/download/2.8/src/haproxy-2.8.7.tar.gz
[root@proxy ~]# tar xf haproxy-2.8.7.tar.gz
[root@proxy ~]# cd haproxy-2.8.7/
[root@haproxy haproxy-2.8.7]# make 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/lua/src LUA_LIB=/usr/local/lua/src
[root@haproxy haproxy-2.8.7]# make install PREFIX=/usr/local/haproxy-2.8
[root@haproxy haproxy-2.8.7]# ln -s /usr/local/haproxy-2.8/ /usr/local/haproxy

4、创建Haproxy启动文件

[root@proxy ~]# vim /usr/lib/systemd/system/haproxy28.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target

[Service]
ExecStartPre=/usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.cfg -c -q
ExecStart=/usr/local/haproxy/sbin/haproxy -Ws -f /usr/local/haproxy/haproxy.cfg -p /var/lib/haproxy28/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

5、创建Haproxy配置文件

[root@proxy ~]# vim /usr/local/haproxy/haproxy.cfg
global
  maxconn 100000
  # uid 99
  # gid 99
  user haproxy
  group haproxy
  daemon
  log 127.0.0.1 local2 info
  pidfile /var/lib/haproxy28/haproxy.pid
  stats socket /var/lib/haproxy28/haproxy.sock mode 600 level admin

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 *:8088
  mode http
  server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5

6、根据配置文件,进行初始化操作

[root@proxy ~]# mkdir /var/lib/haproxy28
[root@proxy ~]# useradd -r -s /sbin/nologin haproxy

7、启动Haproxy

[root@proxy ~]# systemctl enable haproxy28
[root@proxy ~]# systemctl start haproxy28

# 检查端口
[root@rocky9 ~]# ss -ntl
State        Recv-Q       Send-Q               Local Address:Port               Peer Address:Port       Process       
LISTEN       0            128                        0.0.0.0:22                      0.0.0.0:*                        
LISTEN       0            4096                       0.0.0.0:9999                    0.0.0.0:*                        
LISTEN       0            4096                       0.0.0.0:8088                    0.0.0.0:*                        
LISTEN       0            128                           [::]:22                         [::]:*

# 检查进程
[root@route ~]# ps -ef |grep haproxy
root       22565       1  0 21:24 ?        00:00:00 /usr/local/haproxy/sbin/haproxy -Ws -f /usr/local/haproxy/haproxy.cfg -p /var/lib/haproxy28/haproxy.pid
haproxy    22567   22565  0 21:24 ?        00:00:00 /usr/local/haproxy/sbin/haproxy -Ws -f /usr/local/haproxy/haproxy.cfg -p /var/lib/haproxy28/haproxy.pid
root       22571   12441  0 21:24 pts/1    00:00:00 grep --color=auto haproxy

8、验证Haproxy,使用浏览器访问: http://haproxy-server:9999/haproxy-status

3、Haproxy示例配置

我们⾸先回顾 Nginx 负载均衡语法,然后对比 Haproxy 负载均衡语法,这样便于我们更好学习 Haproxy

#----------------------------------------------------------------
# nginx
#----------------------------------------------------------------
server {} 	# 前端监听对外端口
proxy_pass 	# 将前端与后端建立关系
upstream{} 	# 后端资源池

#----------------------------------------------------------------
# haproxy
#----------------------------------------------------------------
frontend 		# 前端监听对外端口
use_backend # 将前端与后端建立关系,带条件的 location ~ \.php$ )
default_backend 	# 默认建立关系 ( location / )
backend 		# 后端资源池

listen 			# frontend和backend的组合体 ( 直接将前后端建立起来 )
default 		# 提供统⼀的默认参数,如果定义了则使用自⼰的,没有定义则使用default的

3.1 场景示例配置1

场景描述:

  • 1、将请求本机 80 端口的服务,都转发至 webservers 后端集群组。

  • 2、后端 webservers 资源池定义了 172.16.1.7:80172.16.1.8:80 两台节点。

  • 3、调度方式采用轮询调度。

1、Nginx实现配置,需要使用到 (server、location、proxy_pass、upstream

upstream webservers {
  server 172.16.1.7:80;
  server 172.16.1.8:80;
}

server {
  listen 80;
  server_name proxy.ops.com;
  
location / {
    proxy_pass http://webservers;
  }
}

2、haproxy实现配置,需要使用到 (frontend、default_backend、backend

frontend web # 定义前端名称
   bind *:80 # 定义前端监听的端口
   default_backend webservers # 所有请求调度至 webservers集群
   
backend webservers # 定义webservers集群名称
   balance roundrobin # 采用轮询调度算法
   server web1 172.16.1.7:80 check # 定义节点信息 [名称 IP:端口] 检查
   server web2 172.16.1.8:80 check # 定义节点信息 [名称 IP:端口] 检查

3.2 场景示例配置2

  • 1、将请求本机 80 端口的服务, uri为 /的默认转发至 webservers 后端集群组。

  • 2、将请求本机 80 端口的服务, uri为 /1.png|/2.gif 的转发至 static 后端集群组。

  • 3、后端 webservers 资源池定义了 172.16.1.7:80172.16.1.8:80 两台节点。

  • 4、后端 static 资源池定义了 172.16.1.9:80172.16.1.10:80 两台节点。

  • 5、调度方式采用轮询调度。

1、Nginx实现配置,需要使用到 (server、location、proxy_pass、upstream

upstream webservers {
  server 172.16.1.7:80;
  server 172.16.1.8:80;
}

upstream static {
  server 172.16.1.9:80;
  server 172.16.1.10:80;
}

server {
  listen 80;
  server_name proxy.ops.com;
  
location / {
    proxy_pass http://webservers;
  }
  
location ~ \.(png|gif)$ {
    proxy_pass http://static;
  }
}

2、haproxy实现配置,需要使用到 (frontend、default_backend、backend

frontend web
   bind *:80
   # 默认所有请求都调度到webservers集群,类似于nginx的locatiton / {}
   default_backend webservers
   
   # 如果请求的url是.png | .gif结尾的,则调度到static集群,类似于nginx的location~ \.{gif|png}$ {}
   acl url_static path_end -i .gif .png
   use_backend static if url_static
   
backend webservers
  balance roundrobin
  server web1 172.16.1.7:80 check
  server web2 172.16.1.8:80 check
  
backend static
   balance roundrobin
   server static1 172.16.1.9:80 check
   server static1 172.16.1.10:80 check

3.3 场景示例配置3

  • 请求本机 80 端口的服务,直接代理至后端的 172.16.1.11:80 节点。

  • 也就是配置代理模式,并非负载均衡;

1、Nginx实现配置,需要使用到 (server、location、proxy_pass

server {
   listen 80;
   server_name proxy.ops.com;
 
 location / {
     proxy_pass http://172.16.1.11:80;
   }
}

2、haproxy实现配置,需要使用到 (listen

listen web
   bind *:80
   server web1 172.16.1.11:80;

3.4 Haproxy官方示例

frontend main
   bind *:5000
   acl url_static path_beg -i /static /images /javascript /stylesheets
   acl url_static path_end -i .jpg .gif .png .css .js
   use_backend static if url_static
   default_backend app
   
backend static
   balance roundrobin
   server static 127.0.0.1:4331 check
   
backend app
   balance roundrobin
   server app1 127.0.0.1:5001 check
   server app2 127.0.0.1:5002 check
   server app3 127.0.0.1:5003 check
   server app4 127.0.0.1:5004 check

.5 Haproxy场景实践

  • 1、配置两个后端节点,监听在8888端口,域名是 proxy.ops.net;

  • 2、配置Haproxy代理两个节点,监听在80端口;

1、准备后端节点 172.16.1.7、172.16.1.8

[root@web01 ~]# vim /etc/nginx/conf.d/proxy.ops.net.conf
server {
   listen 8888;
   server_name proxy.ops.net;
   root /opt;
   
 location / {
     index index.html;
   }
}

[root@web01 ~]# echo "haproxy--web01" > /opt/index.html
[root@web01 ~]# systemctl reload nginx

2、配置Haproxy的负载均衡功能

[root@proxy01 ~]# vim /etc/haproxy/haproxy.cfg
frontend proxy
   bind *:80
   mode http
   
   # 将请求调度到proxyservers这个资源池
   use_backend proxyservers
   
backend proxyservers
   balance roundrobin
   server web01 172.16.1.7:8888 check
   server web02 172.16.1.8:8888 check

4、Haproxy Global配置

4.1 global配置参数

Global settings  全局配置,用于设定全局配置参数;

1、基本配置选项:

选项

作用

配置示例

daemon

以守护进程模式运行,在后台运行

chroot

锁定运行目录

chroot /var/lib/haproxy

user

运行haproxy进程用户身份

user haproxy

group

运行haproxy进程用户组身份

group haproxy

maxconn

设定haproxy每个进程所接受的最⼤连接数

maxconn 4000

log 127.0.0.1 local2

通过本地rsyslog的local2设备写⼊日志

local2.* /var/log/haproxy.log

stats socket /var/lib/haproxy/s

tats

基于socket方式于Haproxy通信,可实现动态配置变更

spread-checks <0..50>

在 HAProxy 中,当后端配置了众多节点时,如果所有健康检查同时进行,将会对 HAProxy 造成较⼤的性能影响。spread-checks 选项用于随机分散执行健康检查的时间,防⽌这些检查在同⼀时刻集中发⽣。官方推荐设置此值在 20% 至 50% 之间,以减少健康检查带来的性能开销。

2、进程与线程相关选项:

选项

作用

配置示例

nbproc <number>

haproxy进程的最⼤数量,默认启动⼀个进程

nbproc 4

nbthread <number>

haproxy线程的最⼤数量,默认启动⼀个线程,不能和nbproc同时使用

nbproc 4

cpu-map <id> <cpu>

cpu亲和配置,第⼀个参数是进程编号,第⼆个参数是cpu序号

cpu-map 1 0

4.2 配置多进程运行

1、配置haproxy以多进程方式运行

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
   log 127.0.0.1 local2
   chroot /var/lib/haproxy
   pidfile /var/run/haproxy.pid
   maxconn 4000
   user haproxy
   group haproxy
   daemon
   
   # 配置haproxy为多进程
   nbproc 4
   
   # 配置CPU亲和,将1234进程,绑定至0123的cpu核⼼
   cpu-map 1 0
   cpu-map 2 1
   cpu-map 3 2
   cpu-map 4 3
   stats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin

[root@proxy ~]# systemctl restart haproxy

2、检查多进程详情

[root@proxy ~]# pstree -p haproxy
haproxy(15295)
haproxy(15296)
haproxy(15297)
haproxy(15298)

4.3 配置多线程运行

因为每个进程都是独立的,且资源不可共享。而⼀个进程多个线程的资源是可以共享的,因此多线程的方式要比多进程的方式性能好;

1、配置Haproxy以多线程方式运行

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
   log 127.0.0.1 local2
   chroot /var/lib/haproxy
   pidfile /var/run/haproxy.pid
   maxconn 4000
   user haproxy
   group haproxy
   daemon
 
   # 配置Haproxy为多线程
   nbthread 4
   # turn on stats unix socket
   stats socket /var/lib/haproxy/stats mode 600 level admin

2、检查结果,{}表示线程

[root@proxy ~]# pstree -p haproxy
haproxy(11799)─┬─{haproxy}(11800)
               ├─{haproxy}(11801)
               └─{haproxy}(11802)

4.4 配置访问日志

1、修改 haproxy 配置

[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
global
   log 127.0.0.1 local2

2.修改 rsyslog 配置

[root@lb01 ~]# vim /etc/rsyslog.conf
# 启用rsyslog的udp
module(load="imudp") # needs to be done just once
input(type="imudp" port="514")

#启用级别为local2的设备,将该设备的所有级别的日志全部输出到/var/log/haproxy.log下
local2.*              /var/log/haproxy.log

3.重启 haproxy 以及 rsyslog 服务

[root@lb01 ~]# systemctl restart rsyslog haproxy

# 记得访问⼀下⽹站,产生日志信息
[root@lb01 ~]# tail -f /var/log/haproxy.log
 haproxy[188268]: 10.0.0.1:65082 [21/Mar/2024:11:45:24.990] proxy proxyservers/web01 0/0/0/1/1 200 213 - - ---- 1/1/0/0/0 0/0 "GET / HTTP/1.1"

5、Haproxy defaults配置

5.1 option选项

option 选项用来启用或禁用特定的功能。以下是⼀些常用的 option 参数及其简单描述:

选项

作用

配置示例

httplog

启用 HTTP 日志格式,主要记录HTTP 请求的详细日志。

option httplog

tcplog

启用 TCP 日志格式,主要记录TCP 连接的基本信息,比如连接时长、发送的字节数等。

option tcplog

dontlognull

不要记录空连接的日志。所谓的空连接指的是没有数据传输的连接,通常是健康检查或者非活动连接。

option dontlognull

http-server-close

每个 HTTP 请求完成后关闭客户端连接,同时尝试保持与后端服务器的连接复用,减少服务器负载。

option http-server-close

http-keep-alive

启用 HTTP 长连接,允许在同⼀连接上进行多个请求与响应,减少连接的建立和关闭的开销。

option http-keep-alive

redispatch

当找不到cookie对应的节点时,重新将请求分配给新的节点(Cookies植⼊时演示)

option redispatch

forwardfor

向后端服务器添加 X-Forwarded-For 头信息,包含了客户端的真实 IP 地址。

option forwardfor except 127.0.0.0/8

在 HAProxy 配置中,如果想要关闭某些选项时,可以在相应的配置段前⾯添加 no 前缀来禁用它们,例如: no option httplog

5.2 timeout选项

timeout 选项主要是用来设定超时时间的,以下是⼀些常用的 timeout 参数及其简单描述:

选项

作用

配置示例

queue

当请求超过服务器最⼤连接数,多余的请求会进⼊队列中,如果请求在队列中等待超过了1分钟,则该请求被丢弃

timeout queue 1m

http-request

等待客户端发送完整的 HTTP 请求头,的时间限制。若超过10秒则断开连接。

timeout http-request 10s

client

客户端空闲连接的最⼤持续时间。如果客户端在1分钟内没有数据传输,连接将被关闭。

timeout client 1m

server

服务器端空闲连接的最⼤持续时间。如果后端服务器在1分钟内没有数据传输,连接将被关闭。

timeout server 1m

connect

HAProxy与后端服务建立 TCP 连接的最⼤时间。如果在10秒内连接没有成功建立,请求将被视为失败。

timeout connect 10s

http-keep-alive

当启用keep-alive 后,此设置决定了客户端在连续两次 HTTP 请求之间,保持空闲状态的最⼤时间。如果在10秒内没有新的请求,连接将被关闭。

timeout http-keep-alive 10s

check

设置了健康检查连接的超时时间。如果在10秒内健康检查没有完成,后端服务器可能被标记为不健康。

timeout check 10s

6、Haproxy Proxies配置

代理相关配置:

  • frontend <name> :用于定义⼀系列监听的端口,这些端口可接受客户端请求并与之建立连接;

  • backend <name> :用于定义⼀系列后端服务器,代理将会将对应客户端的请求转发至这些服务器;

  • listen <name> :通过关联“前端”和“后端”定义了⼀个完整的代理;

frontend:负责监听客户端请求的端口,建立连接。

  • backend:负责定义要转发请求的后端服务器

  • listen:负责关联 frontend 和 backend,组成完整代理。)

6.1 mode参数

mode 概念:设置 Haproxy 运行的协议。

mode 语法: mode { tcp|http }

  • tcp: 实例运行于 TCP 模式,不对7层报文做任何检查;通常用于 SSL、SSH、MySQL 等应用

  • http: 实例运行于 HTTP 模式,客户端请求服务端,服务端重新封装请求报文,请求后端真实节点

mode 示例:

#----------------------------------------------------------------
# listen 中定义mode
#----------------------------------------------------------------
listen www
   bind *:8089
   mode tcp
   server web1 172.16.1.7:8080
   
#----------------------------------------------------------------
# frontend 中定义mode
#----------------------------------------------------------------
frontend shop *:80
   mode http
   use_backend webcluster

6.2 bind参数

bind 概念:设置 Haproxy 实例运行的端口

bind 语法: bind [<address>]:<port_range> [, ...] interface<interface>

  • <address> :可选选项,其可以为主机名、 IPv4 地址、 IPv6 地址或 * ;将其指定为 *0.0.0.0 时,将监听当前系统的所有 IPv4 地址;

  • <port_range> :可以是⼀个特定的 TCP 端口,也可是⼀个端口范围,如 8080-9090

  • <interface> :指定物理接口名称,仅能在 Linux 系统上使用;其不能使用接口别名,只有管理有权限指定绑定的物理接口;

bind 示例:

#----------------------------------------------------------------
# listen 中定义 bind
#----------------------------------------------------------------
listen proxy.ops.com
   bind *:80 # 单个端口
   mode tcp
   server web1 172.16.1.7:8080
   
#----------------------------------------------------------------
# frontend 中定义 bind
#----------------------------------------------------------------
frontend proxy.ops.com
   bind *:8899-9090 # 连续端口
   mode http
   use_backend webcluster

6.3 maxconn参数

maxconn 概念:设定最⼤的连接数,对于⼤型站点来说,应该尽可能提高此值,从而避免 haproxy ⽆法应答用户请求。当然,此值最⼤值不能超出 global 段中的定义。

maxconn 语法: maxconn <number>

在 HAProxy 中,每个连接通常会使用两个8KB的缓冲区,加上其他管理开销,⼤约占用17KB的内存。因此,在适当优化配置后,1GB的RAM 可以支持⼤约40,000到50,000个并发连接。

但也不要设置过高的连接数,以免超出服务器的内存容量,建议根据服务器的实际连接数,配置合理的连接数上限。

maxconn 示例:

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# Global settings (定义全局,最⼤不能超过4000并发连接数)
#----------------------------------------------------------------
global
   maxconn 4000
   
#----------------------------------------------------------------
# defaults settings (当frontend没定义则使用默认,最⼤不能超过3000并发连接数)
#---------------------------------------------------------------- 
defaults
   maxconn 3000
   
#----------------------------------------------------------------
# frontend settings (定义每个站点的最⼤连接数,所有站点并发值加起来不能超过Global中的设定)
#---------------------------------------------------------------- 
frontend web
   bind *:80
   mode http
   maxconn 2000
 
frontend java
   bind *:80
   mode http
   maxconn 2000

6.4 server参数

为后端声明⼀个 server 节点信息。不能用于 defaultsfrontend 区段,只能使用在 backend 区段和 listen 区段。

server 语法: server <name> <address>[:port] [param*]

  • <name> :为此服务器指定标识名称,会出现在日志文件中;

  • <address> :填写节点的 IPv4 地址,也支持使用可解析的主机名称;

  • [:port] :指定将连接所发往节点的目标端口,如未设定,则使用客户端请求的端口;

  • [param*] :为此服务器设定的⼀系参数;其可用的参数非常多,下⾯仅说明几个常用的参数;

1、 backup当所有的正常 server 均不可用时,此 backup 节点则会顶替提供服务;

backend webcluster
   server web1 172.16.1.7:80 backup     		# 当8与9都节点异常,则启用7节点
   server web2 172.16.1.8:80
   server web3 172.16.1.9:80

2、 check <port>对此节点进行 TCP 的健康状态检查;

backend webcluster
   server web1 172.16.1.7:80 backup 				# 当8与9都节点异常,则启用7节点
   server web2 172.16.1.8:80 check port 80 	# 检测tcp的80端口
   server web3 172.16.1.9:80 check port 80

3、 inter <delay>设定健康状态检查的时间间隔,单位为毫秒,默认为 2000 毫秒;

backend webcluster
   server web1 172.16.1.7:80 backup
   server web2 172.16.1.8:80 check inter 3000
   server web3 172.16.1.9:80 check inter 3000

4、 rise <count>设置 "离线状态" 转换至 "正常状态" 需要成功检查的次数;

backend webcluster
   server web1 172.16.1.7:80 backup
   server web2 172.16.1.8:80 check inter 3000 rise 2
   server web3 172.16.1.9:80 check inter 3000 rise 2

5、 fall <count>设置 "正常状态" 转换为 "不可用状态",需要检查的次数;

backend webcluster
   server web1 172.16.1.7:80 backup
   server web2 172.16.1.8:80 check inter 3000 rise 2 fall 3
   server web3 172.16.1.9:80 check inter 3000 rise 2 fall 3

6、 maxconn <maxconn>设定当前服务器能接受的最⼤连接数,如果此服务器的连接数高于指定的值,则将其放置请求队列中,以等待其它连接被释放;

# 所有节点加起来的连接之和不能超过global设定的maxconn
backend webcluster
   server web1 172.16.1.7:80 backup
   server web2 172.16.1.8:80 check inter 3000 rise 2 fall 3 maxconn 2000
   server web3 172.16.1.9:80 check inter 3000 rise 2 fall 3 maxconn 3000

7、 maxqueue <maxqueue>设定请求队列的最⼤长度,当请求超过 maxconn 设定的数值,剩余请求进⼊队列中,而队列的最⼤长度由 maxqueue 决定,队列的超时时间由 timeout queue 1m 选项决定。

backend webcluster
   balance roundrobin
   server web1 172.16.1.7:80 check maxconn 2000 maxqueue 200
   server web2 172.16.1.8:80 check maxconn 2000 maxqueue 200

8、 weight <weight>服务器节点权重,默认为1,最⼤值为256,0表示不参与负载均衡,等同于将该节点进行下线处理

backend webcluster
   server web1 172.16.1.7:80 backup
   server web2 172.16.1.8:80 check inter 3000 rise 2 fall 3 maxconn 2000 max queue 200 weight 2
   server web3 172.16.1.9:80 check inter 3000 rise 2 fall 3 maxconn 3000 max queue 200 weight 2

7、使用子配置管理

  • 当业务众多时,将所有配置都放在⼀个配置文件中,会造成维护困难。

  • 可以考虑按业务分类,将配置拆分,放在不同的配置文件中,从而达到方便维护的目的。

7.1 创建子配置文件

# 创建子配置文件,注意:必须为 cfg 后缀
[root@haproxy ~]# mkdir /etc/haproxy/conf.d
[root@haproxy ~]# vim /etc/haproxy/conf.d/app.ops.net.cfg
frontend app_web
   bind *:80
   mode http
   use_backend app_servers
 
backend app_servers
   balance roundrobin
   server web1 172.16.1.7:81 check inter 3000 fall 2 rise 5
   server web2 172.16.1.8:81 check inter 3000 fall 2 rise 5

7.2 修改启动文件(rpm安装不需要调整)

# 添加子配置目录到 unit 文件中,使其能加载子目录配置
[root@haproxy ~]# cat /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=network-online.target
Wants=network-online.target

[Service]
EnvironmentFile=-/etc/sysconfig/haproxy
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid"

##定义配置文件路径变量
Environment="CONFIG_D=/etc/haproxy/conf.d/"

# 启动或停⽌使用 -f 调用CONFIG_D变量
ExecStartPre=/usr/sbin/haproxy -f $CONFIG -f $CONFIG_D -c -q $OPTIONS
ExecStart=/usr/sbin/haproxy -Ws -f $CONFIG -f $CONFIG_D -p $PIDFILE $OPTIONS
ExecReload=/usr/sbin/haproxy -f $CONFIG -f $CONFIG_D -c -q $OPTIONS
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
SuccessExitStatus=143
Type=notify

[Install]
WantedBy=multi-user.target

7.3 重载服务

[root@haproxy ~]# systemctl daemon-reload
[root@haproxy ~]# systemctl restart haproxy

02 Haproxy高级功能

1、基于cookie会话保持

1.1 Cookie植⼊介绍

HAProxy 中,可以基于 cookie 的功能来确保同⼀用户始终与同⼀个后端服务器连续交互,从而保持会话的⼀致性。这是如何⼯作的:

  • 1、当用户第⼀次访问通过 HAProxy 负载均衡的应用时,HAProxy 选择⼀个后端服务器来处理请求,并在响应的中包含⼀个特殊的 cookie。(通常是节点名称或ID,自行指定)

  • 2、用户的浏览器存储这个 cookie,并在随后的请求中将它发送给HAProxy

  • 3、当 HAProxy 收到带有此 cookie 的请求时,它会读取 cookie 的值,并将请求直接发送到之前选定的那台后端节点。

这样,即使有多个请求,用户的会话都会与最初选定的后端服务器保持⼀致性。

要启用这个功能,需要在 HAProxy 配置文件的 backend 部分进行设置。设置⼀个 cookie 名称,并在 server 指令中为每台服务器的指定 cookie 的值。

1.2 配置示例

1.配置 haproxy 基于 cookie 实现会话绑定。

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend proxy.ops.com
   bind *:80
   mode http
   use_backend webcluster
   
#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend webcluster
   balance roundrobin
 
   # 回传数据时添加Set-Cookie,Key为SERVERID,Value为节点定义cookie值
   cookie SERVERID insert nocache
   server web1 172.16.1.7:80 check cookie web1 # 为每个服务器定义⼀个cookie名称标识
   server web2 172.16.1.8:80 check cookie web2

1.3 验证session

1、客户端第⼀次请求。 haproxy 会挑选⼀个节点响应,并会通过 Set-Cookies 返回该响应的是哪台后端节点

2、客户端第二次请求,此时客户端会通过 Request 携带 对应的Cookie

3、如果此时将服务端分配给客户端的节点关闭,则会造成客户端请求失败,那么该如何实现故障转移;

1.3 redispatch参数

当使用了cookie持久化连接时, haproxy将会将其请求的后端服务器定义的serverID插入到客户端的cookie中,以保证会话的持久性;如果后端的节点出现了宕掉,但是客户端的cookie是不会刷新的,如果设置此参数,将会将客户的请求强制定向到另外一个正常的后端server上,以保证服务的正常。

cookie植入实现会话保持,完整配置示例

[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend proxy.ops.com
  bind *:88
  mode http
  option forwardfor except 127.0.0.1
  option redispatch # 启动redispatch

  maxconn 10000
  use_backend webcluster
  
#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend webcluster
  balance roundrobin
  cookie SERVERID insert nocache
  server web1 172.16.1.7:80 check cookie web01
  server web2 172.16.1.8:80 check cookie web02

2、基于web管理haproxy

HAProxy 有统计报告功能,可以让使用者通过 web页面概览后端服务器的概况,甚至更改它们的状态。

2.1 状态页配置

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# Stats settings
#----------------------------------------------------------------
listen haproxy-stats
  mode http
  bind *:9999
  stats enable		 	# 启用stats功能
  stats refresh 5s 	# 设定自动刷新时间间隔
  stats hide-version 		# 隐藏haproxy版本
  stats uri /haproxy-stats 		 # stats页面的访问路径
  stats realm "HAProxy stats"  # 认证提示信息
  stats auth ops:123456			 	 # 认证的账号和密码
  stats admin if TRUE 	# 启用管理功能
  #http-request user-service prometheus-exporter if { path /metrics }

2.2 状态页登陆

通过http://IP:port/haproxy?stats访问该页面

1. 标题与基础标识

  • HAProxy:表明这是 HAProxy 负载均衡器的统计界面。

  • Statistics Report for pid 1943:该报告对应 进程 ID 为 1943 的 HAProxy 实例。

2. 常规进程信息(General process information)

这部分展示 HAProxy 进程的核心运行参数:

参数

含义解析

pid = 1943 (process #1, nbproc = 4)

- pid = 1943:当前 HAProxy 进程的 ID。
-
process #1:该进程是第 1 个主进程(HAProxy 多进程模式下,主进程负责管理子进程)。
-
nbproc = 4:HAProxy 配置中启动了 4 个工作进程(多进程模式可提升并发处理能力)。

uptime = 0d 0h48m19s

HAProxy 进程的运行时长,此处为「0 天 0 小时 48 分 19 秒」,说明进程启动后稳定运行约 48 分钟。

system limits: memmax = unlimited; ulimit-n = 8038

- memmax = unlimited:进程内存使用无上限限制(由系统内存资源决定)。
-
ulimit-n = 8038:系统对进程的文件描述符最大限制(每个网络连接需占用文件描述符,该值影响 HAProxy 能承载的最大连接数)。

maxsock = 8038; maxconn = 4000, maxpipes = 0

- maxsock = 8038:HAProxy 能打开的最大套接字数(与 ulimit-n一致,因为套接字依赖文件描述符)。
-
maxconn = 4000:HAProxy 配置的最大并发连接数(全局限制,实际需结合前端/后端业务调整)。
-
maxpipes = 0:TCP 管道缓冲的限制(设为 0 表示未显式限制,默认行为)。

current conns = 2; current pipes = 0/0; conn rate = 0/sec; bit rate = 948.699 kbps

- current conns = 2:当前活跃连接数(仅 2 个,负载极低)。
-
current pipes = 0/0:当前 TCP 管道的使用数/最大数(0 表示未启用或无管道流量)。
-
conn rate = 0/sec每秒新建连接数(当前无新连接进入,业务流量平峰或空闲)。
-
bit rate = 948.699 kbps:网络带宽流量(每秒传输约 948.7 KB 数据,流量水平较低)。

Running tasks: 0/20; idle = 99 %

- Running tasks: 0/20:工作进程的任务队列状态(0 表示无待处理任务,20 是任务队列容量)。
-
idle = 99 %:CPU 空闲率(高达 99%,说明系统负载极低,资源充裕)。

3. 右侧「状态图例」解析

界面右侧的颜色标识用于区分 前端(Frontend)或后端(Backend)中服务器的状态,关键状态含义如下:

颜色/状态

含义

active UP(绿色)

服务器活动且正常提供服务(健康检查通过)。

backup UP(蓝色)

备份服务器正常待命(主服务器故障时自动接管)。

active DOWN(红色)

服务器活动但当前不可用(健康检查失败,如端口不通、响应超时)。

not checked(灰色)

服务器未配置健康检查(或临时未执行检查)。

active or backup DOWN for maintenance (MAINT)(棕色)

服务器因主动维护(MAINT 状态)而下线(人工干预暂停服务)。

注释:"NOLB"/"DRAIN" = UP with load-balancing disabled

- NOLB/DRAIN状态:服务器仍为 UP,但负载均衡功能被禁用(如 DRAIN 状态下,服务器不再接收新连接,仅处理现有连接)。

操作选项

作用解释

Set state to READY

设为「就绪」状态 → 允许接收新请求、处理会话。

Set state to DRAIN

设为「排水」状态 → 不再接收新请求,但会处理完现有会话(适合维护时用)。

Set state to MAINT

设为「维护」状态 → 不接收新请求,也不处理会话(强制下线)。

Health: disable checks

禁用健康检查 → HAProxy 不再探测该服务器是否存活(慎用,可能导致流量打到故障节点)。

Health: enable checks

启用健康检查 → 恢复对该服务器的健康状态探测。

Health: force UP

强制标记为「UP」→ 无论探测结果如何,都认为服务器可用(绕过健康检查)。

Health: force NOLB

强制「不负载均衡」→ 服务器仍在线,但负载均衡器不再向其分配流量(保留连接但暂不调度)。

Health: force DOWN

强制标记为「DOWN」→ 无论实际状态如何,都认为服务器不可用(流量直接跳过)。

Agent: disable checks

禁用「代理级健康检查」→ 若配置了 agent-check(自定义健康检查脚本),则停止探测。

Agent: enable checks

启用「代理级健康检查」→ 恢复自定义健康检查逻辑。

Agent: force UP

强制代理层标记为「UP」→ 绕过代理健康检查逻辑,强制认为服务器可用。

Agent: force DOWN

强制代理层标记为「DOWN」→ 绕过代理健康检查逻辑,强制认为服务器不可用。

Kill Sessions

终止选中服务器的所有当前会话(强制断开客户端连接,用于紧急止损)。

3、基于socat管理haproxy

3.1 安装socat

[root@haproxy ~]# yum install socat -y

3.2 修改配置文件

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
global
  # turn on stats unix socket
  stats socket /var/lib/haproxy/stats level admin
  
[root@haproxy ~]# systemctl restart haproxy

3.3 实现主机动态下上线

# 获取详情
[root@haproxy ~]# echo "show info" | socat stdio /var/lib/haproxy/stats

# 获取当前的统计信息
[root@haproxy ~]# echo "show stat" | socat stdio /var/lib/haproxy/stats

# 动态下线主机
[root@haproxy ~]# echo "disable server app_servers/web1" | socat stdio /var/lib/haproxy/stats

# 动态上线主机
[root@haproxy ~]# echo "enable server app_servers/web1" | socat stdio /var/lib/haproxy/stats

3.4 Shell脚本实现代码滚动升级

[root@proxy /etc/haproxy/conf.d]# cat app.cfg 
frontend app_web
   bind *:81
   mode http
   use_backend app_servers
 
backend app_servers
   balance roundrobin
   server 10.0.0.39 10.0.0.39:81 weight 2 check inter 3000 fall 2 rise 5
   server 10.0.0.49 10.0.0.49:81 weight 2 check inter 3000 fall 2 rise 5

[root@manager ~]# echo "New Deploy Haproxy 10.0.0.39" > index.html.10.0.0.39
[root@manager ~]# echo "New Deploy Haproxy 10.0.0.49" > index.html.10.0.0.49
[root@manager ~]# cat deploy.sh
#!/usr/bin/bash

# 集群节点
web_cluster="10.0.0.39 10.0.0.49"

# lb节点IP
lb_server="10.0.0.29"

# 集群资源池名称
cluster="webservers"

# 节点的代码路径
webdir=/proxy

for host in ${web_cluster}
do
  # 1.登录负载均衡,动态摘掉节点
  ssh root@${lb_server} "echo 'disable server ${cluster}/${host}' | socat stdio /var/lib/haproxy/stats"

  # 2.更新节点的代码
  scp ./index.html.${host} root@${host}:${webdir}/index.html
  sleep 2

  # 3.登录负载均衡,动态加入节点
  ssh root@${lb_server} "echo 'enable server ${cluster}/${host}' | socat stdio /var/lib/haproxy/stats"

  # 4.等待一会在继续下一台
  sleep 5
done

[root@manager ~]# chmod +x deploy.sh

[root@manager ~]# while true ; do curl http://10.0.0.29:81 ; sleep 1 ; done

4、后端节点状态监测

对于Haproxy后端节点的状态检测有如下三种方式:

  • 1、基于端口做状态监测,此为默认方式

  • 2、基于指定 URI 做状态监测

  • 3、基于指定 URI request 请求头部内容做状态监测;

4.1 基于端口监测

检查后端serverTCP端口是否正常,但不能保证该端口上运行的业务是否正常工作。

[root@proxy ~]# vim /etc/haproxy/haproxy.cfg
listen http_proxy
  bind *:80
  mode http
  balance roundrobin
  server web1 172.16.1.7:80 check 80 	# 基于tcp检测端口
  server web2 172.16.1.8:80 check

4.2 httpchk参数

option httpchk指令基于http协议来做健康检查,只有返回状态码为2xx3xx的才认为是健康,其余所有状态码都认为不健康。如果不设置该选项时,默认采用 tcp做健康检查,只要能建立 tcp就表示健康。

option httpchk语法:

  • option httpchk

  • option httpchk <uri>:检查的uri路径,默认为/。接受带有查询参数的uri

  • option httpchk <method> <uri>http检查使用的方法。建议采用 HEAD方法。

  • option httpchk <method> <uri> <version>:检查的HTTP协议版本,默认为HTTP/1.0,如果修改为HTTP/1.1,还强制要求指定Host,中间使用\r\n隔离。

4.3 基于URI检测

通过GET方法获取后端节点的资源,基本上可以判断后端服务是否正常工作。

[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
listen http_proxy
  bind *:80
  mode http
  balance roundrobin

  option httpchk GET /index.html 		# 基于URI,会消耗网络带宽
  server web1 172.16.1.7:80 check port 80
  server web2 172.16.1.7:80 check port 80

4.3 基于URI头检测

通过request获取的头部信息进行匹配,然后进行健康检测。

将检查的请求发送至后端节点的80端口,然后与Host进行匹配,用来确定该后端节点是否正常。

[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
listen http_proxy
  bind *:80
  mode http
  balance roundrobin

  option httpchk HEAD / HTTP/1.1\r\nHost:\ app.ops.net # head减少网络消耗
  server web1 172.16.1.7:80 check port 80
  server web2 172.16.1.8:80 check port 80

5、IP地址透传

web服务器中记录客户端的真实IP地址,主要用于访问统计、安全防护、行为分析、区域排行等场景。

5.1 七层负载地址透传

Haproxy工作于反向代理模式,其发往服务器的请求中的客户端 IP 均为Haproxy 主机的地址而非真正客户端的地址,这会使得服务器端的日志信息记录不了真正的请求来源,X-Forwarded-For 首部则可用于解决此问题。

  • HAProxy可以向每个发往的后端节点添加此首部,并以客户端IP为其value

  • option forwardfor语法: option forwardfor [ except <network> ] [ header <name> ] [ if-none ]

  • <network>:可选参数,当指定时,源地址为匹配至此网络中的请求都禁用此功能;

  • <name>:可选参数,可自定义一个首部,如X-Client来替代X-Forwarded-For

  • if-none:仅在此首部不存在时才将其添加至请求报文中;

option forwardfor示例:

[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
defaults
  option forwardfor	 # 此为默认值,首部字段默为:X-Forwarded-For

frontend main
  bind *:80
  mode http
  default_backend app

backend app
  balance roundrobin
  server web 172.16.1.7:80 check
  server web 172.16.1.8:80 check

检查后端 nginx 日志

172.16.1.5 - - [25/Dec/2020:11:18:36] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "10.0.0.1"

5.2 四层负载地址透传

1、配置Haproxy基于TCP协议访问

[root@proxy /etc/haproxy/conf.d]# vim app.cfg 
frontend app_web
   bind *:81
   mode http
   use_backend app_servers
 
backend app_servers
   balance roundrobin
   option httpchk HEAD / HTTP/1.1\r\nHost:\ app.ops.net
   server 10.0.0.39 10.0.0.39:81 weight 2 check inter 3000 fall 2 rise 5
   server 10.0.0.49 10.0.0.49:81 weight 2 check inter 3000 fall 2 rise 5 

frontend main
  bind *:899
  mode tcp
  default_backend app

backend app
  balance roundrobin
  mode tcp
  
  server web 10.0.0.39:889 check send-proxy

2、配置后端web节点

[root@web1 ~]# vi /etc/nginx/nginx.conf
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$proxy_protocol_addr"';
                      
#nginx配置:变量$proxy_protocol_addr 记录透传过来的客户端IP
[root@web1 ~]# cat /etc/nginx/conf.d/main.ops.net.conf 
server {
  listen 889 proxy_protocol;
  server_name main.ops.net;
  location / {
    root /web;
    index index.html;
    }
}

3、检查后端节点日志

[root@lb01 ~]# tail -f /var/log/nginx/access.log
172.16.1.3 - - [25/Dec/2020:11:18:36] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "10.0.0.1"

6、自定义Haproxy日志

HAProxy 中,可以使用 capture来捕获 HTTP 请求或响应的头部(Header)信息。然后将这些捕获的信息写入日志文件中。

  • 1、可以选择记录请求头部(request header)或响应头部(response header)中的特定信息;

  • 2、例如捕获 Host,Content-length,User-agent,X-Forwarded-For等;

  • 3、最后还需要指定对应Header的值保留的长度;

在日志中,这些头部信息被括在花括号 {} 中,如果有多个捕获的头部,它们会按照顺序出现,彼此之间用竖线 | 分隔。如果请求或响应中不存在指定的头部,日志将会记录一个空字符串代表这个头部的值。

capture语法: capture [request|response] header <name> len <length>

  • <name>:要捕获的名称,不区分大小写。注意:记录在日志中是首部对应的值,而非首部名称。

  • <length>:指定记录首部值的字符长度,超出的部分将会被忽略。

6.1 配置示例

1、捕获Request中的HostUser-Agent、以及response中的Server

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
  bind *:80
  mode http
  
  capture request header Host len 40        	# 捕捉请求的域名
  capture request header User-Agent len 16  	# 捕捉请求的设备
  capture response header Server len 40 			# 捕捉响应的Server是什么
  use_backend webcluster

backend webcluster
  balance roundrobin
  server web1 172.16.1.7:80 check
  server web2 172.16.1.8:80 check

6.2 验证日志

日志文件中,关注{}中内容,第一个{}是request,第二个{}是response

haproxy: 10.0.0.100:48132 main webcluster/web2 {proxy.ops.net|curl/7.29.0}

haproxy: 10.0.0.100:48132 main webcluster/web2 {proxy.ops.net|curl/7.29.0} {nginx/1.18.0}

Nov 16 10:29:00 localhost haproxy[991]: 10.0.0.244:55896 [16/Nov/2025:10:29:00.574] proxy proxyservers/web02 0/0/0/1/1 200 202 - - ---- 2/2/0/0/0 0/0 {10.0.0.9|Mozilla/5.0 (Win} {nginx/1.20.1} "GET / HTTP/1.1"

7、自定义错误页面

Haprony自定义错误页面有两种方式

  • 第一种:由haproxy本机提供错误页面,通过errorfile参数来设定。

  • 第二种:将错误转到一个指定的url地址,通过errorcode参数来设定。

7.1 errorfile

1、通过errorfile将错误交给本机haproxy处理,修改haproxy配置

[root@haproxy ~]# cat /etc/haproxy/haproxy.cfg
defaults
  ...
  errorfile 503 /opt/503.http

2、准备503对应的错误页面,需要添加头部信息;

[root@haproxy ~]# vim /opt/503.http
HTTP/1.1 503
Content-Type:text/html;charset=utf-8

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>报错页面</title>
  </head>
  
  <body>
    <center><h1>网站维护中........请稍候再试</h1></center>
    <center><h2>联系 haoge 处理解决</h2></center>
    <center><h3>503 Service Unavailable</h3></center>
  </body>
</html>

3、测试将后端的web服务器停止后测试

7.2 errorloc

1、使用 errorloc 选项来捕捉错误状态码,然后将其重定向到其他url

[root@haproxy ~]# cat /etc/haproxy/haproxy.cfg
defaults
  ...
  errorloc 503 http://10.0.0.19/error/

2、测试页面代码

📎1.gif

📎2.gif

[root@web1 /usr/share/nginx/html/error]# cat index.html 
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>404 Not Found</title>
  <style>
    /* 全局样式重置(可选) */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      background-color: #fff; /* 页面背景色 */
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh; /* 让页面垂直居中 */
    }
    /* 容器:控制整体布局 */
    .container {
      width: 80%;
      max-width: 1000px;
      display: flex;
      flex-direction: row; /* 横向排列左中右 */
      align-items: center;
      justify-content: space-between;
      padding: 20px;
    }
    /* 左侧/右侧区域:表情+文字 */
    .left-section,
    .right-section {
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    .left-section img,
    .right-section img {
      width: 150px;   /* 表情宽度,可自定义 */
      height: 150px;  /* 表情高度,可自定义 */
      object-fit: contain; /* 保持比例 */
      margin-bottom: 10px;
    }
    .left-text,
    .right-text {
      font-size: 18px;
      color: #333; /* 文字颜色 */
      line-height: 1.5;
    }
    /* 中间区域:错误标题+按钮 */
    .middle-section {
      text-align: center;
    }
    .error-title {
      font-size: 20px;
      color: #333;
      margin-bottom: 20px;
    }
    .btn-group {
      display: flex;
      gap: 10px; /* 按钮间距 */
    }
    .btn {
      padding: 8px 16px;
      border-radius: 4px;
      cursor: pointer;
      text-decoration: none;
      font-size: 14px;
      border: 1px solid transparent;
    }
    /* 主按钮(返回首页)样式 */
    .btn-primary {
      background-color: #ff5722; /* 橙色,可自定义 */
      color: #fff;
      border-color: #ff5722;
    }
    /* 辅助按钮(保证不打死管理员)样式 */
    .btn-default {
      background-color: #fff;
      color: #333;
      border-color: #ccc;
    }

    /* 响应式:手机端自动切换为纵向布局 */
    @media (max-width: 768px) {
      .container {
        flex-direction: column;
        width: 90%;
      }
      .left-section,
      .middle-section,
      .right-section {
        margin-bottom: 20px;
      }
    }
  </style>
</head>
<body>
  <div class="container">
    <!-- 左侧:表情+文字 -->
    <div class="left-section">
      <!-- 替换为你自己的表情包地址 -->
      <img src="./images/2.gif" alt="左侧表情" />
      <p class="left-text">卧槽!页面不见了!</p>
    </div>

    <!-- 中间:错误标题+按钮 -->
    <div class="middle-section">
      <h2 class="error-title">404错误!抱歉您要找的页面不存在</h2>
      <div class="btn-group">
        <a href="#" class="btn btn-primary">返回首页</a>
        <a href="#" class="btn btn-default">保证不打死管理员</a>
      </div>
    </div>

    <!-- 右侧:表情+文字 -->
    <div class="right-section">
      <!-- 替换为你自己的表情包地址 -->
      <img src="./images/1.gif" alt="右侧表情" />
      <p class="right-text">兄弟<br>莫激动</p>
    </div>
  </div>
</body>
</html>

[root@web1 /usr/share/nginx/html/error]# ls
images  index.html

[root@web1 /usr/share/nginx/html/error]# ls images/
1.gif  2.gif

3、测试访问haproxy,会发现请求被重定向到新的uri

8、自定义HTTP报文

8.1 http-request

使用 http-request add-header 可以在请求报文中添加一个Header,实现原理client-->haproxy(添加请求Header)--> web

1、在 frontend 中使用 http-request add-header,给后端节点发送一个X-viaHeader

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
  bind *:88
  mode http
  http-request add-header X-via Haproxy-Node1 # HeaderName HeaderValue
  use_backend webcluster
  
[root@lb01 ~]# systemctl reload haproxy

2、修改后端 nginx.conflogformat 添加 "$http_x_via"

[root@web ~]# vim /etc/nginx/nginx.conf
http {
...
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for" "$http_x_via"';
...
}

[root@web ~]# systemctl reload nginx

3、再次访问,检查后端 Nginx 日志,此时就能会看到是通过哪个调度器,调度到本机

10.0.0.9 - - [16/Nov/2025:10:54:31 +0800] "GET / HTTP/1.1" 200 5 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" "10.0.0.244" "Haproxy-Node1"

10.0.0.9 - - [16/Nov/2025:10:54:35 +0800] "GET / HTTP/1.1" 200 5 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" "10.0.0.244" "Haproxy-Node1"

8.2 http-response

使用 http-response add-header 可以在响应报文中添加一个Header,实现原理web-->haproxy--(添加响应Header)-->client

1、在 frontend 中使用 http-response add-header,告诉客户端资源经过了哪个代理服务。

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
  bind *:80
  mode http
  http-response add-header Res-Server Haproxy-3.0
  use_backend webcluster

[root@lb01 ~]# systemctl reload haproxy

2、客户端访问测试,然后检查响应 Header

8.3 http-response del

使用 http-response del-header 可以在响应报文中删除一个Header,实现原理web-->haproxy--(删除Header)-->client

1、在 frontend 中使用 http-response del-header,删除后端Server的版本信息。

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
  bind *:80
  mode http
  http-response add-header Res-Server Haproxy-3.0
  http-response del-header Server 	 # 删除返回给客户端的Header字段
  use_backend webcluster

[root@lb01 ~]# systemctl reload haproxy

2、客户端访问测试,然后检查响应 Header

03 Haproxy调度算法

1、调度算法介绍

Haproxy 根据后端服务器的负载,或其他的计算的标准,判断挑选哪台 RS来进行请求处理。

Haproxy 调度算法语法,可用于 defaults、listen、backend ;

balance <algorithm|算法> [ <arguments> ]
balance url_param <param> [check_post [<max_wait>]]

2、roundrobin

roundrobin :基于权重进行轮询,保持均匀分布,这是最平衡、最公平的算法。

此算法是动态的,表示其权重可以在运行时通过socat方式进行动态调整,不过,在设计上,每个后端服务器仅能最多接受 4128 个连接;

2.1 配置示例

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
   
backend webcluster
   balance roundrobin
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

2.2 调度测试

[root@client ~]# curl -HHost:proxy.ops.net http://10.0.0.5:80
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.net http://10.0.0.5:80
Web Page RS-Node2

2.3 动态调整

通过 socat 命令动态调整权重,将 web1 节点的权重调整为 3

# 查看当前权重
[root@proxy /etc/haproxy]# echo "get weight proxyservers/web01" | socat stdio /var/lib/haproxy/stats
1 (initial 1)

[root@proxy /etc/haproxy]# echo "get weight proxyservers/web02" | socat stdio /var/lib/haproxy/stats
1 (initial 1)


# 调整web1权限为3,调整后测试,会发现立即⽣效
[root@proxy /etc/haproxy]# grep socket haproxy.cfg 
    stats socket /var/lib/haproxy/stats mode 600 level admin
    
[root@proxy /etc/haproxy]# echo "set weight proxyservers/web01 2" | socat stdio /var/lib/haproxy/stats

[root@proxy /etc/haproxy]# echo "get weight proxyservers/web01" | socat stdio /var/lib/haproxy/stats
2 (initial 1)

3、static-rr

static-rr :轮询调度算法,此算法是静态的,表示其权重不可以在运行时进行调整;

3.1 配置示例

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
 
backend webcluster
   balance static-rr
   server web1 172.16.1.7:80 check weight 2
   server web2 172.16.1.8:80 check weight 1

3.2 调度示例

[root@client ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80
Web Page RS-Node2

4、leastconn

leastconn :新的连接请求被派发至具有最少连接数目的后端服务器;

此算法是动态的,可以在运行时调整其权重;

4.1 配置示例

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
 
backend webcluster
   balance leastconn
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

4.2 调度测试

5、source

source :源地址 hash 调度算法,将请求的源地址进行 hash 运算,得到⼀个具体的数值,同时对后端服务器进行编号,按照运算结果将请求分发到对应编号的服务器上。

这可对不同源 IP 的访问进行负载分发,对同⼀个客户端 IP 的请求始终被派发至某特定的服务器。

注意:如某服务器宕机或新添加了服务器,许多客户端的请求可能会被派发至与此前请求不同的服务器;不过也可以使用 hash-type 修改此特性;

5.1 配置示例

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
   
backend webcluster
   balance source
   # hash-type { map-based | consistent }
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

5.2 调度测试

[root@client ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80
Web Page RS-Node1

6、uri

uri :基于对用户请求的 uri 做 hash 并将请求转发到后端指定服务器。

理解:同⼀个节点访问不同的 uri 可能会被调度到不同的后端服务器处理,但是不同的节点访问相同的 uri 则会被调度到同⼀台服务器处理,因为基于uri 调度是在服务端完成的而非客户端。

这种调度算法多用在缓存场景,能有效提高命中率。

注意,此算法仅应用于 HTTP 后端服务器场景;其默认为静态算法,不过也可以使用 hash-type 修改此特性;

6.1 ⻚⾯准备

后端集群 uri ⻚⾯准备

# web1
[root@web01 ~]# mkdir /opt/{app1,app2} -p
[root@web01 ~]# echo "web1 app1" > /opt/app1/index.html
[root@web01 ~]# echo "web1 app2" > /opt/app2/index.html

# web2
[root@web02 ~]# mkdir /opt/{app1,app2} -p
[root@web02 ~]# echo "web2 app1" > /opt/app1/index.html
[root@web02 ~]# echo "web2 app2" > /opt/app2/index.html

6.2 配置示例

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
 
backend webcluster
   balance uri
   # hash-type { map-based | consistent }
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

6.3 调度测试

client 测试,访问不同 url 测试,不同 uri 会被调度到不同的后端节点处理;

# 请求app1资源,发现是web2提供
[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80/app1/index.html
web2 app1

# 请求app2资源,发现是web1提供
[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80/app2/index.html
web1 app2

更换其他 Client 测试,使用不同的节点,但请求相同 uri  会调度到同⼀后端服务器处理;

[root@route ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80/app1/index.html
web2 app1

[root@route ~]# curl -HHost:proxy.ops.com http://10.0.0.5:80/app2/index.html
web1 app2

7、url

url_param ,它允许根据请求 URL 中指定的查询参数的值来进行哈希计算。例如,使用 user 参数来进行哈希,那么 user 参数值相同的请求,都会被路由到同⼀台后端服务器。这样,即便是分布在多个请求中,用户的会话也能保持⼀致性。

url_params 调度算法配置示例,(基于用户请求中携带的 user 做 hash)

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
 
backend webcluster
   balance url_param user
   # hash-type { map-based | consistent }
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

7.1 调度测试

Client 测试,不带参数请求,默认为轮询调度

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80
Web Page RS-Node2

Client 测试,携带相同的 User 参数请求,会发现始终定向到同⼀台后端节点

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=ops
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=ops
Web Page RS-Node1

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=ops
Web Page RS-Node1

Client 测试,携带不同的 User 参数请求,发现可能会调度到不同的节点

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=jason
Web Page RS-Node2

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=jack
Web Page RS-Node2

[root@client ~]# curl -HHost:proxy.ops.com 172.16.1.5:80?user=it
Web Page RS-Node1

8、hdr

hdr(<name>) :针对用户发起 HTTP 请求中 Header 中的 <name> 关键字进行 hash 计算,如⽆有效的值,则会被轮询调度;

此算法默认为静态的,不过其也可以使用 hash-type 修改此特性;(使用较少)

8.1 配置示例

hdr(<name>) 调度算法配置示例(基于客户端浏览器来调度)

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend main
   bind *:80
   mode http
   use_backend webcluster
   
backend webcluster
   balance hdr(User-Agent)
   # hash-type { map-based | consistent }
   server web1 172.16.1.7:80 check
   server web2 172.16.1.8:80 check

8.2 调度测试

client 测试,置空 User-Agent 字段,会发现进⼊了轮询。

[root@client ~]# curl -A '' -HHost:proxy.ops.com 172.16.1.5:80
Web Page RS-Node1

[root@client ~]# curl -A '' -HHost:proxy.ops.com 172.16.1.5:80
Web Page RS-Node2

client 测试,通过 curl 命令模拟⾕歌,发现调度到 web2 节点。

[root@client ~]# curl -A 'Chrome' -H Host:proxy.ops.com 172.16.1.5:80
web1

4.使用 Chrome 浏览器测试访问,经过 hash 取模后发现依然会调度到 web1 节点。

9、hash_type

  • hash-type <method> 用于将 hash 映射至后端服务器的方法;

  • 可用方法有 map-based|consistent ;

  • ⼤多数场景下使用默认 map-based 方法;

9.1 map-based

map-based 算法实现公式: hash(ip) % node_counts = index

  • map-based 算法问题:如果下线⼀台节点,会出现重新计算 hash 值,造成⼤规模变化。

9.2 consistent

consistent :⼀致性哈希,该 hash 是动态的,当服务器节点下线,或新增⼀台节点,对调度结果影响是局部的,不会引起⼤的变动。

⼀致性 Hash 算法也是使用取模的方法,但不是对服务器节点数量进行取模,而是对 2的32方 取模。即,⼀致性 Hash 算法将整个 Hash 空间组织成⼀个虚拟的圆环, Hash 函数值的空间为 0 ~ 2^32 - 1 ,整个哈希环如下:

  • Hash算法原理

  • Hash算法增加节点

  • Hash算法减少节点

  • Hash算法数据倾斜问题

backend proxyservers
   balance source
   hash-type consistent

04 Haproxy ACL实践

1、ACL基本概述

1.1 什么是ACL

ACL 在 HAProxy 中是用来决定请求如何被处理的⼀种规则。我们可以将其想象成⼀个检查站,每个过往的请求都要经过这个检查站,而 ACL 就是检查员,根据⼀些特定的标准来判断请求是否符合某种条件。在 HAProxy 中使用 ACL,通常涉及以下两个步骤:

1、定义ACL 规则: 即定义⼀个测试条件,条件可以是请求报文中的源地址、源端口、目标地址、目标端口、请求方法、 URL 、文件后缀等;这些条件是我们用来识别和分类流量的依据。

举个例子,如果我们想识别所有请求 /admin 管理⻚⾯的流量,我们可以定义如下这样ACL规则

acl is_admin_page path_beg /admin

# is_admin_page 是我们给这个条件起的名字、path_beg 指的是请求的路径开头部分、/admin 则是路径的具体内容

2、 定义好条件后,我们要告诉 HAProxy 当条件满足时应该做什么。这个“做什么”可以是将流量引导到特定的服务器,也可以是拒绝请求。例如:当请求满足is_admin_page 这个条件时,把请求发送到名叫 admin_servers 的服务器组。

use_backend admin_servers if is_admin_page

总的来说,ACL 就是设置的⼀系列规则,这些规则基于请求的信息来决定应该采取什么行动。

整个流程就像是:请求来了 -> ACL 检查请求 -> 根据规则执行相应的动作。

这样我们就可以根据请求的特征来决定如何处理它们。

1.2 ACL定义方法

# ACL语法
acl <aclname> <criterion> [flags] [operator] [<value>] ...
#acl 名称 条件 条件标记位 具体操作符 操作对象类型  

# ACL示例
acl my_chrome hdr(User-Agent) -i -m sub Chrome

<aclname> : ACL 名称,可使用字⺟ 数字 : . - _ 区分字符⼤⼩写<criterion> : 比较的标准和条件

  • 基于源地址、源端口、目标地址、目标端口: src、src_port、dst、dst_port

  • 基于 Header 信息比对: hdr、hdr_beg、hdr_end、hdr_dom

  • 基于路径或后缀比对: path_beg、path_end

  • 基于请求的方法比对: method

<flags> :条件标记

  • -i 不区分⼤⼩写

  • -m 使用 pattern 匹配方法

  • -n 不做 DNS 解析

  • -u 禁⽌ acl 重名,否则多个同名的 ACL 为或的关系

[operator] :条件筛选

  • -eq(等于)、-ne(不等于)、-ge(⼤于或等于)、-le(⼩于或等于)、gt(⼤于)、lt(⼩于)

  • (-m str) :字符串必须完全匹配模式

  • (-m sub) :在提取的字符串中查找,如果其中任何⼀个被发现, ACL 将匹配

  • (-m beg) :在提取的字符串⾸部中查找,如果其中任何⼀个被发现, ACL 将匹配

  • (-m end) :在提取的字符串尾部中查找,如果其中任何⼀个匹配,则 ACL 进行匹配

  • (-m dir) :提取用斜线 / 分隔的字符串,如果其中任何⼀个匹配,则 ACL 进行匹配

  • (-m dom) :提取用点的 . 分隔字符串,如果其中任何⼀个匹配,则 ACL 进行匹配

<value> :条件目标

  • 布尔值:比如, false,true

  • 整数或整数范围,比如用于匹配端口范围: 1024~32768

  • IP 地址或 IP 范围: 192.168.0.1 ~ 192.168.0.1/24

  • 字符串:比如匹配 URL 路径, /static、/images

1.3 ACL基于路径匹配示例

1、 path_beg <string> :用于测试请求的 URL 是否以 <string> 指定的字符串开头。

# 匹配URL以/static、/images、/javascript开头的
acl url_static path_beg -i /static /images /javascript

2、 path_end <string> :用于测试请求的 URL 是否以 <string> 指定的字符串结尾。

# 示例:下⾯的例子用户测试URL是否以jpg、gif、png、css或js结尾。
acl url_static path_end -i .jpg .gif .png .css .js

1.4 ACL基于域名匹配示例

1、 hdr_beg 用于测试请求报文指定的header字段,开头部分是否符合给定的字符串模式。

# 示例:测试请求的host开头是否为,img、video、static
acl host_img hdr_beg(host) -i img.
acl host_video hdr_beg(host) -i video.
acl host_static hdr_beg(host) -i static.

2、 hdr_end 用于测试请求报文指定的Header字段,结尾部分是否符合给定的字符串模式。

# 示例:测试请求的host的结尾是否为,.com .net .cn
acl host_com hdr_end(host) -i .com
acl host_net hdr_end(host) -i .net
acl host_cn hdr_end(host) -i .cn

3、 hdr_dom(host) 用于匹配请求的 host 头部字段是否包含指定的域名。

# 示例:测试请求的host是否为 www.ops.com
acl my_www_host hdr_dom(host) -i www.ops.net

4、 hdr 用于匹配指定的请求Header字段,是否包含特定的字符串。

# 示例:请求的User-Agent是否为Chrome
acl my_chhrome hdr(User-Agent) -m sub -i Chrome

2、ACL逻辑关系

在配置 HAProxy 时,可能会遇到需要基于“多个条件”来控制流量的情况。为了灵活地实现这些条件,HAProxy 允许组合使用多个 ACL。这些 ACL 之间可以通过逻辑运算符 and、or 和 not 来组合。

2.1 与(AND)关系示例

当希望⼀个动作只在多个条件“同时满足”时才执行时,可以使用逻辑“与”。默认情况下多个 ACL 它们之间就是逻辑“与”的关系。

示例:只有当 acl1 和 acl2 同时满足时,相关的操作才会执行。

  • 1、定义了两个 ACL:acl1 检查请求的 host 是否为 www.ops.com;acl2检查请求的路径是否以 /admin 开头。

  • 2、当这两个条件都满足时(即请求来自 www.ops.net 且以 /admin 开头),流量才会被发送到 backend_admin。

acl acl1 hdr_dom(host) -i www.ops.net
acl acl2 path_beg -i /admin
use_backend backend_admin if acl1 acl2

2.2 或(OR)关系示例

有时候希望当多个条件中的任意⼀个满足时就执行某个动作。在这种情况下可以使用逻辑“或”。使用运算符 or 或 || 来表示。

示例,只要 acl1 或 acl2 中的任意⼀个满足,相关的操作就会执行。

  • 1、定义了两个 ACL:acl1 检查请求的 host 是否为 www.ops.net;acl2 检查请求的 host 是否为 api.ops.net。

  • 2、在 use_backend 指令中,如果请求的 host 是 www.ops.net 或者api.ops.com 中的任意⼀个,流量将被发送到 webservers。

acl acl1 hdr_dom(host) -i www.ops.net
acl acl2 hdr_dom(host) -i api.ops.net
use_backend webservers if acl1 || acl2

2.3 非(NOT)关系示例

如果想要在某个条件“不满足”时执行动作,可以使用逻辑“非”。在 ACL 前⾯加上 ! 代表取反。

示例,如果 acl1 不满足,相关的操作才会执行。

  • 1、定义了⼀个 ACL:acl1 检查请求的 host 是否为 www.ops.net

  • 2、如果请求不是来自 www.ops.net,流量将被发送到 backend_other

acl acl1 hdr_dom(host) -i www.ops.net
use_backend backend_other if ! acl1

3、ACL实现访问控制

3.1 基于HTTP访问控制

配置七层的请求访问控制;只能用在 mode http 中;

http-request { allow | deny } [ { if | unless } <condition> ]

示例:仅允许 10.0.0.1 访问 haproxy 的 stats

listen haproxy-stats
   bind *:9999
   stats enable
   stats hide-version
   stats uri /haproxy-stats
   stats realm "HAProxy statistics"
   stats auth admin:123456
   stats admin if TRUE
 
   # 除 10.0.0.244 以外的用户全部拒绝
   acl all_ip src 10.0.0.244
   http-request deny if ! all_ip
 
   # 拒绝使用curl
   acl method_curl hdr(User-Agent) -m sub -i curl
   http-request deny if method_curl

[root@rocky9 ~]# curl http://10.0.0.9:9999
<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>

3.2 基于TCP访问控制

配置四层的请求访问控制;只能用在 mode tcp 中;

tcp-request connection {accept|reject} [{if | unless} <condition>]

示例:拒绝来源 tcp 应用,比如 SSH

listen ssh
   bind *:2222
   mode tcp
   balance roundrobin
   server web01 10.0.0.19:22 check
   server web02 10.0.0.29:22 check
 
   acl invalid_src src 10.0.0.49 #定义acl匹配规则
   tcp-request connection reject if invalid_src #在四层拒绝满足名为invalid_src的acl匹配规则

4、ACL案例-基于域名调度

根据用户请求不同的域名,调度到不同的后端集群。

  • 用户请求 www.ops.net 调度到 172.16.1.7:80,172.16.1.8:80

  • 用户请求 m.ops.net 调度到 172.16.1.7:8080,172.16.1.8:8080

4.1 配置后端节点

# www站点定义(所有web节点配置⼀致)
[root@web01 ~]# vi /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
   
   location / {
     index index.html;
   }
}

[root@web01 ~]# echo "www.ops.com-->web1" > /opt/index.html
[root@web01 ~]# echo "www.ops.com-->web2" > /opt/index.html

# m站点定义所有web节点配置⼀致)
[root@web01 ~]# vi /etc/nginx/conf.d/m.ops.net.conf
server {
   listen 8080;
   server_name m.ops.net;
   root /mnt;
 
   location / {
     index index.html;
   }
}

[root@web01 ~]# echo "m.ops.net-->web1" > /mnt/index.html
[root@web01 ~]# echo "m.ops.net-->web2" > /mnt/index.html

4.2 配置Haproxy

[root@lb01 ~]# cat cat/etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend main
  bind *:80
  mode http

  # 定义ACL规则(请求www,则被my_www规则匹配,请求m,则被my_m⻤规则匹配)
  acl my_www hdr(host) -i www.ops.net
  acl my_m hdr(host) -i m.ops.net
 
  # 调用ACL规则(如果来源www则调度至www_site集群处理,如果请求来源m,则调度至m_site集群处理)
  use_backend www_site if my_www
  use_backend m_site if my_m

  # 如果ACL都没有匹配成功,则默认调度www站点
  use_backend my_www

#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend www_site
   balance roundrobin
   server web1 172.16.1.7:80 check port 80
   server web2 172.16.1.8:80 check port 80

backend m_site
   balance roundrobin
   server web1 172.16.1.7:8080 check port 8080
   server web2 172.16.1.8:8080 check port 8080

backend my_www
   balance roundrobin
   server web1 172.16.1.7:88 check

4.3 客户端测试

Client 客户端测试访问,请求 www 站点测试

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.5:80
www.ops.com-->web1

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.5:80
www.ops.com-->web2

Client 客户端测试访问,请求 m 站点测试

[root@client ~]# curl -HHost:m.ops.net http://10.0.0.5:80
www.ops.com-->web1

[root@client ~]# curl -HHost:m.ops.net http://10.0.0.5:80
www.ops.com-->web2

5、ACL案例-基于设备调度

根据用户请求的 User-Agent ,调度到不同的后端集群。

  • 用户通过 Chrome 调度到 172.16.1.7:80

  • 用户请求 Firefox 调度到 172.16.1.8:80

5.1 配置后端节点

# web1:Chrome站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
   
   location / {
     index index.html;
   }
}

[root@web01 ~]# echo "Chrome-->web1" > /opt/index.html

# web2:Firefox站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
   
 location / {
     index index.html;
   }
}

[root@web01 ~]# echo "Firefox-->web2" > /opt/index.html

5.2 配置Haproxy

编辑 Haproxy 配置文件 (注释之前的ACL规则)

[root@lb01 ~]# cat cat/etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend main
  bind *:80
  mode http
  
  # 定义ACL规则
     # 浏览器为Chrome,则被my_chrome规则匹配
     # 浏览器为Firefox,则被my_firefox规则匹配
  acl my_chrome hdr(User-Agent) -m sub -i Chrome
  acl my_firefox hdr(User-Agent) -m sub -i Firefox
  
  # 调用ACL规则
     # 如果来源设备为Chrome则调度至www_site集群处理
     # 如果来源设备为Firefox则调度至m_site集群处理
  use_backend chrome_site if my_chrome
  use_backend firefox_site if my_firefox

  # 如果ACL都没有匹配成功,则默认调度chrome_site站点
  use_backend chrome_site

#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend chrome_site
   balance roundrobin
   server web1 172.16.1.7:80 check port 80

backend firefox_site
   balance roundrobin
   server web2 172.16.1.8:80 check port 80

5.3 客户端测试

Client 客户端测试访问,使用 Chrome 和 Firefox 浏览器进行测试

[root@client ~]# curl -A "Chrome" -HHost:www.ops.net http://10.0.0.5:80
Chrome-->web1

[root@client ~]# curl -A "Firefox" -HHost:www.ops.net http://10.0.0.5:80
Firefox-->web2

6、ACL案例-基于路径调度

根据用户请求的 URL ,调度到不同的后端集群。

  • 用户通过 /static 调度到 172.16.1.7:80

  • 用户请求 /user 调度到 172.16.1.8:80

6.1 配置后端节点

# web1:/static站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
    root /opt;
    
   location / {
     index index.html;
   }
}

[root@web01 ~]# mkdir /opt/static
[root@web01 ~]# echo "static-web1" > /opt/static/index.html

# web2:/user站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
   
   location / {
     index index.html;
   }
}

[root@web02 conf.d]# mkdir /opt/user
[root@web02 conf.d]# echo "user-web2" > /opt/user/index.html

6.2 配置Haproxy

[root@lb01 ~]# cat cat/etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend main
   bind *:80
   mode http
 
  # 定义ACL规则
     # 请求url为/static,则被path_static规则匹配
     # 请求url为/user,则被path_user规则匹配
   acl path_static path_beg -i /static /images
   acl path_user path_beg -i /user

  # 调用ACL规则
     # 如果来源URL为/static则调度至static_site集群处理
     # 如果来源URL为/user则调度至user_site集群处理
   use_backend static_site if path_static
   use_backend user_site if path_user

  # 如果ACL都没有匹配成功,则默认调度user_site站点
  use_backend user_site
  
#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend static_site
   balance roundrobin
   server web1 172.16.1.7:80 check port 80

backend user_site
   balance roundrobin
   server web2 172.16.1.8:80 check port 80

6.3 客户端访问

Client 客户端测试访问,请求不同的 URL 测试调度效果

[root@client ~]# curl -HHost:www.ops.com http://10.0.0.5:80/static/
static-web1

[root@client ~]# curl -HHost:www.ops.com http://10.0.0.5:80/user/
user-web2

7、ACL案例-基于后缀调度

根据用户请求的后缀,调度到不同的后端集群。

  • 用户通过 .txt 调度到 172.16.1.7:80

  • 用户请求 .pdf 调度到 172.16.1.8:80

7.1 配置后端节点

# web1:.txt站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
 
   location / {
     index index.html;
   }
}
[root@web01 ~]# echo ".txt--web1" > /opt/web.txt

# web2:.pdf站点定义
[root@web01 ~]# cat /etc/nginx/conf.d/www.ops.net.conf
server {
   listen 80;
   server_name www.ops.net;
   root /opt;
 
   location / {
     index index.html;
   }
}

[root@web02 conf.d]# echo ".pdf--web2" > /opt/web.pdf

7.2 配置Haproxy

[root@lb01 ~]# cat cat/etc/haproxy/haproxy.cfg
#----------------------------------------------------------------
# main frontend which proxys to the backends
#----------------------------------------------------------------
frontend main
   bind *:80
   mode http
 
  # 定义ACL规则
     # 请求文件为.txt,则被path_txt规则匹配
     # 请求文件为.pdf,则被path_pdf规则匹配
   acl path_txt path_end -i .txt
   acl path_pdf path_end -i .pdf

  # 调用ACL规则
     # 如果请求文件为.txt 则调度至txt_site集群处理
     # 如果来源文件为.pdf 则调度至pdf_site集群处理
   use_backend txt_site if path_txt
   use_backend pdf_site if path_pdf

  # 如果ACL都没有匹配成功,则默认调度txt_site站点
  use_backend txt_site

#----------------------------------------------------------------
# round robin balancing between the various backends
#----------------------------------------------------------------
backend txt_site
   balance roundrobin
   server web1 172.16.1.7:80 check port 80

backend pdf_site
   balance roundrobin
   server web2 172.16.1.8:80 check port 80

7.3 客户端访问

  • Client 客户端测试访问,请求不同类型文件测试调度效果

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.5:80/web.txt
.txt--web1

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.5:80/web.pdf
.pdf--web2

8、ACL案例-实现动静分离

动静分离示例:
global
   log 127.0.0.1 local2
   
   chroot /var/lib/haproxy
   pidfile /var/run/haproxy.pid
   maxconn 4000
   user haproxy
   group haproxy
   daemon
   
   # turn on stats unix socket
   stats socket /var/lib/haproxy/stats
   
defaults
   mode http
   log global
   option httplog
   option dontlognull
   option http-server-close
   option forwardfor except 127.0.0.0/8
   option redispatch
   retries 3
   timeout http-request 10s
   timeout queue 1m
   timeout connect 10s
   timeout client 1m
   timeout server 1m
   timeout http-keep-alive 10s
   timeout check 10s
   maxconn 30000
 
listen stats
   mode http
   bind 0.0.0.0:1080
   stats enable
   stats hide-version
   stats uri /haproxyadmin?stats
   stats realm Haproxy\ Statistics
   stats auth admin:admin
   stats admin if TRUE
 
frontend http-in
   bind *:80
   mode http
   log global
   capture request header Host len 20
   capture request header Referer len 60
   acl url_static path_beg -i /static /images /javascript /stylesheets
   acl url_static path_end -i .jpg .jpeg .gif .png .css .js
   use_backend static_servers if url_static
   default_backend dynamic_servers

backend static_servers
   balance roundrobin
   server imgsrv1 172.16.200.7:80 check maxconn 6000
   server imgsrv2 172.16.200.8:80 check maxconn 6000

backend dynamic_servers
   cookie srv insert nocache
   balance roundrobin
   server websrv1 172.16.200.7:80 check maxconn 1000 cookie websrv1
   server websrv2 172.16.200.8:80 check maxconn 1000 cookie websrv2
   server websrv3 172.16.200.9:80 check maxconn 1000 cookie websrv3

05 Haproxy场景实践

1.配置MySQL负载均衡

用户请求 3366 端口,调度到 172.16.1.7:3306、172.16.1.8:3306

1.1 配置MySQL

1.后端节点 MySQL 配置如下

[root@web01 ~]# yum install mysql mysql-server -y

[root@web01 ~]# systemctl start mysqld

# 用户all、密码xx
mysql> create user proxy@'%' identified by 'proxy.net';
mysql> grant all privileges on *.* to proxy@'%';

1.2 配置Haproxy

  • 配置 Haproxy 为 TCP 模式,代理 MySQL

[root@lb01 ~]# cat /etc/haproxy/conf.d/mysql.cfg
frontend mysql
  bind *:3366
  mode tcp
  use_backend mysql_servers

backend mysql_servers
   mode tcp
   balance roundrobin
   server node1 172.16.1.7:3306 check port 3306
   server node2 172.16.1.8:3306 check port 3306

1.3 客户端测试

Client 客户端测试

[root@client ~]# yum install mysql -y
# 第⼀次登陆测试

[root@client ~]# mysql -h10.0.0.200 -uall -p123456 -P3366
mysql> select @@hostname;
+------------+
| @@hostname |
+------------+
| web01 |
+------------+
1 row in set (0.01 sec)

# 第⼆次登陆测试
[root@client ~]# mysql -h10.0.0.200 -uall -p123456 -P3366
mysql> select @@hostname;
+------------+
| @@hostname |
+------------+
| web02 |
+------------+
1 row in set (0.01 sec)

2.配置HTTP负载均衡

用户请求 www.ops.net 调度到 172.16.1.7:80,172.16.1.8:80

2.1 配置web节点

2.2 配置Haproxy

[root@lb01 ~]# cat /etc/haproxy/haproxy.cfg
frontend www
  bind *:80
  mode http
 
  acl my_www hdr(host) -i www.ops.net
  use_backend www_site if my_www

backend www_site
   balance roundrobin
   server web1 172.16.1.7:80 check port 80
   server web2 172.16.1.8:80 check port 80

2.3 客户端测试

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.200:80/
RealServer Node1

[root@client ~]# curl -HHost:www.ops.net http://10.0.0.200:80/
RealServer Node2

3.配置HTTPS负载均衡

3.1 签发证书

# 创建自签证书文件
[root@lb01 ~]# cd /opt
[root@lb01 opt]# openssl genrsa -idea -out server.key 2048
[root@lb01 opt]# openssl req -days 36500 -x509 -sha256 \
-nodes -newkey rsa:2048 -keyout server.key -out server.crt

# 合并证书
[root@lb01 opt]# cat server.crt > ha_ssl.pem
[root@lb01 opt]# cat server.key >> ha_ssl.pem

3.2 配置单域名https

📎21214937_www.zhouhaoit.com_nginx.zip

[root@proxy01 ~]# cat /etc/haproxy/haproxy.cfg
frontend blog
   bind *:80
   bind *:443 ssl crt /ssl/blog_key.pem 	# pem+key的合并结果文件
   mode http
   
   # 当请求域名为blog.ops.net 则重定向到https协议,并调度到后端blog_cluster集群处理
   acl blog_domain hdr_dom(host) -i blog.ops.net
   redirect scheme https code 301 if !{ ssl_fc } blog_domain
   use_backend blog_cluster if blog_domain
   
   # 域名匹配zh.ops.net
   acl zh_domain hdr_dom(host) -i zh.ops.net
   use_backend zh_cluster if zh_domain
   
###################################################
backend blog_cluster
   balance roundrobin
   server blog01 10.0.0.100:8081 check
   server blog02 10.0.0.100:8082 check
   
backend zh_cluster
   balance roundrobin
   server web3 172.16.1.7:80 check port 80
   server web4 172.16.1.8:80 check port 80

3.3 配置多域名单证书https

[root@proxy01 ~]# cat /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend blog
   bind *:80
   bind *:443 ssl crt /ssl/blog_key.pem
   mode http
 
   # 域名匹配blog.ops.net
   acl blog_domain hdr_dom(host) -i blog.ops.net
   use_backend zh_cluster if blog_domain
 
   # 域名匹配zh.ops.net
   acl zh_domain hdr_dom(host) -i zh.ops.net
   use_backend zh_cluster if zh_domain
 
   # 开启全站https
   redirect scheme https if !{ ssl_fc }
 
###################################################
backend blog_cluster
   balance roundrobin
   server web1 172.16.1.7:80 check port 80
   server web2 172.16.1.8:80 check port 80

backend zh_cluster
   balance roundrobin
   server web3 172.16.1.7:80 check port 80
   server web4 172.16.1.8:80 check port 80

3.4 配置多域名多证书https

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend blog
   bind *:80
   # 如果每个域名证书都是独立,就都引用,haproxy会自行识别并匹配
   # 示例:bind :443 ssl crt $filepath crt $file2path crt $file3path
   bind *:443 ssl crt /ssl/blog_key.pem crt /ssl/zh_key.pem
   mode http
 
   # 域名匹配blog.ops.net
   acl blog_domain hdr_dom(host) -i blog.ops.net
   use_backend zh_cluster if blog_domain
 
   # 域名匹配zh.ops.net
   acl zh_domain hdr_dom(host) -i zh.ops.net
   use_backend zh_cluster if zh_domain
   
   # 开启全站https
   redirect scheme https if !{ ssl_fc }
 
###################################################
backend blog_cluster
   balance roundrobin
   server web1 172.16.1.7:80 check port 80
   server web2 172.16.1.8:80 check port 80
 
backend zh_cluster
   balance roundrobin
   server web3 172.16.1.7:80 check port 80
   server web4 172.16.1.8:80 check port 80

3.5 配置多域名部分uri走https

[root@proxy01 ~]# cat /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend blog
   bind *:80
   bind *:443 ssl crt /ssl/blog_key.pem crt /ssl/zh_key.pem
   mode http
   
 # 开启全站https
 # redirect scheme https if !{ ssl_fc }
 # blog.ops.net域名走http,碰到/login /pay走https
 acl blog_domain hdr_dom(host) -i blog.ops.net
 use_backend blog_cluster if blog_domain
 acl blog_ssl_path path_beg -i /login /pay
 redirect scheme https code 301 if !{ ssl_fc } blog_domain blog_ssl_path
 redirect scheme http code 301 if { ssl_fc } blog_domain !blog_ssl_path
 
 # 域名匹配zh.ops.net,所有uri都走https
 acl zh_domain hdr_dom(host) -i zh.ops.net
 redirect scheme https code 301 if !{ ssl_fc } zh_domain
 use_backend zh_cluster if zh_domain
###################################################
backend blog_cluster
 balance roundrobin
 server web1 172.16.1.7:80 check port 80
 server web2 172.16.1.8:80 check port 80
backend zh_cluster
 balance roundrobin
 server web3 172.16.1.7:80 check port 80
 server web4 172.16.1.8:80 check port 80

3.6 客户端测试

[root@client ~]# curl -k -HHost:www.ops.net https://10.0.0.200
RealServer Node1

[root@client ~]# curl -k -HHost:www.ops.net https://10.0.0.200
RealServer Node2

4.配置高可用负载均衡

4.1 环境准备

1.基于 Keepalived 实现 Haproxy 高可用。

状态

IP

角色

节点1

10.0.0.5

Master

节点2

10.0.0.6

Backup

VIP

10.0.0.10

2.在 Master 与 backup 上分别安装 keepalived

[root@lb01 ~]# yum install keepalived -y
[root@lb02 ~]# yum install keepalived -y

4.2 配置Master

  • 配置 Master 节点

[root@lb01 ~]# cat /etc/keepalived/keepalived.conf
global_defs { 
   router_id lb01 
}

vrrp_instance VI_1 {
   state MASTER
   interface eth0
   virtual_router_id 50
   priority 150
   advert_int 1
   authentication {
     auth_type PASS
     auth_pass 1111
}
   virtual_ipaddress {
       10.0.0.10
   }
}

4.3 配置Backup

  • 配置 Backup 节点

[root@lb02 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
   router_id lb02
}

vrrp_instance VI_1 {
   state BACKUP
   interface eth0
   virtual_router_id 50
   priority 100
   advert_int 1
   authentication {
       auth_type PASS
       auth_pass 1111
 }
 
 virtual_ipaddress {
     10.0.0.3
   }
}

4.4 高可用测试验证

  • 启动 Master 与 Backup 节点的 keepalived

#lb01
[root@lb01 ~]# systemctl enable keepalived
[root@lb01 ~]# systemctl start keepalived

#lb02
[root@lb02 ~]# systemctl enable keepalived
[root@lb02 ~]# systemctl start keepalived