HAProxy 负载均衡实战:从环境搭建到七层/四层/HTTPS 全解析

目录

  • 一、HAProxy 入门:它到底是什么
  • 二、实验环境与网络拓扑
  • 三、配置文件结构详解
  • 四、配置七层负载均衡
  • 五、远程日志采集
  • 六、socat 热更新:不停机动态调整
  • 七、状态页(Web 监控面板)
  • 八、客户端真实 IP 透传
  • 九、负载均衡算法详解
  • 十、基于 Cookie 的会话保持
  • 十一、自定义错误页面
  • 十二、ACL 访问控制
  • 十三、四层负载实战:MariaDB 数据库集群
  • 十四、全站 HTTPS 加密

一、HAProxy 入门:它到底是什么

1.1 什么是负载均衡

先用一个比喻理解负载均衡:

一家餐厅生意火爆,门口排长队,老板于是招了 3 个服务员。可客人进门只认"这家店",不会认服务员。这时候店里需要一位大堂经理:客人进门,经理按"谁空就派给谁"的原则,把客人分给 3 个服务员中的一个。

这里的映射关系是:

餐厅角色 负载均衡术语 说明
店门口招牌 VIP / 对外地址 客户端只认这个地址
大堂经理 负载均衡器(LB) 负责接收所有请求并分发
3 个服务员 Real Server(后端服务器) 真正干活、响应请求的机器

负载均衡的核心价值有四点:

  1. 提升吞吐量 ------ 多台机器同时干活,总处理能力翻倍;
  2. 高可用 ------ 某台后端挂了,自动把请求转发给存活的机器,业务不中断;
  3. 横向扩展 ------ 流量再涨,加机器即可,前端地址不用变;
  4. 统一入口 ------ 客户端只面对一个地址,架构对使用者透明。

1.2 HAProxy 是什么

HAProxy(High Availability Proxy) 是一个免费、开源、基于 C 语言编写TCP / HTTP 反向代理与负载均衡软件,单机可以支撑十万级并发连接,是很多大厂(GitHub、Stack Overflow 等)都在用的老牌工具。

它的特点:

  • 工作在用户态(普通进程),不像 LVS 需要改内核;
  • 支持七层(HTTP)和四层(TCP)两种模式,既能负载 Web,也能负载数据库、Redis;
  • 自带健康检查,能自动摘掉坏掉的后端;
  • 配置灵活,支持 ACL 访问控制、SSL 卸载、会话保持等高级特性;
  • 支持热更新(socat 在线调整权重/上下线),不需要重启进程。

1.3 HAProxy 与 LVS、Nginx 的对比

对比项 LVS HAProxy Nginx
工作层级 四层(内核) 四层 + 七层 七层为主
性能 最高(内核转发)
健康检查 较简单 非常丰富 较简单
配置灵活性 一般 高(ACL 等)
会话保持 较弱 强(Cookie 等) 一般
适用场景 超大流量、四层转发 七层应用分发、四层数据库/Redis Web 服务、静态资源、反向代理

1.4 七层负载 vs 四层负载

四层(L4 / TCP) 七层(L7 / HTTP)
看的东西 只认 IP + 端口 能识别 HTTP 协议内容(URL、Header、Cookie、域名)
转发方式 直接把 TCP 流量转给后端 读懂 HTTP 请求再决定发给谁
配置关键字 mode tcp mode http
典型场景 MySQL、Redis、游戏服务 Web 网站、API 网关、按域名/URL 分流
优点 性能高、协议无关 智能,能按内容路由、做 SSL 卸载
缺点 看不到业务内容 只能处理 HTTP 类协议

二、实验环境与网络拓扑

2.1 主机规划

本次实验用 VMware Workstation 搭了一台 HAProxy + 两台后端(RHEL 系虚拟机),后端先装好 Nginx/Apache 提供一个测试页面。

功能 IP 说明
客户端 本机 Windows(192.168.17.1) 用浏览器 / curl 测试
HAProxy 调度器 eth0:192.168.17.100(对外) eth1:193.168.0.100(对内) 双网卡,前接客户端,后接后端
RS1(后端 1) eth0:193.168.0.10 网关指向 193.168.0.100
RS2(后端 2) eth0:193.168.0.20 网关指向 193.168.0.100

2.2 拓扑图

复制代码
                客户端(Windows 本机)
                  |
                  |    192.168.17.0/24 网段(NAT 模式,模拟外网/客户端)
                  |
        +----------------------------+
        |        HAProxy 调度器       |
        | eth0 : 192.168.17.100       |   <- 对外服务口,客户端访问这个 IP
        | eth1 : 193.168.0.100        |   <- 对内通信口,连接后端(也是后端的网关)
        +-------------+---------------+
                      |
                      |    193.168.0.0/24 网段(仅主机模式,内网)
        +-------------+---------------+
        |                             |
   +----+---------+            +------+--------+
   |     RS1      |            |      RS2      |
   |  .10         |            |  .20          |
   | 网关:.100    |            | 网关:.100     |
   +--------------+            +---------------+

注意一个关键点:RS1 / RS2 的默认网关都指向 193.168.0.100(HAProxy 的 eth1)。这样后端回包会原路返回给 HAProxy,由 HAProxy 转发给客户端------这和 LVS 的 NAT 模式思路一致。

2.3 HAProxy 双网卡配置

先给 HAProxy 的 eth1(内网口)新建一个网络连接配置,再把 eth0(外网口)改为静态 IP:

bash 复制代码
[root@localhost li]#  nmcli con add type ethernet con-name eth1 ifname eth1 ipv4.method manual ipv4.addresses 193.168.0.100/24
连接 "eth1" (c5b0f679-781b-4c3a-9f4b-746c923bcf0b) 已成功添加。
[root@localhost li]# nmcli con up eth1
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/4)
[root@localhost li]# nmcli con mod eth0 ipv4.method manual ipv4.addresses 192.168.17.100/24
[root@localhost li]#  nmcli con up eth0
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/5)

逐条解释:

命令 作用
nmcli con add type ethernet con-name eth1 ifname eth1 ... 新建 eth1 的连接配置,con-name 是连接名,ifname 是实际网卡名
ipv4.method manual 关闭 DHCP,改为手动指定 IP
ipv4.addresses 193.168.0.100/24 指定内网 IP 和掩码
nmcli con up eth1 激活该连接,让配置立即生效
nmcli con mod eth0 ... 修改 eth0 现有连接

2.4 RS1 配置

bash 复制代码
nmcli con mod eth0 ipv4.method manual ipv4.addresses 193.168.0.10/24 ipv4.gateway 193.168.0.100
[root@localhost li]# nmcli con up eth0
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/3)

2.5 RS2 配置

bash 复制代码
[root@localhost li]# nmcli con mod eth0 ipv4.method manual ipv4.addresses 193.168.0.20/24 ipv4.gateway 193.168.0.100
[root@localhost li]# nmcli con up eth0
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/3)

2.6 安装并启动 HAProxy

在 HAProxy 调度器上安装并开机自启:

bash 复制代码
[root@localhost ~]# dnf install haproxy -y
[root@localhost ~]# systemctl enable --now haproxy.service
命令 作用
dnf install haproxy -y 安装 HAProxy 软件包(RHEL 8+ 用 dnf,旧版用 yum)
systemctl enable --now haproxy.service enable 设置开机自启,--now 表示立即启动服务

三、配置文件结构详解

HAProxy 的主配置文件是 /etc/haproxy/haproxy.cfg 。整个配置文件由几个配置段组成:

复制代码
haproxy.cfg 整体结构
┌─────────────────────────────────────────────┐
│  global       全局配置:进程级的运行参数       │
│  ─────────────────────────────────────────── │
│  defaults     默认配置:给所有实例套用的默认值   │
│  ─────────────────────────────────────────── │
│  frontend     前端:监听端口,接收请求,定义规则 │
│  backend      后端:定义一组真实服务器          │
│  ─────────────────────────────────────────── │
│  listen       监听:前端+后端写在一起的简写方式 │
│  (一个 listen 同时扮演 frontend + backend)   │
└─────────────────────────────────────────────┘

3.1 global 段详解

作用: 设置 HAProxy 进程本身的运行参数(用户、文件、并发、CPU 绑定等),是整个配置最先被读取的一段。

ini 复制代码
global
    log         193.168.0.10 local2			#定义全局的syslog服务;日志服务器需要开启udp协议,最多可以定义两个
    chroot      /var/lib/haproxy			#锁定运行目录
    pidfile     /var/run/haproxy.pid		#指定pid文件
    maxconn     4000						#指定连接最大数
    user        haproxy						#指定haproxy的运行用户
    group       haproxy						#指定haproxy的运行组
    daemon									#指定haproxy以守护进程方式运行
    stats socket /var/lib/haproxy/stats		#指定haproxy套接字文件
    nbproc		2							#指定haproxy的work进程数量,默认1个
    cpu-map		1 0							#指定第一个work绑定第一个cpu核心
    cpu-map		2 1							#指定第二个work绑定第二个cpu核心
    nbthread 	2							#指定haproxy的线程数量,默认每个进程一个线程,此参数与nbproc互斥
    maxsslconn	10000						#每个haproxy进程ssl最大连接数,用于haproxy配置了证书场景下
    maxsslrate	1000						#每秒SSL握手速率
    maxconnrate	1000						#全协议每秒新建TCP连接速率
    maxconn		10000						#进程所有TCP总并发连接上限
参数 含义 原理补充 / 为什么需要
log 193.168.0.10 local2 把日志通过 UDP 发送到日志服务器(193.168.0.10)的 local2 设施 HAProxy 本身不写日志文件,靠 syslog 协议外发。local2 是 syslog 的"设施"分类,日志服务器据此区分条目来源
chroot /var/lib/haproxy 锁定运行目录:进程只能访问该目录下的文件 安全机制
pidfile 记录进程 PID 的文件路径 方便管理进程、热更新时定位
user / group haproxy 以普通用户 haproxy 运行 安全机制:不 root 运行,权限最小化
daemon 后台守护进程方式运行 不占用终端,像其它系统服务一样常驻
stats socket 监听一个 Unix 套接字,供管理工具(socat)连入 没有它 socat 没法连接
nbproc 2 + cpu-map 开 2 个工作进程,并分别绑定到 CPU 0 和 CPU 1 进程 → CPU 核绑定,减少进程切换,提升性能;适合多核 CPU
nbthread 2 每个进程开 2 个线程 nbproc 互斥,二选一使用
maxsslconn SSL 最大连接数 只有在 HAProxy 配置了证书时才需要
maxconnrate 每秒新建 TCP 连接速率上限 防止突发流量打爆进程

3.2 defaults 段详解

作用: 给所有 frontend / backend / listen 提供一套默认值 ,后面每个实例没写的参数都会继承这里的值。省得每个实例重复写一遍。

ini 复制代码
defaults
    mode                    http				#haproxy实例使用的连接协议
    log                     global				#指定日志地址和记录日志条目的syslog/rsyslog日志设备
    option                  httplog				#日志记录选项,httplog表示与http会话相关的各种属性
    option                  dontlognull			#dontlognull表示不记录空会话连接日志
    option http-server-close					#等待客户端完整http请求时间,此处为10s
    option forwardfor       except 127.0.0.0/8	#透传客户端真实ip至后端
    option                  redispatch			#当server id对应服务器挂掉后,强制定向到其他健康服务器,重新派发
    option http-keep-alive						#开启与客户端的会话保持
    retries                 3					#连接后端服务器失败次数	
    timeout http-request    10s					#等待客户端请求完全接受和处理的最长时间
    timeout queue           1m					#设置删除连接和客户端收到503或服务不可用等提示信息前的等待时间
    timeout connect         10s					#设置等待服务器连接成功的时间
    timeout client          1m					#设置允许客户端处于非活动状态,既不发数据也不收数据的时间
    timeout server          1m					#设置服务器超时时间,即允许服务器处于既不接也不发数据的非活动时间
    timeout http-keep-alive 10s					#session会话保持超时时间,此时间段内会转发到相同的后端服务器
    timeout check           10s					#指定后端服务器的健康检查超时时间
    maxconn                 3000				#最大承受的并发连接
参数 含义 一句话记住
mode http 当前实例工作在七层 HTTP 模式 想处理数据库就改成 mode tcp
option httplog 日志记录 HTTP 会话属性(方法、URL、状态码等) 排错必须开,否则日志没内容
option dontlognull 不记录"空连接"(连上啥也没干就断开)的日志 防止恶意扫描刷爆日志
option http-server-close 客户端和 HAProxy 之间保持长连接,HAProxy 与后端之间用完就关 节省后端连接资源
option forwardfor 在发给后端的请求头里追加 X-Forwarded-For 字段,透传客户端真实 IP 第 8 章主角,except 127.0.0.0/8 表示本机地址不追加
option redispatch 后端挂了时,把这次请求重新分发给其它健康服务器 保证可用性
retries 3 连接后端失败最多重试 3 次 防止单次抖动误伤
timeout connect 等待与后端建立 TCP 连接的最长时间 后端没响应就放弃,别傻等
timeout client / server 客户端 / 后端无活动多久视为超时 闲置连接自动清理
timeout http-request 等待客户端把整个 HTTP 请求发完的时间 防止慢速攻击挂起连接
maxconn 本实例最大并发连接数 超出的请求排队或返回 503

3.3 server 指令详解

server 是 backend / listen 里声明一台后端服务器的关键字。它的完整语法是:

ini 复制代码
server <名称> <IP:端口> [参数...]

后面的方括号参数很常用,重点讲 健康检查 相关的一组:

ini 复制代码
check				#对指定real进行健康检查,如果不加此设置,默认不进行健康检查
addr <IP>			#可指定的健康状态检测ip,可以是专门的数据网段,减少业务网络流量
port <num>			#指定的健康状态检测端口
inter <num>			#健康状态检查间隔时间,默认2000ms
fall <num>			#后端服务器从线上转为线下的检查的连续失效次数,默认为3
rise <num>			#后端服务器从线下恢复线上的检查的连续有效次数,默认为2
weight <weight>		#默认为1,最大值为256,0(状态为蓝色)表示不参与负载均衡,但仍接受持久连接
backup				#将后端服务器标记为备份状态,只有所有非备份主机down时提供服务
disable				#将后端服务器标记为不可用状态,即维护状态,除了持久模式
redir prefix http://xxx.xxx.xxx/		#将临时请求重定向到其他url,只适用于http模式
maxconn <maxconn>						
参数 含义 补充
check 开启健康检查 ⚠️ 不写 check 的话 HAProxy 默认永远认为这台机器是好的,从不主动探测
inter <时间> 每隔多久检查一次 默认 2000ms;写 inter 2s 就是每 2 秒探测一次
fall <次数> 连续失败多少次判定为下线 默认 3;防止一次网络抖动就误判
rise <次数> 连续成功多少次判定为上线 默认 2;防止后端刚启动还没就绪就被放流量
weight <权重> 负载权重 默认 1,最大 256;权重越大分到的请求越多
backup 标记为备份服务器 平时不干活,只有所有正常服务器全挂时才顶上
disable 标记为维护状态 手动摘除,常用于排障
maxconn 限制该服务器最大连接数 防止某台弱机器被压垮

健康检查的完整逻辑是一个状态机UP ↔ DOWN,中间靠 inter / fall / rise 三个参数控制跳转速度。

复制代码
UP 状态 ──连续失败 fall 次──▶ DOWN 状态
DOWN 状态 ──连续成功 rise 次──▶ UP 状态

下面看一个完整的 server 示例:

ini 复制代码
listen webcluster
        bind *:80
        mode http
        balance roundrobin
        server web1 193.168.0.10:80 weight 3 check inter 2s fall 3 rise 3	#健康检测间隔2s一次,连续失败3次视为下线,连续成功2次视为上线,权重为3
        server web2 193.168.0.20:80 weight 1 check inter 2s fall 3 rise 3	#健康检测间隔2s一次,连续失败3次视为下线,连续成功2次视为上线,权重为1
含义
listen webcluster 定义一个名为 webcluster 的监听实例
bind *:80 监听所有网卡的 80 端口(* 表示任意 IP)
mode http 七层 HTTP 模式
balance roundrobin 负载算法用加权轮询(第 9 章详解)
server web1 ... weight 3 web1 权重 3,每 2 秒健康检查一次,连续失败 3 次下线、连续成功 3 次上线
server web2 ... weight 1 web2 权重 1,其余同 web1

权重 3 : 1 意味着:大约每 4 个请求,3 个分给 web1,1 个分给 web2

测试效果 (客户端访问,健康检查正常,两台后端均 UP):


四、配置七层负载均衡(HTTP)

七层负载是 HAProxy 最常见的用法。有两种等价写法:frontend + backendlisten

4.1 方式一:frontend + backend

这种写法把"入口"和"服务器列表"拆成两段,适合一个入口对应多组后端(按域名 / URL 分流)的场景。

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
        bind *:80
        mode http
        use_backend web-80

backend web-80
        balance roundrobin
        server web1 193.168.0.10:80 check
        server web2 193.168.0.20:80 check
        
[root@localhost ~]# systemctl restart haproxy.service
[root@localhost ~]# systemctl stop firewalld
含义
frontend webcluster 定义一个前端,名叫 webcluster(名字随便起)
bind *:80 监听所有网卡的 80 端口,接收客户端请求
use_backend web-80 把所有请求转发给名为 web-80 的后端
backend web-80 定义一个后端组,名叫 web-80
balance roundrobin 轮询算法
server web1 / web2 ... check 声明两台后端,check 开启健康检查

这条拓扑里的对应关系:

复制代码
客户端 ──▶ frontend webcluster(:80)──use_backend──▶ backend web-80
                                                           │
                                                           ├──▶ web1 (193.168.0.10:80)
                                                           └──▶ web2 (193.168.0.20:80)

重启服务后,用 netstat 确认 80 端口已被 HAProxy 监听:

bash 复制代码
[root@localhost ~]# netstat -antlupe | grep haproxy
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      0          67253      5425/haproxy        
udp        0      0 0.0.0.0:45331           0.0.0.0:*                           985        67261      5425/haproxy        
udp        0      0 0.0.0.0:55129           0.0.0.0:*                           985        67259      5425/haproxy         

看到 tcp 0.0.0.0:80 ... LISTEN 就说明 HAProxy 已在 80 端口对外服务了。(

4.2 方式二:listen 方式

一个入口只对应一组后端 时,可以直接用 listen 把前端 + 后端写在一起,更简洁:

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg

listen webcluster
        bind *:80
        mode http
        balance roundrobin
        server web1 193.168.0.10:80 check
        server web2 193.168.0.20:80 check
        
[root@localhost ~]# systemctl restart haproxy.service

4.3 两种方式如何选

对比 frontend + backend listen
结构 入口与后端分离 前端后端写在一起
适用场景 一个入口按规则分发到多个后端组(ACL 分流) 一个入口对应一组后端
可读性 复杂场景清晰 简单场景省事
灵活性

4.4 效果验证

客户端(Windows 本机)多次访问 http://192.168.17.100,观察请求是否被轮询分发到两台后端:

frontend + backend 方式的测试结果:

listen 方式的测试结果:


五、远程日志采集

5.1 为什么要做远程日志

HAProxy 的日志默认通过 syslog 协议发送。如果只写在本地,一旦调度器硬盘坏了或系统重装,日志就全没了 。把日志集中采集到一台专门的日志服务器上,既能长期保存,也方便统一分析、安全审计。

架构图:

复制代码
HAProxy ──UDP 514 端口──▶ 日志采集服务器(rsyslog)
  (生产日志)                (统一存储、归档、审计)

5.2 日志采集服务器配置

在日志服务器上,打开 rsyslog 的 UDP 接收模块

bash 复制代码
[root@localhost ~]# vim /etc/rsyslog.conf 

module(load="imudp") # needs to be done just once
input(type="imudp" port="514")


[root@localhost ~]# systemctl restart rsyslog.service
配置行 含义
module(load="imudp") 加载 imudp 模块,让 rsyslog 支持接收 UDP 日志
input(type="imudp" port="514") UDP 514 端口上监听,接收网络传来的日志
systemctl restart rsyslog.service 重启 rsyslog 使配置生效

5.3 HAProxy 端配置

在 HAProxy 的 global 段里,把日志发送目标指向日志服务器:

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg

#    local2.*                       /var/log/haproxy.log
#
log         193.168.0.10 local2

chroot      /var/lib/haproxy
pidfile     /var/run/haproxy.pid
maxconn     4000

[root@localhost ~]# systemctl restart haproxy.service

讲解:

  • log 193.168.0.10 local2 ------ 把日志通过 UDP 发给 193.168.0.10,设施为 local2
  • 配置里被 # 注释掉的那行 local2.* /var/log/haproxy.log本地写文件的写法,如果要日志同时写本地,取消注释即可;
  • chroot / pidfile / maxconn 等是 global 段的其它已有配置,这里一并展示。

5.4 验证

在日志采集服务器上查看收集到的日志:

bash 复制代码
[root@localhost ~]# cat /var/log/remote/193.168.0.100/root.log 
Aug  6 16:38:56 193.168.0.100 root: haproxy udp test
Aug  6 16:44:34 193.168.0.100 root: haproxy udp test 2
[root@localhost ~]# 

能看到日志文件里出现了 193.168.0.100(HAProxy) 发来的记录,说明远程日志链路已经打通。


六、socat 热更新:不停机动态调整

6.1 原理

日常运维中,可能要临时调整权重、把某台后端下线维护、再上线。如果每次都改配置重启 HAProxy,会造成短暂的服务中断(连接断开)。

HAProxy 提供了热更新 能力:它通过 global 段里的 stats socket 暴露了一个 Unix 套接字 (管理通道),我们只要用 socat 往这个套接字里发一条文本命令,就能在线 调整运行中的实例,全程无需重启

复制代码
socat(命令工具) ──写命令──▶ /var/lib/haproxy/stats(套接字) ──▶ HAProxy 进程(即时生效)

6.2 实战命令

先安装 socat,再开启套接字(mode 600 level admin 表示权限 600、管理员级别,可执行修改类命令):

bash 复制代码
[root@localhost ~]# dnf install socat

[root@localhost /]# echo "show info" | socat stdio /var/lib/haproxy/stats		#查看状态
[root@localhost /]# echo "show servers state" | socat stdio /var/lib/haproxy/stats		#查看集群
[root@localhost /]# echo "get weight webcluster/web1" | socat stdio /var/lib/haproxy/stats		#查看权重
1 (initial 1)			#前面是生效权重,后面是设置权重

# turn on stats unix socket
stats socket /var/lib/haproxy/stats mode 600 level admin		#设置套接字
[root@localhost /]# systemctl restart haproxy.service
[root@localhost /]# echo "set weight webcluster/web1 2" | socat stdio /var/lib/haproxy/stats	#修改权重
[root@localhost /]# echo "disable server webcluster/web1" | socat stdio /var/lib/haproxy/stats	#指定服务器下线
[root@localhost /]# echo "enable server webcluster/web1" | socat stdio /var/lib/haproxy/stats	#指定服务器上线

命令语法拆解:

bash 复制代码
echo "命令文本" | socat stdio /var/lib/haproxy/stats
#        ↑ 要执行的HAProxy命令       ↑    ↑  连到管理套接字

常用命令:

socat 命令 作用
show info 查看 HAProxy 运行状态、连接数、版本等信息
show servers state 查看集群中所有后端服务器的状态
get weight <组>/<服务器> 查看某台服务器的权重
set weight <组>/<服务器> <值> 在线修改权重(无需重启)
disable server <组>/<服务器> 在线摘除服务器(下线维护)
enable server <组>/<服务器> 在线恢复服务器(重新上线)

⚠️ 注意:get weight 输出 1 (initial 1)前面是当前生效权重,括号里是配置文件中设置的初始权重

stats socket ... level admin 里的 level admin 决定了权限级别,只有 admin 级别才能执行 set / disable / enable 等写操作


七、状态页(Web 监控面板)

7.1 配置状态页

HAProxy 自带一个 Web 状态页,能实时看到每台后端的健康状态、请求数、连接数。配置一个专门用于监控的 listen 实例:

bash 复制代码
[root@localhost /]# vim /etc/haproxy/haproxy.cfg 
listen stats_page
        bind    *:4444
        mode http
        stats   enable  #开启状态页
        log     global  #全局配置日志
        stats   refresh 1s      #自动刷新,1秒一次
        stats uri /stats        #自定义uri
        stats auth li:li        #认证,支持多行
        
[root@localhost /]# systemctl restart haproxy.service
配置行 含义
bind *:4444 状态页监听在 4444 端口(独立端口,不与业务混用)
stats enable 开启状态页功能
stats refresh 1s 页面每秒自动刷新一次
stats uri /stats 状态页的访问路径,访问 http://IP:4444/stats
stats auth li:li 访问认证(用户名:密码),可写多行加多组账号

7.2 状态页怎么看

浏览器访问 http://192.168.17.100:4444/stats,输入账号密码进入:

字段 含义 关注点
每台 web1 / web2 各后端服务器的实时状态 Status
Status = UP 服务器健康,正在服务 ✅ 正常
Status = DOWN 健康检查失败,已被摘除 🔴 排查后端问题
Status = MAINT 维护状态(被 disable) 🟠 主动下线中
Sessions / Bytes 等 会话数、流量统计 观察负载分布是否均匀

八、客户端真实 IP 透传

8.1 为什么要透传真实 IP

默认情况下,后端服务器看到的所有请求来源都是 HAProxy 的 IP(193.168.0.100),而不是真实客户端 IP。这会带来两个问题:

  1. 日志失真 ------ 后端 access.log 里全是代理 IP,没法做访问分析、地域统计;
  2. 安全失控 ------ 风控、防 CC 攻击、IP 黑名单全都失效,因为根本不知道谁在访问。

所以"让后端能看到真实客户端 IP "是生产环境的硬需求。而具体怎么做,取决于 HAProxy 工作在七层还是四层

工作模式 透传手段 原理
七层(HTTP) X-Forwarded-For 头 在 HTTP 请求头里附加客户真实 IP
四层(TCP) PROXY Protocol 在 TCP 连接建立后,先发一行带客户端 IP 的文本头

8.2 七层透传:X-Forwarded-For

原理

复制代码
客户端(192.168.17.1) → HAProxy ──在请求头追加 X-Forwarded-For: 192.168.17.1──▶ 后端

只需要在 HAProxy 的 defaults 里开启 option forwardfor,后端再配合读取该头即可。

8.2.1 后端是 Nginx

① 透传前:后端 Nginx 的 access.log 里全是代理 IP:

bash 复制代码
[root@localhost ~]# cat /var/log/nginx/access.log 
193.168.0.100 - - [06/Aug/2026:17:07:03 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-"
193.168.0.100 - - [06/Aug/2026:17:07:03 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-"
193.168.0.100 - - [06/Aug/2026:17:07:10 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-"

① 的截图验证

② 在 HAProxy 开启 forwardfor

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8			#配置这条命令
    option                  redispatch
    retries                 3
    
[root@localhost ~]# systemctl restart haproxy.service


#rs服务器上
[root@localhost ~]# cat /var/log/nginx/access.log 
193.168.0.100 - - [06/Aug/2026:17:09:07 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1"
193.168.0.100 - - [06/Aug/2026:17:09:07 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1"
193.168.0.100 - - [06/Aug/2026:17:09:14 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1"

② 的截图验证

③ 结果对比 :日志末尾 多出来的 "192.168.17.1" 就是真实客户端 IP!

原理:Nginx 默认的 combined 日志格式里有 $http_x_forwarded_for 字段,HAProxy 追加了 X-Forwarded-For 头,Nginx 就把它打印到了日志末尾。HAProxy 开启 → 后端自动就能看到,不需要改 Nginx 配置。

8.2.2 后端是 Apache

Apache 需要手动改日志格式 ,把 X-Forwarded-For 加进 combined 格式里:

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8			#配置这条命令
    option                  redispatch
    retries                 3
    
[root@localhost ~]# systemctl restart haproxy.service
[root@localhost ~]# vim /etc/httpd/conf/httpd.conf
<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Forwarded-For}i\"  " combined			#添加X-Forwarded-For
    LogFormat "%h %l %u %t \"%r\" %>s %b" common
    
[root@localhost ~]# systemctl restart httpd

[root@localhost ~]# cat /etc/httpd/logs/default.log
193.168.0.100 - - [09/Aug/2026:11:35:21 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0"
193.168.0.100 - - [09/Aug/2026:11:35:22 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0"
193.168.0.100 - - [09/Aug/2026:11:35:23 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0"
193.168.0.100 - - [09/Aug/2026:11:35:24 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0"
193.168.0.100 - - [09/Aug/2026:11:41:25 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1"  
193.168.0.100 - - [09/Aug/2026:11:41:26 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1"  
193.168.0.100 - - [09/Aug/2026:11:41:27 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "192.168.17.1" 

修改后的截图验证

讲解:

  • 关键改动是 combined 格式末尾加的 "%{X-Forwarded-For}i"i 表示读请求头(incoming header);
  • 对比上下的日志:修改后,日志末尾出现了 "192.168.17.1" 真实客户端 IP。

8.3 四层透传:PROXY Protocol

原理

四层模式下 HAProxy 不解析 HTTP,没法用 X-Forwarded-For 头。解决办法是 PROXY Protocol:HAProxy 在和后端建立 TCP 连接后、发送业务数据前,先给后端发一行特殊文本,格式类似:

复制代码
PROXY TCP4 192.168.17.1 193.168.0.100 51234 80\r\n
      ↑ 协议版本   ↑真实客户端IP   ↑后端IP ↑源端口 ↑目的端口

后端开启 proxy_protocol 接收能力后,就能从这行文本里取出真实 IP。适用于数据库、Redis、裸 TCP 等七层无法处理的场景。

复制代码
客户端(192.168.17.1) ──TCP──▶ HAProxy ──先发一行PROXY头──▶ 后端(解析出真实IP)

8.3.1 HAProxy 端(核心)

server 行加 send-proxy 参数,告诉 HAProxy 往后端发 PROXY 头;同时模式改为 mode tcp

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
        bind *:80
        mode tcp
        balance roundrobin
        server web1 193.168.0.10:80 send-proxy check
        server web2 193.168.0.20:80 send-proxy check
        
[root@localhost ~]# systemctl restart httpd

8.3.2 后端 Nginx

在 Nginx 的 server 块里加 proxy_protocol,表示"我接收 PROXY 头":

bash 复制代码
[root@localhost ~]# vim /etc/nginx/nginx.conf
  # for more information.
    include /etc/nginx/conf.d/*.conf;
    server {
        listen       80 proxy_protocol;				#只能通过四层访问
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf; 
        
[root@localhost ~]# systemctl  restart nginx

加了 listen 80 proxy_protocol 后,Nginx 只接受带 PROXY 头的连接,直接访问后端 IP:80 反而会报错

8.3.3 后端 Apache

Apache 需要加载 mod_remoteip 模块并开启 PROXY 解析:

bash 复制代码
[root@localhost ~]# vim /etc/httpd/conf.modules.d/10-proxy_h2.conf
LoadModule proxy_http2_module modules/mod_proxy_http2.so
LoadModule remoteip_module modules/mod_remoteip.so

[root@localhost ~]# vim /etc/httpd/conf/httpd.conf
RemoteIPProxyProtocol on
RemoteIPTrustedProxy 192.168.17.0/24		#信任谁来访问

[root@localhost ~]# systemctl restart httpd
配置行 含义
LoadModule remoteip_module ... 加载 remoteip 模块,Apache 才有能力解析 PROXY 头
RemoteIPProxyProtocol on 开启 PROXY Protocol 解析
RemoteIPTrustedProxy 192.168.17.0/24 只信任来自这个网段的 PROXY 头------安全关键配置!防止外部伪造 PROXY 头冒充任意 IP

RemoteIPTrustedProxy 必须配置且写准确,否则任何人都能伪造 PROXY 头,把自己"伪装"成任意客户端 IP,绕过 IP 风控。信任来源永远要最小化。

8.4 效果验证

Apache 后端日志中,来源 IP 已变成真实客户端 192.168.17.1:

bash 复制代码
[root@localhost ~]# cat /etc/httpd/logs/default.log
192.168.17.1 - - [09/Aug/2026:12:07:25 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-"  
192.168.17.1 - - [09/Aug/2026:12:10:46 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-"  
192.168.17.1 - - [09/Aug/2026:12:10:46 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/8.4.0" "-" 

Nginx 后端 需要把 $proxy_protocol_addr 写进日志格式才能取到 PROXY 头里的 IP:

bash 复制代码
[root@localhost ~]# vim /etc/nginx/nginx.conf
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '"$proxy_protocol_addr"'
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
                      
[root@localhost ~]# systemctl restart nginx.service

[root@localhost ~]# cat /var/log/nginx/access.log 
193.168.0.100 - - [09/Aug/2026:12:21:37 +0800] "GET / HTTP/1.1" "192.168.17.1"200 18 "-" "curl/8.4.0" "-"
193.168.0.100 - - [09/Aug/2026:12:21:37 +0800] "GET / HTTP/1.1" "192.168.17.1"200 18 "-" "curl/8.4.0" "-"
193.168.0.100 - - [09/Aug/2026:12:21:38 +0800] "GET / HTTP/1.1" "192.168.17.1"200 18 "-" "curl/8.4.0" "-"
193.168.0.100 - - [09/Aug/2026:12:21:39 +0800] "GET / HTTP/1.1" "192.168.17.1"200 18 "-" "curl/8.4.0" "-"

日志里 "192.168.17.1" 就是 PROXY 头解析出的真实客户端 IP($proxy_protocol_addr)。

8.5 七层 vs 四层透传对比小结

维度 七层 X-Forwarded-For 四层 PROXY Protocol
适用模式 mode http mode tcp
实现机制 在 HTTP 头追加字段 在 TCP 流开头发文本头
HAProxy 配置 option forwardfor server ... send-proxy
后端配置 Nginx 默认支持 / Apache 改 LogFormat Nginx proxy_protocol / Apache mod_remoteip
风险点 头可被伪造,需信任链 头可被伪造,需 RemoteIPTrustedProxy 限制
典型场景 Web 网站访问分析 数据库、Redis、裸 TCP

九、负载均衡算法详解

HAProxy 通过 balance 参数指定后端调度算法,balance 可以写在 listenbackend 段里。

算法分成三大类:静态算法、动态算法、哈希类(混合)算法

复制代码
                    ┌─ 静态算法:static-rr、first(运行时不改权重)
   HAProxy 调度算法 ─┼─ 动态算法:roundrobin、leastconn(支持运行时调整)
                    └─ 哈希算法:source、uri、url_param、hdr(按内容做hash)

9.1 静态算法

static-rr:基于权重的轮询调度

按权重依次轮流分发,规则固定、从头到尾一个节奏

  • 不支持运行时用 socat 动态调整权重(只支持 0 和 1);
  • 不支持后端服务器"慢启动";
  • 后端主机数量没有限制;
  • 相当于 LVS 中的 wrr(加权轮询)。

记忆:static-rr 名字里的 static(静态)就是它的宿命------配置时定死,运行时改不动

first:优先填满第一台

  • 根据服务器在列表中的位置,自上而下调度;
  • 只有当前服务器的连接数达到上限,新请求才分给下一台;
  • 会忽略服务器的权重设置
  • 不支持用 socat 动态修改权重(可设置 0、1,其它值无效)。

记忆:first 就像"第一台没满就永远先发给第一台",适合做热备叠加(后加的机器吃剩余流量)。

9.2 动态算法

roundrobin:加权轮询(默认算法)

最常用的动态轮询,支持运行时改权重

  • 基于权重的轮询动态调度算法;
  • 支持权重的运行时调整(区别于 LVS 的 rr 轮询);
  • 支持慢启动(新上线的服务器权重逐渐增加,避免刚上线就被打爆);
  • 每个 backend 中最多支持 4095 个 real server;
  • 支持对 real server 权重动态调整;
  • roundrobin 是默认调度算法,使用最广泛。

记忆:动态轮询 = 能"边跑边改"的轮询。

leastconn:最少连接

优先把新连接分给当前活跃连接数最少的后端。

  • 加权的最少连接动态调度算法;
  • 支持权重的运行时调整和慢启动;
  • 根据当前连接最少的后端服务器而非权重进行优先调度(对新客户端连接而言);
  • 比较适合长连接场景,如:MySQL、Redis。

记忆:轮询是"按顺序",leastconn 是"谁最闲给谁"。短请求轮询更公平,长连接一定要用 leastconn,否则所有连接都堆在第一台。

9.3 哈希类算法(混合)

source:基于源地址哈希

对客户端源 IP 做 hash,再把结果映射到后端。

  • 基于源地址 hash,将请求转发到后端服务器,后续同一个源地址的请求会被转发到同一台后端服务器
  • 弊端:后端服务器上线或下线会导致 hash 结果偏移,大量请求可能被重新分配(对应"雪崩式迁移")。

记忆:source 的天然副作用是"同一用户永远粘在同一台机器",所以它也能当会话保持用,但后端增减会导致映射大乱。

一致性哈希(解决 source 的弊端)

当服务器总权重发生变化时,使用一致性哈希后,对调度结果的影响是局部的,不会引起大的变动------只有被增删节点"命中的那一小段"请求才需要重新分配。

复制代码
普通取模hash:    1 2 3 4 5 6 7 8  → 去掉一台 → 全部重排,所有请求大搬家
一致性hash:      (哈希环上按段分配)→ 去掉一台 → 只有该节点附近的一小段受影响

配置方法,在 backend 里加上:

ini 复制代码
hash-type consistent
  • 该 hash 算法是动态的,支持 socat 等工具在线调整;
  • 支持慢启动。

uri:基于 URI 哈希

对用户请求的 URI(左半部分或整个 URI)做 hash,再对总权重取模后分发。

  • 适用于后端是缓存服务器的场景(同一 URL 永远到同一台缓存,命中率高);
  • 默认是静态算法,也可以通过 hash-type map-basedhash-type consistent,选择使用取模法 还是一致性哈希

url_param:基于 URL 参数哈希

对用户请求 URL 的 params 部分中某个参数 key 对应的 value 做 hash 计算,再除以总权重派发。

  • 后端处理同一数据会被调度到同一个服务器;
  • 多用于电商 ------比如按 user_id 参数把同一用户的请求固定到同一台机器。

hdr:基于 HTTP 头部哈希

针对用户每个 HTTP 头部(Header)请求中指定信息做 hash,再由服务器总权重取模后派发。

  • 如果请求头里没有有效值,则使用默认的轮询调度。

记忆:hdr = "看头不看身",可以按 CookieUser-Agent 等任意请求头做粘滞。

9.4 算法选型总结

算法 类型 适用场景 是否支持运行时改权重
static-rr 静态 简单轮询、后端固定不变
first 静态 第一台打满再用第二台
roundrobin 动态 通用 Web 默认首选
leastconn 动态 长连接:MySQL / Redis
source 哈希 会话粘滞、同一来源固定后端
uri 哈希 缓存服务、CDN 命中
url_param 哈希 电商按用户参数分流
hdr 哈希 按请求头分流

10.1 为什么需要会话保持

很多网站有"登录状态":用户在 A 机器上登录了,下次请求如果被轮询分到 B 机器,B 上没有他的登录信息,就会掉线

解决办法就是会话保持(Session Sticky):让同一个用户的所有请求都发给同一台后端。

Cookie 会话保持的原理

复制代码
第1次请求:客户端 ──▶ HAProxy(后端任选一台 web1,并在响应里种一个 Cookie)
第2次请求:客户端带着Cookie ──▶ HAProxy(一看Cookie,直接转发给 web1)

cookie value 指令就是为当前 server 指定 Cookie 值,实现基于 Cookie 的会话粘滞。它与 source 算法比,对客户端的区分粒度更精准(source 按 IP,NAT 下多台用户共享一个 IP 就分不开了),但同时也加大了 HAProxy 的负担。目前此模式使用较少,很多场景已被 session 共享服务器(如 Redis)替代。

ini 复制代码
cookie name [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain ]* [maxidle <idle> ] [ maxlif ]
name		#cookie的key名称,用于实现持久连接
insert		#插入新的cookie,默认不插入cookie
indirect	#如果客户端已经有cookie,则不会再发送cookie信息
nocache		#当client和paharxy之间有缓存服务器时,不允许中间缓存器缓存cookie,因为这会导致很多经过同一个CDN的请求都发送到同一台后端服务器
选项 含义
name Cookie 的 key 名称,用于实现持久连接
insert 由 HAProxy 插入一个新的 Cookie(默认不插入)
indirect 如果客户端已经有该 Cookie,就不再下发 Cookie 信息
nocache 客户端与 HAProxy 之间有缓存服务器(CDN)时,禁止中间缓存器缓存 Cookie------否则很多经过同一个 CDN 的请求会被固定到同一台后端,破坏负载均衡

10.3 配置实战

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
        bind *:80
        mode http
        balance roundrobin
        cookie WEBCOOKIE insert nocache indirect
        server web1 193.168.0.10:80 cookie server1 weight 1 check inter 2s fall 3 rise 3
        server web2 193.168.0.20:80 cookie server2 weight 1 check inter 2s fall 3 rise 3
        
[root@localhost ~]# systemctl restart haproxy.service 

关键行解读:

  • cookie WEBCOOKIE insert nocache indirect ------ 设置名为 WEBCOOKIE 的会话保持 Cookie;
  • server web1 ... cookie server1 ------ 给 web1 一个 Cookie 值 server1
  • server web2 ... cookie server2 ------ 给 web2 一个 Cookie 值 server2

测试效果:第一次访问后,响应里出现 Set-Cookie: WEBCOOKIE=server1;刷新后再访问,请求头里带着该 Cookie,就会被固定转发到 web1

带 Cookie 后无论刷新多少次,都稳定落在同一台后端上,说明会话保持生效了。


十一、自定义错误页面

11.1 默认错误页

HAProxy 自带一套默认错误页面,放在 /usr/share/haproxy/ 目录下:

bash 复制代码
[root@localhost ~]# cd /usr/share/haproxy/					#默认错误页面位置
[root@localhost haproxy]# ls
400.http  403.http  408.http  500.http  502.http  503.http  504.http  README
[root@localhost haproxy]# 

文件名就是状态码:400 / 403 / 408 / 500 / 502 / 503 / 504。当后端全部宕机时,HAProxy 返回 503(Service Unavailable)。

11.2 自定义错误页

步骤分两步:

第 1 步:写一个带完整 HTTP 响应头的错误页文件

bash 复制代码
[root@localhost ~]# mkdir /htmlerr/

[root@localhost ~]# vim /htmlerr/503.errpage
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache
Connection: close
Content-Type: text/html

<html><body><h1>页面错误,请耐心等待</h1>
No server is available to handle this request.
</body></html>

⚠️ 注意:文件必须手动写全 HTTP 响应头(状态行、Cache-Control、Content-Type 等),因为 HAProxy 是原样把整个文件发给客户端的。

第 2 步:用 errorfile 指令把状态码指向自定义文件

bash 复制代码
errorfile <code> <file>
<code>		#HTTP status code,支持200,400,403,405,408,425,429,500,502,503,504等
<file>		#包含完整HTTP响应头的错误页文件的绝对路径

[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
defaults

    errorfile 503               /htmlerr/503.errpage
    
[root@localhost ~]# systemctl restart haproxy.service

语法讲解:

参数 含义
errorfile 指定某状态码使用自定义错误页
503 要替换的 HTTP 状态码(支持 200/400/403/405/408/425/429/500/502/503/504)
/htmlerr/503.errpage 自定义错误页文件的绝对路径

测试效果:停掉两台后端,访问网站看到的就是你自定义的 503 页面:


十二、ACL 访问控制

12.1 ACL 是什么

ACL(Access Control List,访问控制列表) 是一种基于包过滤的访问控制技术。

它可以根据设定的条件对经过服务器的数据包进行过滤(条件匹配),即对接收到的报文进行匹配和过滤,基于请求报文头部中的源地址、源端口、目标地址、目标端口、请求方法、URL、文件后缀等信息内容进行匹配,并执行进一步操作,比如允许其通过或丢弃。

通俗讲:ACL = 给请求"体检",体检合格(匹配)就发到 A 组后端,不合格就发到 B 组或直接拒绝。它是 HAProxy 实现"按规则分流"的核心武器。

12.2 ACL 语法

bash 复制代码
#用acl来定义或声明一个acl
acl		<aclname>		<criterion>		[flags]		[operator]		[<value>]
acl		名称				匹配规范		匹配模式		具体操作符		操作对象类型

语法结构图:

复制代码
acl   test     path_end     -m   sub    /a
 │     │          │         │    │     │
名称   匹配规范   匹配模式  操作符  匹配值
部分 含义
aclname ACL 名称,自定义,后面要用它引用
criterion 匹配规范,按什么字段匹配(如 hdr_end 匹配请求头结尾、path_end 匹配路径结尾)
flags 匹配模式(如 -i 忽略大小写、-m 指定匹配方法)
operator 操作符(如 sub 子串匹配)
value 匹配的对象值

ACL 名称命名规则:

bash 复制代码
acl		test		path_end		-m			sub			/a
#ACL名称可以使用大写A-Z、小写a-z、数字0-9、冒号:、点.、中横线和下划线,并严格区分大小写

即:名称可用 A-Z a-z 0-9 : . - _,且严格区分大小写

12.3 实战:按域名分发到不同后端

需求:客户端访问的 Host 以 .com 结尾 → 分发到 webserver1;否则 → 分发到默认后端。

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
frontend webcluster
        bind    *:80
        mode    http

        acl test hdr_end(host) -i .com		#Host 以 .com 结尾(不分大小写)
    
        use_backend webserver1 if test		#匹配
        default_backend webserverdefault	#不匹配

backend webserver1
        server web1 193.168.0.10:80 check inter 2s fall 2 rise 3
backend webserverdefault
        server web2 193.168.0.20:80 check inter 2s fall 2 rise 3
        
[root@localhost ~]# systemctl restart haproxy.service

逐行解读:

含义
acl test hdr_end(host) -i .com 定义名为 test 的 ACL:取请求的 Host 头,看它是否.com 结尾-i 表示忽略大小写
use_backend webserver1 if test 如果匹配 test 这个 ACL → 转发到 webserver1
default_backend webserverdefault 否则(默认) → 转发到 webserverdefault
check inter 2s fall 2 rise 3 健康检查:每 2 秒一次,连续失败 2 次下线,连续成功 3 次上线

测试效果:

用不同的域名(Host 头)访问同一 IP,看请求被分流到不同后端:

.com 结尾的域名(应命中 webserver1):

.org 结尾的域名(不命中 test,走默认 webserverdefault):


十三、四层负载实战:MariaDB 数据库集群

四层(TCP)负载------HAProxy 不关心数据库协议内容,只负责把 TCP 连接转发给数据库后端。

13.1 架构

复制代码
客户端 ──▶ HAProxy :3306(mode tcp)──▶ db1(193.168.0.10:3306, server-id=10)
                                        └──▶ db2(193.168.0.20:3306, server-id=20)

13.2 RS1、RS2:安装并初始化 MariaDB

在两台后端上安装 MariaDB,并设置不同的 server-id(主从复制识别用,这里用来区分验证):

bash 复制代码
[root@localhost ~]# dnf install mariadb-server

[root@localhost ~]# vim /etc/my.cnf.d/mariadb-server.cnf 
[mysqld]
server-id=10
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mariadb/mariadb.log
pid-file=/run/mariadb/mariadb.pid

[root@localhost ~]# systemctl start mariadb.service
[root@localhost ~]# mysql
MariaDB [(none)]> create user root@'%' identified by '123'

说明:db1 配 server-id=10,db2 把 server-id 改成 20,这样查询 @@server_id 时能区分到底连到了哪一台。同时创建一个可从任意主机登录的 root 账号(root@'%'),方便从客户端连接。

13.3 HAProxy:四层数据库负载

在 HAProxy 配置里新增一个 listenmode tcp 是关键------对数据库这种非 HTTP 协议,必须用四层模式:

bash 复制代码
[root@localhost system-connections]# vim /etc/haproxy/haproxy.cfg 
listen dbcluster
        bind *:3306
        mode tcp
        balance roundrobin
        server db1 193.168.0.10:3306 check inter 2s fall 3 rise 3
        server db2 193.168.0.20:3306 check inter 2s fall 3 rise 3

[root@localhost system-connections]# systemctl restart haproxy.service

讲解:

含义
bind *:3306 监听 3306 端口(MySQL 默认端口)
mode tcp 四层 TCP 模式,不解析数据库协议,只转发流量
server db1 / db2 ... check inter 2s fall 3 rise 3 两台数据库后端,健康检查 2 秒一次,失败 3 次下线、成功 3 次上线

13.4 验证

客户端通过 HAProxy 地址连数据库,查询 server_id 确认请求被轮询分发到了两台不同的数据库:

bash 复制代码
[root@localhost ~]# mysql -uroot -p123 -h192.168.17.100
MariaDB [(none)]> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          10 |
+-------------+

[root@localhost ~]# mysql -uroot -p123 -h192.168.17.100
MariaDB [(none)]> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          20 |
+-------------+

第一次返回 10、第二次返回 20,说明 HAProxy 在 db1 和 db2 之间做了轮询------四层数据库负载均衡成功


十四、全站 HTTPS 加密

14.1 HTTPS 终止原理

SSL 卸载(SSL Termination) :客户端和 HAProxy 之间跑 HTTPS(443) ,HAProxy 负责解密;HAProxy 和后端之间跑普通 HTTP(80),后端不需要处理加解密。

复制代码
客户端 ──HTTPS(443,加密)──▶ HAProxy(解密) ──HTTP(80,明文)──▶ 后端

这样做的好处:

  • 证书只需维护一份(放在 HAProxy 上),后端不用各自配证书;
  • 减轻后端 CPU 负担(加解密最耗 CPU,集中到 HAProxy 处理);
  • 全站加密,传输过程不被窃听。

14.2 生成自签名证书

bash 复制代码
mkdir /etc/haproxy/certs/

openssl req -newkey rsa:2048 -nodes -sha256  -keyout /etc/haproxy/certs/qw.key -x509 -days 365 -out /etc/haproxy/certs/qw.crt
[root@localhost ~]# cat /etc/haproxy/certs/qw.key /etc/haproxy/certs/qw.crt  > /etc/haproxy/qw.pem

命令讲解:

命令 作用
mkdir /etc/haproxy/certs/ 建证书存放目录
openssl req ... -keyout qw.key ... -out qw.crt 生成 RSA 2048 位 自签名证书:-newkey rsa:2048 生成新密钥,-x509 生成自签名证书,-days 365 有效期一年
cat qw.key qw.crt > qw.pem 把私钥和证书合并成一个 PEM 文件------HAProxy 要求证书和私钥在同一个文件里

14.3 配置 HAProxy

bash 复制代码
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg 
listen webcluster-80
        bind    *:80
        mode    http
        redirect scheme https if !{ ssl_fc }

frontend webcluster
        bind    *:443 ssl crt /etc/haproxy/qw.pem
        mode    http
        use_backend webserver

backend webserver
        server web1 193.168.0.10:80 check inter 2s fall 2 rise 3
        server web2 193.168.0.20:80 check inter 2s fall 2 rise 3

[root@localhost ~]# systemctl restart haproxy.service

逐行解读:

配置行 含义
listen webcluster-80 专门处理 80 端口(HTTP)的实例
redirect scheme https if !{ ssl_fc } HTTP 强制跳转 HTTPSssl_fc 是"当前连接已启用 SSL"的标记,!{ ssl_fc } 表示"不是 SSL 连接",此时强制跳转到 https
bind *:443 ssl crt /etc/haproxy/qw.pem 在 443 端口启用 SSL,使用之前生成的 qw.pem 证书
use_backend webserver 解密后的请求转给 webserver 后端(后端仍是普通 HTTP)

14.4 验证

相关推荐
一条泥憨鱼18 分钟前
【从0开始学习计算机网络】| WebSocket-握手、全双工和心跳
websocket·计算机网络·网络安全·https·dns
云飞云共享云桌面20 分钟前
机械设计研发成本优化:怎样用一台高性能服务器替代 10 台 SolidWorks 工作站?
大数据·运维·服务器·汽车·制造
Horn Still Sounds25 分钟前
Linux系统编程进阶:进程回收、程序替换与多线程并发模型
linux·运维·服务器·c语言
挨踢诗人3 小时前
金蝶K3 WISE × 乐企平台 直连数电票集成解决方案(商贸企业开票自动化)
大数据·运维·自动化
光电的一只菜鸡9 小时前
ubuntu之坑(二十)——VMware虚拟机系统无法正常进入如何处理
linux·运维·ubuntu
青 春 记 忆9 小时前
Dify Docker Compose 通用无损升级指南:从备份、双版本预演到切换与回滚
运维·人工智能·python·docker·容器
Java王小怪10 小时前
Docker环境Redis没挂载保存数据
运维·docker·容器
你住过的屋檐10 小时前
【Nginx】linux上安装nginx
linux·运维·nginx
晓晓_za89866812 小时前
开源 GEO 优化源码二次开发:贴牌改造与业务模块扩展实践
java·运维·服务器·开发语言·性能优化·开源