云原生(haproxy七层代理概述)

一、负载均衡概述

1.1 什么是负载均衡

负载均衡(Load Balance,简称LB)是一种服务或基于硬件设备等实现的高可用反向代理技术。负载均衡将特定的业务(Web服务、网络流量等)分担给指定的一个或多个后端特定的服务器或设备,从而提高了公司业务的并发处理能力、保证了业务的高可用性、方便了业务后期的水平动态扩展。

阿里云SLB介绍:https://yq.aliyun.com/articles/1803

1.2 为什么用负载均衡

  • Web服务器的动态水平扩展:对用户无感知

  • 增加业务并发访问及处理能力:解决单服务器瓶颈问题

  • 节约公网IP地址:降低IT支出成本

  • 隐藏内部服务器IP:提高内部服务器安全性

  • 配置简单:固定格式的配置文件

  • 功能丰富:支持四层和七层,支持动态下线主机

  • 性能较强:并发数万甚至数十万

1.3 负载均衡类型

1.3.1 硬件负载均衡
1.3.2 四层负载均衡

特点:

  1. 通过IP+端口决定负载均衡的去向

  2. 对流量请求进行NAT处理,转发至后台服务器

  3. 记录TCP、UDP流量分别是由哪台服务器处理,后续该请求连接的流量都通过该服务器处理

支持四层的软件:

  • LVS:重量级四层负载均衡器

  • Nginx:轻量级四层负载均衡器,可缓存(nginx四层是通过upstream模块)

  • HAProxy:模拟四层转发

1.3.3 七层负载均衡

代理功能:

  1. 通过虚拟URL或主机IP进行流量识别,根据应用层信息进行解析,决定是否需要进行负载均衡

  2. 代理后台服务器与客户端建立连接,如nginx可代理前后端,与前端客户端TCP连接,与后端服务器建立TCP连接

支持七层代理的软件:

  • Nginx:基于http协议(nginx七层是通过proxy_pass)

  • HAProxy:七层代理,会话保持、标记、路径转移等

1.3.4 四层和七层的区别

所谓的四到七层负载均衡,就是在对后台的服务器进行负载均衡时,依据四层的信息或七层的信息来决定怎么样转发流量。

四层负载均衡:就是通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。

七层负载均衡:就是在四层的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征,比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。

主要区别:

  1. 分层位置:四层负载均衡在传输层及以下,七层负载均衡在应用层及以下

  2. 性能:四层负载均衡架构无需解析报文消息内容,在网络吞吐量与处理能力上较高;七层可支持解析应用层报文消息内容,识别URL、Cookie、HTTP header等信息

  3. 原理:四层负载均衡是基于IP+port;七层是基于虚拟的URL或主机IP等

  4. 功能类比:四层负载均衡类似于路由器;七层类似于代理服务器

  5. 安全性:四层负载均衡无法识别DDoS攻击;七层可防御SYN Cookie/Flood攻击

二、HAProxy简介

HAProxy是法国开发者威利·塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件,是一款具备高并发(万级以上)、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统计。

相关网址:

企业版和社区版功能对比:

功能 社区版 企业版
高级HTTP/TCP负载平衡和持久性 支持 支持
高级健康检查 支持 支持
应用程序加速 支持 支持
高级安全特性 支持 支持
高级管理 支持 支持
HAProxy Dev Branch新功能 支持 支持

三、HAProxy的安装和服务信息

3.1 实验环境

功能 IP
客户端 eth0: 172.25.254.10
HAProxy eth0: 172.25.254.100, eth1: 192.168.0.10
RS1 eth0: 192.168.0.101
RS2 eth0: 192.168.0.102

3.2 软件安装

软件包下载地址: https://github.com/haproxy/wiki/wiki/Packages

安装软件包:

复制代码
dnf install haproxy -y

查看版本:

复制代码
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.e19_4.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Apr 10 10:29:16 EDT 2024 x86_64

3.3 HAProxy的基本配置信息

官方文档: http://cbonte.github.io/haproxy-dconv/

HAProxy的配置文件haproxy.cfg由两大部分组成:

  1. global:全局配置段

    • 进程及安全配置相关的参数

    • 性能调整相关参数

    • Debug参数

  2. proxies:代理配置段

    • defaults:为frontend、backend、listen提供默认配置

    • frontend:前端,相当于nginx中的server

    • backend:后端,相当于nginx中的upstream

    • listen:同时拥有前端和后端配置,配置简单,生产推荐使用

3.3.1 global配置

global配置示例:

复制代码
global
    log 127.0.0.1 local2               # 定义全局的syslog服务器:日志服务器需要开启UDP协议,最多可以定义两个
    chroot /var/lib/haproxy            # 锁定运行目录
    pidfile /var/run/haproxy.pid       # 指定pid文件
    maxconn 100000                     # 指定最大连接数
    user haproxy                       # 指定haproxy的运行用户
    group haproxy                      # 指定haproxy的运行组
    daemon                             # 指定haproxy以守护进程方式运行
    stats socket /var/lib/haproxy/stats # 指定haproxy的套接字文件
3.3.1.2 多进程和线程

多进程和socket文件配置:

复制代码
global
    log 127.0.0.1 local2
    chroot /var/lib/haproxy
    pidfile /var/run/haproxy.pid
    maxconn 100000
    user haproxy
    group haproxy
    daemon
    
    # turn on stats unix socket
    stats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1  # 启用多个sock文件
    stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2
    nbproc 2                                   # 启用多进程
    cpu-map 1 0                                # 进程和cpu核心绑定防止cpu抖动从而减少系统资源消耗
    cpu-map 2 1                                # 2表示第二个进程,1表示第二个cpu核心

查看多进程信息:

复制代码
pstree -p | grep haproxy
3.3.2 Proxies配置
3.3.2.1 defaults配置
复制代码
defaults
    mode http                              # HAProxy实例使用的连接协议
    log global                             # 指定日志地址和记录日志条目的syslog/rsyslog日志设备
                                           # 此处的global表示使用global配置段中设定的log值
    option httplog                         # 日志记录选项,httplog表示记录与HTTP会话相关的各种属性值
                                           # 包括HTTP请求、会话状态、连接数、源地址以及连接时间等
    option dontlognull                     # dontlognull表示不记录会话连接日志
    option http-server-close               # 等待客户端完整HTTP请求的时间,此处为等待10s
    option forwardfor except 127.0.0.0/8   # 透传客户端真实IP至后端web服务器
    option redispatch                      # 当server Id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发
    option http-keep-alive                 # 开启与客户端的会话保持
    retries 3                              # 连接后端服务器失败次数
    timeout http-request 10s               # 等待客户端请求完全被接收和处理的最长时间
    timeout queue 1m                       # 设置删除连接和客户端收到503或服务不可用等提示信息前的等待时间
    timeout connect 120s                   # 设置等待服务器连接成功的时间
    timeout client 600s                    # 设置允许客户端处于非活动状态,即既不发送数据也不接收数据的时间
    timeout server 600s                    # 设置服务器超时时间,即允许服务器处于既不接收也不发送数据的非活动时间
    timeout http-keep-alive 60s            # session会话保持超时时间,此时间段内会转发到相同的后端服务器
3.3.2.2 frontend配置

frontend配置参数:

  • bind:指定HAProxy的监听地址,可以是IPv4或IPv6,可以同时监听多个IP或端口,可同时用于listen字段中

    • 格式:bind [<address>:<port_range>[,...] [param*]

    • 注意:如果需要绑定在非本机的IP,需要开启内核参数:net.ipv4.ip_nonlocal_bind = 1

  • backlog <backlog>:针对所有server配置,当前端服务器的连接数达到上限后的后援队列长度

    • 注意:不支持backend

frontend配置示例:

复制代码
frontend lee-webserver-80
    bind 172.25.254.100:80
    mode http
    use_backend lee-webserver-80-RS  # 调用backend的名称
3.3.2.3 backend配置
  • 定义一组后端服务器,backend服务器将被frontend进行调用

  • 注意:backend的名称必须唯一,并且必须在listen或frontend中事先定义才可以使用,否则服务无法启动

配置参数:

  • mode http|tcp:指定负载协议类型,和对应的frontend必须一致

  • option:配置选项

  • server:定义后端real server,必须指定IP和端口

注意: option后面加httpchk、smtpchk、mysql-check、pgsql-check、ssl-hello-chk方法,可用于实现更多应用层检测功能。

server配置参数:

  • check:对指定real进行健康状态检查,如果不加此设置,默认不开启检查

    • 默认对相应的后端服务器IP和端口,利用TCP连接进行周期性健康性检查

    • 注意必须指定端口才能实现健康性检查

  • addr <IP>:可指定的健康状态监测IP,可以是专门的数据网段,减少业务网络的流量

  • port <num>:指定的健康状态监测端口

  • inter <num>:健康状态检查间隔时间,默认2000ms

  • fall <num>:后端服务器从线上转为线下的检查的连续失效次数,默认为3

  • rise <num>:后端服务器从下线恢复上线的检查的连续有效次数,默认为2

  • weight <weight>:默认为1,最大值为256,0(状态为蓝色)表示不参与负载均衡,但仍接受持久连接

  • backup:将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务,类似SorryServer

  • disabled:将后端服务器标记为不可用状态,即维护状态,除了持久模式将不再接受连接,状态为深黄色,优雅下线,不再接受新用户的请求

  • redirect prefix http://www.baidu.com/:将请求临时(302)重定向至其它URL,只适用于http模式

  • maxconn <maxconn>:当前后端server的最大并发连接数

backend配置示例:

复制代码
backend lee-webserver-80-RS
    mode http
    server web1 192.168.0.101:80 check inter 3s fall 3 rise 5
    server web2 192.168.0.102:80 check inter 3s fall 3 rise 5

测试效果:

复制代码
for N in {1..6}; do curl 172.25.254.100; done

输出示例:

复制代码
RS1 server - 192.168.0.101
RS2 server - 192.168.0.102
RS1 server - 192.168.0.101
RS2 server - 192.168.0.102
RS1 server - 192.168.0.101
RS2 server - 192.168.0.102
3.3.2.4 listen简化配置

使用listen替换frontend和backend的配置方式,可以简化设置,通常只用于TCP协议的应用。

3.4 多进程处理

如果开启多进程,那么我们在对进程的sock文件进行操作时,其对进程的操作是随机的。如果需要指定操作进程,那么需要用多sock文件方式来完成。

配置示例:

复制代码
stats socket /var/lib/haproxy/stats1 mode 600 level admin process 1
stats socket /var/lib/haproxy/stats2 mode 600 level admin process 2
nbproc 2
cpu-map 1 0
cpu-map 2 1

这样每个进程就会有单独的sock文件来进行单独管理。

四、HAProxy的算法

HAProxy通过固定参数balance指明对后端服务器的调度算法。balance参数可以配置在listen或backend选项中。HAProxy的调度算法分为静态和动态调度算法,有些算法可以根据参数在静态和动态算法中相互转换。

4.1 静态算法

静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时修改权重(只能为0和1,不支持其它值),只能靠重启HAProxy生效。

4.1.1 static-rr:基于权重的轮询调度
  • 不支持运行时利用sock进行权重的动态调整(只支持0和1,不支持其它值)

  • 不支持后端服务器慢启动

  • 其后端主机数量没有限制,相当于LVS中的wrr

慢启动:是指在服务器刚刚启动时不会把它所应该承担的访问压力全部给它,而是先给一部分,当没问题后再给一部分。

示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance static-rr
    server webserver1 192.168.0.101:80 weight 2 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.1.2 first
  • 根据服务器在列表中的位置,自上而下进行调度

  • 只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务

  • 会忽略服务器的权重设置

  • 不支持用socat进行动态修改权重,可以设置0和1,可以设置其它值但无效

示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance first
    server webserver1 192.168.0.101:80 maxconn 3 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 check inter 3s fall 3 rise 5

测试效果:

复制代码
# 在两台主机上分别执行此循环,可以观察是否102被调度到
while true; do curl 172.25.254.100; sleep 0.1; done

4.2 动态算法

动态算法:基于后端服务器状态进行调度适当调整,新请求将优先调度至当前负载较低的服务器,权重可以在haproxy运行时动态调整无需重启。

4.2.1 roundrobin
  1. 基于权重的轮询动态调度算法

  2. 支持权重的运行时调整,不同于LVS中的rr轮训模式

  3. HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数)

  4. 其每个后端backend中最多支持4095个real server

  5. 支持对real server权重动态调整

  6. roundrobin为默认调度算法,此算法使用广泛

示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance roundrobin
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5

动态调整权重:

复制代码
echo "set weight webserver_80/webserver1 2" | socat stdio /var/lib/haproxy/haproxy.sock
4.2.2 leastconn
  • leastconn加权的最少连接的动态

  • 支持权重的运行时调整和慢启动,即:根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接)

  • 比较适合长连接的场景使用,比如:MySQL等场景

4.3 其他算法

其他算法即可作为静态算法,又可以通过选项成为动态算法。

4.3.1 source

源地址hash,基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器。默认为静态方式,但是可以通过hash-type支持的选项更改。这个算法一般是在不插入Cookie的TCP模式下使用,也可给拒绝会话cookie的客户提供最好的会话粘性,适用于session会话保持但不支持cookie和缓存的场景。

源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash。

示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance source
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5

测试:

复制代码
for N in {1..6}; do curl 172.25.254.100; done

注意: 如果访问客户端是一个家庭,那么所有的家庭的访问流量都会被定向到一台服务器,这时source算法的缺陷。

4.3.1.1 map-base 取模法

map-based:取模法,对source地址进行hash计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。

此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。

缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变。

hash-type指定的默认值为此算法。

注意: 所谓取模运算,就是计算两个数相除之后的余数,10%7=37%4=3

map-based算法:基于权重取模,hash(source_ip)%所有后端服务器相加的总权重

取模法配置示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance source
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5

不支持动态调整权重值:

复制代码
echo "set weight webserver_80/webserver1 2" | socat stdio /var/lib/haproxy/haproxy.sock
# 输出:Backend is using a static LB algorithm and only accepts weights '0%' and '100%'

只能动态上线和下线:

复制代码
echo "set weight webserver_80/webserver1 0" | socat stdio /var/lib/haproxy/haproxy.sock
echo "get weight webserver_80/webserver1" | socat stdio /var/lib/haproxy/haproxy.sock
# 输出:0 (initial 1)
4.3.1.2 一致性hash

一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动:hash(o) mod n

该hash算法是动态的,支持使用socat等工具进行在线权重调整,支持慢启动。

算法:

  1. 后端服务器哈希环点 keyA = hash(后端服务器虚拟IP) % (2^32)

  2. 客户机哈希环点 key1 = hash(client_ip) % (2^32),得到的值在[0---4294967295]之间

  3. 将keyA和key1都放在hash环上,将用户请求调度到离key1最近的keyA对应的后端服务器

hash环偏斜问题: 增加虚拟服务器IP数量,比如:一个后端服务器根据权重为1生成1000个虚拟IP,再hash。而后端服务器权重为2则生成2000的虚拟IP,再hash,最终在hash环上生成3000个节点,从而解决hash环偏斜问题。

一致性hash示意图:

一致性hash配置示例:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance source
    hash-type consistent
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.2 uri

基于对用户请求的URI的左半部分或整个uri做hash,再将hash结果对总权重进行取模后根据最终结果将请求转发到后端指定服务器。适用于后端是缓存服务器场景。

默认是静态算法,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash。

注意: 此算法基于应用层,所以只支持mode http,不支持mode tcp。

URI结构:

复制代码
<scheme>://<user>:<password>@<host>:<port></path>;<params>?<query>#<frag>
  • 左半部分:</path>;<params>

  • 整个uri:</path>;<params>?<query>#<frag>

4.3.2.1 uri取模法配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance uri
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.2.2 uri一致性hash配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance uri
    hash-type consistent
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5

测试:

复制代码
# 在后端服务器创建测试文件
[root@rs1 ~]# echo "RS1 192.168.0.101 index1" > /var/www/html/index1.html
[root@rs1 ~]# echo "RS1 192.168.0.101 index2" > /var/www/html/index2.html
[root@rs1 ~]# echo "RS1 192.168.0.101 index3" > /var/www/html/index3.html

[root@rs2 ~]# echo "RS2 192.168.0.102 index1" > /var/www/html/index1.html
[root@rs2 ~]# echo "RS2 192.168.0.102 index2" > /var/www/html/index2.html
[root@rs2 ~]# echo "RS2 192.168.0.102 index3" > /var/www/html/index3.html

# 访问测试
[root@node10 ~]# curl 172.25.254.100/index3.html
[root@node10 ~]# curl 172.25.254.100/index1.html
[root@node10 ~]# curl 172.25.254.100/index2.html
[root@node10 ~]# curl 172.25.254.100/index3.html

访问不同的uri,确认可以将用户同样的请求转发至相同的服务器。

4.3.3 url_param

url_param对用户请求的url中的params部分中的一个参数key对应的value值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器,后端搜索同一个数据会被调度到同一个服务器,多用于电商。

通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server。

如果没有key,将按roundrobin算法。

假设: url = http://www.timinglee.com/foo/bar/index.php?key=value
则: host = "www.timinglee.com"url_param = "key=value"

4.3.3.1 url_param取模法配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance url_param name,userid  # 支持对多个url_param hash
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.3.2 url_param一致性hash配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance url_param name,userid  # 支持对多个url_param hash
    hash-type consistent
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.3.3 测试访问
复制代码
[root@node10 ~]# curl "172.25.254.100/index.html?name=timinglee"
[root@node10 ~]# curl "172.25.254.100/index.html?name=timinglee"
[root@node10 ~]# curl "172.25.254.100/index.html?userid=lee"
[root@node10 ~]# curl "172.25.254.100/index.html?userid=lee"
4.3.4 hdr

针对用户每个http头部(header)请求中的指定信息做hash,此处由name指定的http首部将会被取出并做hash计算,然后由服务器总权重取模以后派发至某挑出的服务器,如果无有效值,则会使用默认的轮询调度。

4.3.4.1 hdr取模法配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance hdr(User-Agent)
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.4.2 hdr一致性hash配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode http
    balance hdr(User-Agent)
    hash-type consistent
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
4.3.4.3 测试访问
复制代码
[root@node10 ~]# curl -v 172.25.254.100
[root@node10 ~]# curl -v -A "firefox" 172.25.254.100
[root@node10 ~]# curl -v -A "sougou" 172.25.254.100
4.3.6 算法总结
算法 类型 支持协议
static-rr 静态 tcp/http
first 静态 tcp/http
roundrobin 动态 tcp/http
leastconn 动态 tcp/http
random 动态 tcp/http
source 静态/动态* tcp/http
uri 静态/动态* http
url_param 静态/动态* http
hdr 静态/动态* http

注:source、uri、url_param、hdr算法可以是静态或动态,取决于hash_type是否设置为consistent。

4.3.7 各算法使用场景
  • first:使用较少

  • static-rr:做了session共享的web集群

  • roundrobin:通用场景

  • random:随机分配

  • leastconn:数据库等长连接场景

  • source:基于客户端公网IP的会话保持

  • uri:缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯

  • url_param:可以实现session保持,电商场景

  • hdr:基于客户端请求报文头部做下一步处理

五、HAProxy高级配置及实用案例

5.1 基于cookie的会话保持

cookie value:为当前server指定cookie值,实现基于cookie的会话黏性,相对于基于source地址hash调度算法对客户端的粒度更精准,但同时也加大了haproxy负载,目前此模式使用较少,已经被session共享服务器代替。

注意: 不支持tcp mode,使用http mode。

5.1.1 配置选项
复制代码
cookie name [rewrite | insert | prefix] [indirect] [nocache] [postonly] [preserve] [httponly] [secure] [domain *] [maxidle <idle>] [maxlife]
  • name:cookie的key名称,用于实现持久连接

  • insert:插入新的cookie,默认不插入cookie

  • indirect:如果客户端已经有cookie,则不会再发送cookie信息

  • nocache:当client和haproxy之间有缓存服务器(如:CDN)时,不允许中间缓存器缓存cookie,因为这会导致很多经过同一个CDN的请求都发送到同一台后端服务器

5.1.2 配置示例
复制代码
listen webserver_80
    bind 172.25.254.100:80
    option forwardfor
    mode http
    balance roundrobin
    cookie WEBCOOKIE insert nocache indirect
    server webserver1 192.168.0.101:80 cookie web1 weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 cookie web2 weight 1 check inter 3s fall 3 rise 5
5.1.3 验证cookie信息

通过命令行验证:

复制代码
# 第一次访问
[root@node10 ~]# curl -i 172.25.254.100
HTTP/1.1 200 OK
date: Wed, 10 Jul 2024 16:36:17 GMT
server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.2k-fips
last-modified: Thu, 04 Jul 2024 11:18:39 GMT
etag: "1b-61c6a1bd2408d"
accept-ranges: bytes
content-length: 27
content-type: text/html; charset=UTF-8
set-cookie: WEBCOOKIE=web2; path=/
cache-control: private

RS2 server - 192.168.0.102

# 第二次访问
[Administrator.WIN-20240602BIS] > curl -i 172.25.254.100
HTTP/1.1 200 OK
server: nginx/1.20.1
date: Wed, 10 Jul 2024 08:36:43 GMT
content-type: text/html
content-length: 18
last-modified: Wed, 10 Jul 2024 04:09:05 GMT
etag: "668e0961-12"
accept-ranges: bytes
set-cookie: WEBCOOKIE=web1; path=/
cache-control: private

RS1 192.168.0.101

# 使用指定cookie访问
[root@node10 ~]# curl -b WEBCOOKIE=web1 172.25.254.100
RS1 server - 192.168.0.101
[root@node10 ~]# curl -b WEBCOOKIE=web2 172.25.254.100
RS2 server - 192.168.0.102

5.2 启用状态页

5.2.1 通过web界面,显示当前HAProxy的运行状态
5.2.2 启用状态页配置
复制代码
listen stats
    mode http
    bind 0.0.0.0:8888
    stats enable
    log global
    stats uri /status
    stats auth lee:lee  # 认证,此行可以出现多次

测试: 浏览器访问:http://172.25.254.100:8888/status

5.2.3 登录状态页信息解读

进程信息:

  • pid:当前pid号

  • process:当前进程号

  • nbpnthread:一共多少进程和每个进程多少个线程

示例: pid = 27134 (process #1, nbproc = 1, nthread = 1)

运行时间:

  • uptime:启动了多长时间

示例: uptime = 0d0h00m04s

系统资源限制:

  • memmax:内存限制

  • ulimit-n:最大打开文件数

示例: system limits: memmax = unlimited; ulimit-n = 200029

连接信息:

  • maxsock:最大socket连接数

  • maxconn:单进程最大连接数

  • maxpipes:最大管道数

  • current conns:当前连接数

  • current pipes:当前管道数

  • conn rate:当前连接速率

示例: maxsock = 200029; maxconn = 100000; maxpipes = 0; current conns = 2; current pipes = 0/0; conn rate = 2/sec; bit rate = 0.000 kbps

运行任务:

  • Running tasks:运行的任务/当前空闲率

示例: Running tasks: 1/14; idle = 100%

服务器状态:

  • active UP:在线服务器

  • backup UP:标记为backup的服务器

  • active UP, going down:监测未通过正在进入down过程

  • backup UP, going down:备份服务器正在进入down过程

  • active DOWN, going up:down的服务器正在进入up过程

  • backup DOWN, going up:备份服务器正在进入up过程

  • active or backup DOWN:在线的服务器或者是backup的服务器已经转换成了down状态

  • not checked:标记为不监测的服务器

  • active or backup DOWN for maintenance (MAINT):active或者backup服务器人为下线的

  • active or backup SOFT STOPPED for maintenance:active或者backup被人为软下线(人为将weight改成0)

5.2.4 backend server信息

Session rate(每秒的连接会话信息):

  • cur:每秒的当前会话数量

  • max:每秒新的最大会话数量

  • limit:每秒新的会话限制量

Errors(错误统计信息):

  • Req:错误请求量

  • Conn:错误链接量

  • Resp:错误响应量

Sessions(会话信息):

  • cur:当前会话量

  • max:最大会话量

  • limit:限制会话量

  • Total:总共会话量

Warnings(警告统计信息):

  • Retr:重新尝试次数

  • Redis:再次发送次数

Server(real server信息):

  • LBTot:选中一台服务器所用的总时间

  • Status:后端机的状态,包括UP和DOWN

  • Last:和服务器的持续连接时间

  • LastChk:持续检查后端服务器的时间

  • Wght:权重

  • Act:活动链接数量

  • Bck:备份的服务器数量

  • Chk:心跳检测时间

  • Dwn:后端服务器连接后都是DOWN的数量

  • Dwntime:总的downtime时间

  • Thrtle:server状态

Bytes(流量统计):

  • In:网络的字节输入总量

  • Out:网络的字节输出总量

Denied(拒绝统计信息):

  • Req:拒绝请求量

  • Resp:拒绝回复量

5.3 IP透传

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

5.3.1 四层IP透传

未开启透传的四层代理配置:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode tcp
    balance roundrobin
    server webserver1 192.168.0.101:80 weight 1 check inter 3s fall 3 rise 5

正常的nginx配置:

复制代码
http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    server {
        listen 80;
        listen [::]:80;
        server_name _;
        root /usr/share/nginx/html;
        # 内容省略
    }
}

在访问haproxy后查看nginx日志:

复制代码
tail -n 3 /var/log/nginx/access.log

输出示例:

复制代码
192.168.0.10 - - [10/jul/2024:15:21:00 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "-"
192.168.0.10 - - [10/jul/2024:15:26:11 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "-"

在此日志中是无法看到真实访问源地址的。

5.3.2 开启四层透传

nginx配置: 在访问日志中通过变量$proxy_protocol_addr记录透传过来的客户端IP

复制代码
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"';
    
    server {
        listen 80 proxy_protocol;  # 启用此项,将无法直接访问此网站,只能通过四层代理访问
        listen [::]:80;
        server_name _;
        root /usr/share/nginx/html;
        # 内容省略
    }
}

修改haproxy配置:

复制代码
listen webserver_80
    bind 172.25.254.100:80
    mode tcp
    balance roundrobin
    server webserver1 192.168.0.101:80 send-proxy weight 1 check inter 3s fall 3 rise 5

查看日志内容:

复制代码
tail -n 3 /var/log/nginx/access.log

输出示例:

复制代码
192.168.0.10 - - [10/jul/2024:15:21:00 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "-"
192.168.0.10 - - [10/jul/2024:15:26:11 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "-"
192.168.0.10 - - [10/jul/2024:15:41:56 +0800] "GET / HTTP/1.1" "172.25.254.10" 200 18 "-" "curl/7.29.0"
5.3.3 七层IP透传

当haproxy工作在七层的时候,也可以透传客户端真实IP至后端服务器。

5.3.3.1 HAProxy配置

在由haproxy发往后端主机的请求报文中添加"X-Forwarded-For"首部,其值为前端客户端的地址;用于向后端主发送真实的客户端IP。

复制代码
option forwardfor [except <network>] [header <name>] [if-none]
  • [except <network>]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络

  • [header <name>]:使用自定义的首部名称,而非"X-Forwarded-For",示例:X-client

  • [if-none]:如果没有首部才添加首部,如果有使用默认值

示例:

复制代码
listen webserver_80
    option forwardfor
    bind 172.25.254.100:80
    mode http
    balance roundrobin
    server webserver1 192.168.0.101:80 send-proxy weight 1 check inter 3s fall 3 rise 5
    server webserver2 192.168.0.102:80 weight 1 check inter 3s fall 3 rise 5
5.3.3.2 web服务器日志格式配置

配置web服务器,记录负载均衡透传的客户端IP地址。

Apache配置:

复制代码
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

Nginx日志格式:

复制代码
log_format main '$proxy_add_x_forwarded_for - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" $http_x_forwarded_for';

查看日志:

复制代码
# Nginx日志
[root@rs1 ~]# tail -n 3 /var/log/nginx/access.log
"172.25.254.10, 192.168.0.10" 192.168.0.10 - - [10/jul/2024:16:15:00 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "172.25.254.10"

# Apache日志
[root@rs2 ~]# tail -n 3 /etc/httpd/logs/access_log
172.25.254.10 192.168.0.10 - - [11/jul/2024:00:15:00 +0800] "GET / HTTP/1.1" 200 27 "-" "curl/7.29.0"

5.4 ACL(访问控制列表)

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

示例:

复制代码
frontend test_ac1
    bind *:80
    mode http
    # acl bad_browser hdr_beg(User-Agent) -i curl
    # http-request deny if bad_browser
    
    # acl test hdr_dom(host) -i www.timinglee.org
    # acl test hdr_end(host) -i .org
    # acl test base_sub -m sub org
    # acl test path_sub -m sub /a
    # acl test path_end -m sub /a
    # acl test path_reg -i /\t
    acl test url_sub -m sub lee
    acl test path_dir -m sub a
    use_backend test_web if test
    
    default_backend default_webserver

backend default_webserver
    mode http
    server web1 172.25.254.20:80 check inter 3 fall 3 rise 5

backend test_web
    mode http
    server web2 172.25.254.30:80 check inter 3 fall 3 rise 5
5.4.1 ACL配置选项

ACL定义语法:

复制代码
acl <aclname> <criterion> [flags] [operator] [<value>]
  • aclname:ACL名称

  • criterion:匹配规范

  • flags:匹配模式

  • operator:具体操作符

  • value:操作对象类型

5.4.1.1 ACL-Name名称

示例:acl test path_end -m sub /a

ACL名称,可以使用大写字母A-Z、小写字母a-z、数字0-9、冒号:、点.、中横线-和下划线_,并且严格区分大小写,比如:my_aclMy_acl就是两个完全不同的acl。

5.4.1.2 ACL-criterion匹配规范

定义ACL匹配规范,即:判断条件。

hdr string:提取在一个HTTP请求报文的首部

  • hdr([<name>[,<occ>]]):完全匹配字符串,header的指定信息,<occ>表示在多值中使用的值出现次数

  • hdr_beg([<name>[,<occ>]]):前缀匹配,header中指定匹配内容的begin

  • hdr_end([<name>[,<occ>]]):后缀匹配,header中指定匹配内容end

  • hdr_dom([<name>[,<occ>]]):域匹配,header中的dom(host)

  • hdr_dir([<name>[,<occ>]]):路径匹配,header的uri路径

  • hdr_len([<name>[,<occ>]]):长度匹配,header的长度匹配

  • hdr_reg([<name>[,<occ>]]):正则表达式匹配,自定义表达式(regex)模糊匹配

  • hdr_sub([<name>[,<occ>]]):子串匹配,header中的uri模糊匹配模糊匹配

示例:

复制代码
# hdr(<string>)用于测试请求头部首部指定内容
hdr_dom(host)      # 请求的host名称,如www.timinglee.org
hdr_beg(host)      # 请求的host开头,如www. img. video. download. ftp.
hdr_end(host)      # 请求的host结尾,如.com .net .cn

# 示例:
acl bad_agent hdr_sub(User-Agent) -i curl wget
http-request deny if bad_agent

# 有些功能是类似的,比如以下几个都是匹配用户请求报文中host的开头是不是www
acl short_form hdr_beg(host) www.
acl alternate1 hdr_beg(host) -m beg www.
acl alternate2 hdr_dom(host) -m beg www.
acl alternate3 hdr(host) -m beg www.

base : string:返回第一个主机头和请求的路径部分的连接,该请求从主机名开始,并在问号之前结束,对虚拟主机有用

复制代码
<scheme>://<user>:<password>@<host>:<port></path>;<params>?<query>#<frag>
  • base:exact string match

  • base_beg:prefix match

  • base_dir:subdir match

  • base_dom:domain match

  • base_end:suffix match

  • base_len:length match

  • base_reg:regex match

  • base_sub:substring match

path : string:提取请求的URL路径,该路径从第一个斜杠开始,并在问号之前结束(无主机部分)

复制代码
<scheme>://<user>:<password>@<host>:<port></path>;<params>?<query>#<frag>
  • path:exact string match

  • path_beg:prefix match(请求的URL开头,如/static、/images、/img、/css)

  • path_end:suffix match(请求的URL中资源的结尾,如.gif .png .css .js .jpg .jpeg)

  • path_dom:domain match

  • path_dir:subdir match

  • path_len:length match

  • path_reg:regex match

  • path_sub:substring match

示例:

复制代码
path_beg -i /haproxy-status/
path_end .jpg .jpeg .png .gif
path_reg /images.*\.jpeg
path_sub image
path_dir jpegs
path_dom timinglee

url : string:提取请求中的整个URL

  • url:exact string match

  • url_beg:prefix match

  • url_dir:subdir match

  • url_dom:domain match

  • url_end:suffix match

  • url_len:length match

  • url_reg:regex match

  • url_sub:substring match

其他匹配规范:

  • dst:目标IP

  • dst_port:目标PORT

  • src:源IP

  • src_port:源PORT

  • status:integer,返回在响应报文中的状态码

示例:

复制代码
acl invalid_src src 10.0.0.7 192.168.1.0/24
acl invalid_src src 172.16.0.0/24
acl invalid_port src_port 0:1023

# 七层协议
acl valid_method method GET HEAD
http-request deny if !valid_method
5.4.1.3 ACL-flags 匹配模式
  • -i:不区分大小写

  • -m:使用指定的正则表达式匹配方法

  • -n:不做DNS解析

  • -u:禁止acl重名,否则多个同名ACL匹配或关系

5.4.1.4 ACL-operator 具体操作符

整数比较: eq、ge、gt、le、lt

字符比较:

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

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

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

  • suffix match (-m end):将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配

  • subdir match (-m dir):查看提取出来的用斜线分隔("/")的字符串,如其中任一个匹配,则ACL进行匹配

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

5.4.1.5 ACL-value 操作对象

value的类型:

  • Boolean:布尔值

  • integer or integer range:整数或整数范围,比如用于匹配端口范围

  • IP address / network:IP地址或IP范围,192.168.0.1,192.168.0.1/24

  • string:字符串

    • exact:精确比较

    • substring:子串

    • suffix:后缀比较

    • prefix:前缀比较

    • subdir:路径,/wp-includes/js/jquery/jquery.js

    • domain:域名,www.timinglee.org

  • regular expression:正则表达式

  • hex block:16进制

5.4.2 多个ACL的组合调用方式

多个ACL的逻辑处理:

  • :隐式(默认)使用

  • :使用"or"或"||"表示

  • 否定:使用"!"表示

示例:

复制代码
if valid_src valid_port        # 与关系,ACL中A和B都要满足为true,默认为与
if invalid_src || invalid_port # 或,ACL中A或者B满足一个为true
if ! invalid_src               # 非,取反,不满足ACL才为true
5.4.3 ACL示例-域名匹配

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ########### ACL settings #######################
    acl web_host hdr_dom(host) www.timinglee.org
    ########### host ###########################
    use_backend timinglee_host if web_host
    ########### default server ###################
    default_backend default_webserver

backend timinglee_host
    mode http
    server web1 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5
    server web2 192.168.0.102:80 check weight 1 inter 3s fall 3 rise 5

backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

在浏览器所在主机中做地址解析:

复制代码
vim /etc/hosts
# 添加
172.25.254.100 www.timinglee.org

测试结果:

复制代码
[root@node10 html]# curl www.timinglee.org
RS1 192.168.0.101
[root@node10 html]# curl www.timinglee.org
RS2 server - 192.168.0.102
[root@node10 html]# curl 172.25.254.100
default web server node10
5.4.4 ACL示例-基于源IP或子网调度访问

将指定的源地址调度至指定的web服务器组。

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ######## ACL settings ##############
    acl ip_test src 172.25.254.1 192.168.0.0/24
    ######## host ##############
    use_backend ip_test-host if ip_test
    ######## default server ##############
    default_backend default_webserver

backend ip_test-host
    mode http
    server web1 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5

backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

测试结果:

复制代码
[172.25.254.10 root@node10 html]# curl 172.25.254.100
default web server node10

[172.25.254.1 Administrator.WIN-20240602BIS] > curl 172.25.254.100
RS1 192.168.0.101

[192.168.0.102 root@rs1 ~]# curl 192.168.0.101
RS1 192.168.0.101
5.4.5 ACL示例-基于源地址的访问控制

拒绝指定IP或者IP范围访问。

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ########### ACL settings #######################
    acl web_host hdr_dom(host) www.timinglee.org
    acl ip_test src 172.25.254.1 192.168.0.0/24
    ########### host ###########################
    http-request deny if web_host
    ########### default server ###################
    default_backend default_webserver

backend ip_test-host
    mode http
    server web1 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5

backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

测试:

复制代码
[root@node10 ~]# curl www.timinglee.org
<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>

[root@node10 ~]# curl 172.25.254.100
default web server node10
5.4.6 ACL示例-匹配浏览器类型

匹配客户端浏览器,将不同类型的浏览器调度至不同的服务器组。

范例: 拒绝curl和wget的访问

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ########### ACL settings #######################
    acl user_agent_block hdr_sub(User-Agent) -i curl wget
    acl user_agent_redirect hdr_sub(User-Agent) -i Mozilla/5.0
    ########### host ###########################
    http-request deny if user_agent_block
    redirect prefix https://www.baidu.com if user_agent_redirect
    ########### default server ###################
    default_backend default_webserver

backend ip_test-host
    mode http
    server web1 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5

backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

测试:

复制代码
1. [root@node10 ~]# curl 172.25.254.100
<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>

2. [root@node10 ~]# wget http://172.25.254.100/index.html
--2024-07-11 23:04:36--  http://172.25.254.100/index.html
Connecting to 172.25.254.100:80... connected.
HTTP request sent, awaiting response... 403 Forbidden
2024-07-11 23:04:36 ERROR 403: Forbidden.

3. 浏览器中测试(会重定向到百度)
5.4.7 ACL示例-基于文件后缀名实现动静分离

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ########### ACL settings ###########
    acl url_static path_end -i .jpg .png .css .js .html
    acl url_php path_end -i .php
    ########### host ###########
    use_backend static_host if url_static
    use_backend php_host if url_php
    ########### default server ###########
    default_backend default_webserver
    
backend static_host
    mode http
    server web2 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5
    
backend php_host
    mode http
    server web1 192.168.0.102:80 check weight 1 inter 3s fall 3 rise 5
    
backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

测试:

复制代码
# 在后端服务器创建测试文件
[root@rs1 ~]# echo "css 192.168.0.101" > /usr/share/nginx/html/index.css
[root@rs2 ~]# echo "php 192.168.0.102" > /var/www/html/index.php

# 测试访问
[root@node10 ~]# curl 172.25.254.100/index.php
php 192.168.0.102
[root@node10 ~]# curl 172.25.254.100/index.css
css 192.168.0.101
5.4.8 ACL-匹配访问路径实现动静分离

配置示例:

复制代码
frontend testacl
    bind :80
    mode http
    
    ########### ACL settings ###########
    acl url_static path_end -i .jpg .png .css .js .html
    acl url_static path_end -m sub /static /images /javascript
    acl acl_app path_beg -m sub /api
    ########### host ###########
    use_backend static_host if url_static
    use_backend api_host if acl_app
    ######## default server ###########
    default_backend default_webserver
    
backend static_host
    mode http
    server web2 192.168.0.101:80 check weight 1 inter 3s fall 3 rise 5
    
backend api_host
    mode http
    server web1 192.168.0.102:80 check weight 1 inter 3s fall 3 rise 5
    
backend default_webserver
    mode http
    server web1 172.25.254.10:80 check weight 1 inter 3s fall 3 rise 5

创建相关文件:

复制代码
[root@rs1 ~]# mkdir /usr/share/nginx/html/static
[root@rs1 ~]# echo "static 192.168.0.101" > /usr/share/nginx/html/static/index.html
[root@rs2 ~]# mkdir /var/www/html/api/
[root@rs2 ~]# echo "api 192.168.0.102" > /var/www/html/api/index.html

测试访问:

复制代码
[root@node10 ~]# curl 172.25.254.100/api/
api 192.168.0.102
[root@node10 ~]# curl 172.25.254.100/static/
static 192.168.0.101

5.5 自定义HAProxy错误界面

对指定的报错进行重定向,进行优雅的显示错误页面。使用errorfile和errorloc指令的两种方法,可以实现自定义各种错误页面。

haproxy默认使用的错误页面:

复制代码
[root@haproxy ~]# rpm -ql haproxy24z-2.4.27-1.el7.zenetys.x86_64 | grep -E http$
/usr/share/haproxy/400.http
/usr/share/haproxy/403.http
/usr/share/haproxy/408.http
/usr/share/haproxy/500.http
/usr/share/haproxy/502.http
/usr/share/haproxy/503.http
/usr/share/haproxy/504.http
5.5.1 基于自定义的错误页面文件

语法:

复制代码
errorfile <code> <file>
  • <code>:HTTP status code,支持200,400,403,405,408,425,429,500,502,503,504

  • <file>:包含完整HTTP响应头的错误页文件的绝对路径。建议后缀".http",以和一般的html文件相区分

示例:

复制代码
errorfile 503 /haproxy/errorsages/503page.http

完整配置示例:

复制代码
defaults
    mode http
    # ...内容省略...
    timeout client 1m
    timeout server 1m
    timeout http-keep-alive 10s
    timeout check 10s
    maxconn 1000000
    
    errorfile 503 /haproxy/errorsages/503page.http

创建错误页面文件:

复制代码
mkdir /haproxy/errorsages/ -p
cp /usr/share/haproxy/503.http /haproxy/errorsages/503page.http
vim /haproxy/errorsages/503page.http

错误页面内容:

复制代码
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache
Connection: close
Content-Type: text/html; charset=UTF-8

<html><body><h1>什么动物生气最安静</h1>大猩猩!!</body></html>

测试: 关闭后端的RS主机,然后用浏览器去访问172.25.254.100

5.5.2 基于http重定向错误页面

语法:

复制代码
errorloc <code> <url>

相当于errorloc302 <code> <url>,利用302重定向至指定URL。

示例:

复制代码
errorloc 503 https://www.baidu.com

范例:

复制代码
defaults
    mode http
    # ...内容省略...
    timeout client 1m
    timeout server 1m
    timeout http-keep-alive 10s
    timeout check 10s
    maxconn 1000000
    
    errorloc 503 https://www.baidu.com

测试: 浏览器访问172.25.254.100自动跳转到百度。

5.6 HAProxy四层负载

针对除HTTP以外的TCP协议应用服务访问的应用场景:MySQL、Redis、Memcache、RabbitMQ等。

注意: 如果使用frontend和backend,一定在frontend和backend段中都指定mode tcp。

四层负载示例:

复制代码
listen mysql-port
    bind 10.0.0.7:6379
    mode tcp
    balance leastconn
    server server1 10.0.0.17:3306 check
    server server2 10.0.0.27:3306 check backup
5.6.1 对MySQL服务实现四层负载

使用listen方式:

复制代码
listen mysql_port
    bind :3306
    mode tcp
    balance leastconn
    server mysql1 192.168.0.101:3306 check
    server mysql2 192.168.0.102:3306 check

或者使用frontend和backend实现:

复制代码
frontend mysql_port
    bind :3306
    mode tcp
    use_backend mysql_rss

backend mysql_rss
    mode tcp
    balance leastconn
    server mysql1 192.168.0.101:3306 check
    server mysql2 192.168.0.102:3306 check

重启服务:

复制代码
systemctl restart haproxy.service

在后端服务器安装和配置mariadb服务:

复制代码
# 在rs1和rs2上执行
yum install mariadb-server -y

# rs1配置
vim /etc/my.cnf
[mysqld]
server-id=1

# rs2配置
vim /etc/my.cnf
[mysqld]
server-id=2

# 启动服务
systemctl start mariadb

# 授权
mysql -e "grant all on *.* to lee@'%' identified by 'lee';"

测试:

复制代码
[root@node10 ~]# mysql -ulee -plee -h 172.25.254.100 -e "show variables like 'hostname'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| hostname      | rs2   |
+---------------+-------+

[root@node10 ~]# mysql -ulee -plee -h 172.25.254.100 -e "show variables like 'hostname'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| hostname      | rs1   |
+---------------+-------+

[root@node10 ~]# mysql -ulee -plee -h172.25.254.100 -e "select @@server_id"
+-------------+
| @@server_id |
+-------------+
|           1 |
+-------------+

[root@node10 ~]# mysql -ulee -plee -h172.25.254.100 -e "select @@server_id"
+-------------+
| @@server_id |
+-------------+
|           2 |
+-------------+

5.7 HAProxy HTTPS实现

haproxy可以实现https的证书安全,从用户到haproxy为https,从haproxy到后端服务器用http通信。但基于性能考虑,生产中证书都是在后端服务器比如nginx上实现。

配置HAProxy支持https协议,支持ssl会话:

复制代码
bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE

指令crt后证书文件为PEM格式,需要同时包含证书和所有私钥。

合并证书和私钥:

复制代码
cat demo.key demo.crt > demo.pem

把80端口的请求重定向到443:

复制代码
bind *:80
redirect scheme https if !{ ssl_fc }
5.7.1 证书制作
复制代码
mkdir /etc/haproxy/certs/
openssl req -newkey rsa:2048 \
    -nodes -sha256 -keyout /etc/haproxy/certs/timinglee.org.key \
    -x509 -days 365 -out /etc/haproxy/certs/timinglee.org.crt
5.7.2 HTTPS配置示例
复制代码
frontend webserver
    bind *:80
    redirect scheme https if !{ ssl_fc }
    mode http
    use_backend webcluster

frontend webserver-https
    bind *:443 ssl crt /etc/haproxy/timinglee.org.pem
    mode http
    use_backend webcluster

backend webcluster
    mode http
    balance roundrobin
    server web1 172.25.254.200:80 check inter 3s fall 3 rise 5
    server web2 172.25.254.201:80 check inter 3s fall 3 rise 5

测试:

复制代码
[root@客户端 ~]# curl -IkL http://172.25.254.100
HTTP/1.1 302 Found
content-length: 0
location: https://www.timinglee.org/
cache-control: no-cache
HTTP/1.1 200 OK
date: Sat, 04 Apr 2020 02:31:31 GMT
server: Apache/2.4.6 (CentOS) PHP/5.4.16
last-modified: Thu, 02 Apr 2020 01:44:13 GMT
etag: "a-5a244f01f8adc"
accept-ranges: bytes
content-length: 10
content-type: text/html; charset=UTF-8

[root@centos6 ~]# curl -Ik https://www.timinglee.org
HTTP/1.1 200 OK
date: Sat, 04 Apr 2020 02:31:50 GMT
server: Apache/2.4.6 (CentOS) PHP/5.4.16
last-modified: Thu, 02 Apr 2020 01:44:28 GMT
etag: "a-5a244f0fd5175"
accept-ranges: bytes
content-length: 10
content-type: text/html; charset=UTF-8

六、总结

HAProxy是一个功能强大、性能优异的高可用负载均衡器,支持四层和七层负载均衡,具有丰富的调度算法和高级功能。通过合理的配置,可以满足各种复杂的业务场景需求,包括:

  1. 高可用性:通过健康检查、故障切换等机制保证服务的高可用

  2. 高性能:支持高并发连接,性能强劲

  3. 灵活性:支持多种调度算法,可根据业务需求灵活选择

  4. 安全性:支持ACL访问控制、IP透传、HTTPS等安全功能

  5. 可扩展性:支持动态水平扩展,方便业务扩展

  6. 易管理性:提供状态监控页面,便于运维管理

相关推荐
阿里云云原生19 小时前
巨人网络《超自然行动组》携手阿里云打造云原生游戏新范式
云原生
71ber1 天前
深入理解 HAProxy:四层/七层透传与高级 ACL 调度详解
linux·云原生·haproxy
A-刘晨阳1 天前
K8S 之 DaemonSet
运维·云原生·容器·kubernetes·daemonset
2501_948114241 天前
资深程序员真实测评:9家中转API平台实战横评
微服务·云原生·架构
切糕师学AI1 天前
Kubernetes 中的 Volume(存储卷)
云原生·容器·kubernetes
AI_56781 天前
Terraform多环境部署:模块化配置一次搞定
云原生·terraform
DeepFlow 零侵扰全栈可观测1 天前
使用 eBPF 零代码修改绘制全景应用拓扑
java·前端·网络·分布式·微服务·云原生·云计算
ghostwritten1 天前
worker01 NotReady 排查与修复步骤
云原生·kubernetes
予枫的编程笔记1 天前
【Docker基础篇】Docker入门必看:镜像、容器、仓库的关系,用类比讲得明明白白
docker·云原生·后端开发·docker架构·docker入门·镜像与容器·程序员干货
xixiyuguang2 天前
解决Docker启动后自动生成docker0虚拟网卡(172.17.0.1)及启动失败问题
云原生·eureka