HAProxy

本文最后更新于:2023年12月5日 晚上

web 架构介绍

单机房架构

多机房架构

公有云架构

私有云架构

负载均衡

负载均衡:Load Balance,简称 LB

负载均衡类型

  • 四层(传输层):LVS、Nginx(1.9 之后)、HAProxy

    LVS 是集成在内核中的功能,真四层;Nginx 和 HAProxy 是用户空间的软件,伪四层

  • 七层(应用层):Nginx、HAProxy

  • 硬件:F5NetscalerArray深信服北京灵州

    大公司都用 F5

应用场景

企业生产环境中,每天会有很多的需求变更,比如增加服务器、新业务上线、url 路由修改、域名配置等等,对于前端负载均衡设备来说,容易维护,复杂度低,是首选指标

LVS 性能最高,最稳定,但是只支持四层负载 ,百万级并发

HAProxy 四层负载和七层代理都支持,如果只做反向代理,推荐 HAProxy,功能丰富,方便管理,万级并发

Nginx 的功能基于模块实现,HAProxy 有的功能 Nginx 都有,小型系统可以使用 Nginx

HAProxy

C 语言开发,高性能的 TCP 和 HTTP 负载均衡器,支持基于 cookie 的持久性,自动故障切换,支持正则表达式及 web 状态统计,目前最新 TLS 版本为 2.2

分为企业版和社区版

  • TCP 和 HTTP 反向代理
  • SSL/TSL 服务器
  • 可以针对 HTTP 请求添加 cookie,进行路由后端服务器
  • 可平衡负载至后端服务器,并支持持久连接
  • 支持所有主服务器故障切换至备用服务器
  • 支持专用端口实现监控服务
  • 支持停止接受新连接请求,而不影响现有连接
  • 可以在双向添加,修改或删除 HTTP 报文首部
  • 响应报文压缩
  • 支持基于 pattern 实现连接请求的访问控制
  • 通过特定的 URI 为授权用户提供详细的状态信息

HAProxy 安装

yum 或者 apt 安装的版本较老,推荐编译安装

HAProxy 支持基于 Lua 实现功能扩展

编译安装 Lua

wget http://www.lua.org/ftp/lua-5.4.2.tar.gz
mkdir /usr/local/lua
tar zxvf lua-5.4.2.tar.gz -C /usr/local/lua --strip-components 1
cd /usr/local/lua
make all test
./src/lua -v # Lua 5.4.2  Copyright (C) 1994-2020 Lua.org, PUC-Rio

编译安装 HAProxy

http://www.haproxy.org/ # 优先下载 LTS 版

http://www.haproxy.org/download/

wget http://www.haproxy.org/download/2.2/src/haproxy-2.2.6.tar.gz
yum -y install gcc openssl-devel pcre-devel systemd-devel
mkdir /usr/local/haproxy
tar zxvf haproxy-2.2.6.tar.gz
cd haproxy-2.2.6/
# 参考INSTALL文件进行编译安装
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/
make install PREFIX=/usr/local/haproxy
vim /etc/profile # export PATH=/usr/local/haproxy/sbin:$PATH
. /etc/profile
haproxy -v
haproxy -V
haproxy -vv
apt install make gcc build-essential libssl-dev zlib1g-dev libpcre3 libpcre3-dev libsystemd-dev libreadline-dev -y

配置文件

查看配置文件示例:

[root@c71 ~]$tree /usr/local/src/haproxy-2.2.6/examples/
/usr/local/src/haproxy-2.2.6/examples/
├── acl-content-sw.cfg
├── content-sw-sample.cfg
├── errorfiles
│   ├── 400.http
│   ├── 403.http
│   ├── 408.http
│   ├── 500.http
│   ├── 502.http
│   ├── 503.http
│   ├── 504.http
│   └── README
├── haproxy.init
├── option-http_proxy.cfg
├── socks4.cfg
├── transparent_proxy.cfg
└── wurfl-example.cfg

1 directory, 15 files

创建自定义的配置文件:

HAProxy 的配置文件 haproxy.cfg 由两大部分组成,分别是 global 和 proxies 部分

  • global:全局配置段

    进程及安全配置相关的参数、性能调整相关参数、Debug 参数

  • proxies:代理配置段

    • defaults [<name>]:默认配置项,针对以下的 frontend、backend 和 listen 生效,可以多个 name 也可以没有 name
    • frontend <name>:前端,类似于 Nginx 的 server 和 LVS 的服务集群
    • backend <name>:后端,相当于 nginx 的 upstream 和 LVS 中的 RS
    • listen <name>:同时拥有前端和后端配置,配置简单,通常只用于 TCP 协议的应用,适合简单的环境,生产推荐使用

以下列举的是部分配置,官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html

global

[root@c71 ~]$mkdir /var/lib/haproxy
[root@c71 ~]$mkdir /etc/haproxy
[root@c71 ~]$vim /etc/haproxy/haproxy.cfg
global
    chroot /usr/local/haproxy # 锁定运行目录
    stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin  # socket文件
    #uid 99
    #gid 99
    user haproxy  # 运行haproxy的用户
    group haproxy  # 运行haproxy的用户组
    daemon
    nbproc 1   # 启的haproxy work 进程数,默认进程数是一个
    # nbthread 1  # 指定每个haproxy进程开启的线程数,默认为每个进程一个线程,和nbproc配置互斥
    #cpu-map 1 0  # 绑定haproxy worker 进程至指定CPU,将第1个work进程绑定至0号CPU
    #cpu-map 2 1  # 绑定haproxy worker 进程至指定CPU,将第2个work进程绑定至1号CPU
    #cpu-map 3 2
    #cpu-map 4 3
    maxconn 100000  # 每个haproxy进程的最大并发连接数
    # maxsslconn n  # 每个haproxy进程ssl最大连接数,用于haproxy配置了证书的场景下
    # maxconnrate n  # 每个进程每秒创建的最大连接数量
    # spread-checks n # 后端server状态check随机提前或延迟百分比时间,建议2-5(20%-50%)之间,默认0
    pidfile /var/lib/haproxy/haproxy.pid # 指定pid文件路径
    # log 127.0.0.1 local2 info   # 全局的syslog服务器;日志服务器需要开启UDP协议,最多可以定义两个

HAproxy 本身不记录客户端的访问日志,此外为减少服务器负载,一般生产中 HAProxy 不记录日志

defaults

defaults
    option redispatch  # 当server Id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发
    option abortonclose  # 服务器负载很高时,自动结束当前队列处理比较久的连接,针对业务情况选择开启
    option http-keep-alive # 开启与客户端的会话保持
    option forwardfor  # 透传客户端真实IP至后端web服务器
    maxconn 100000
    mode http    # http|tcp,设置默认工作类型,使用TCP服务器性能更好,减少压力
    timeout connect 120s # 客户端请求从haproxy到后端server最长连接等待时间(TCP连接之前),默认单位ms
    timeout server 120s # 客户端请求从haproxy到后端服务端的请求处理超时时长(TCP连接之后),默认单位ms,如果超时,会出现502错误,此值建议设置较大些,访止502错误
    timeout client 120s # 设置haproxy与客户端的最长非活动时间,默认单位ms,建议和timeoutserver相同
    timeout check 5s # 对后端服务器的默认检测超时时间
    default-server inter 1000 weight 3 # 指定后端服务器的默认设置

default-server 指定后端服务器的默认配置,以下是主要配置项:

# 对指定real server进行健康状态检查,默认不开启检查,只有check后面没有其它配置也可以启用检查功能
# 默认对相应的后端服务器IP和端口,利用TCP连接进行周期性健康性检查,注意必须指定端口才能实现健康性检查
check
    addr <IP>  # 健康状态监测IP,可以是专门的数据网段,减少业务网络的流量
    port <num>  # 健康状态监测端口
    inter <num>  # 健康状态检查间隔时间,默认2000 ms
    fall <num>  # 后端服务器从线上转为线下的检查的连续失效次数,默认为3
    rise <num>  # 后端服务器从下线恢复上线的检查的连续有效次数,默认为2
# 动态修改权重,默认为1,最大值为256,0(状态为蓝色)表示不参与负载均衡,但仍接受持久连接
weight <weight>
backup   # 将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务,类似Sorry Server
disabled  # 将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接,状态为深黄色
redirect prefix http://www.baidu.com/ # 302重定向
redir http://www.baidu.com    # 同上,区别是最后不加"/"
maxconn <maxconn>       # 当前后端server的最大并发连接数

注意:HAProxy server 的 weight 和 Nginx server 的 weight 表示的意义是一样的,和 Keepalived real_server 的 weight 表示的意义不一样

frontend

frontend magedu_web_port # 形式命名:业务-服务-端口号
    # 指定HAProxy的监听地址,可以是IPV4或IPV6,可以同时监听多个IP或端口,可同时用于listen字段中
    # bind [<address>]:<port_range> [, ...] [param*],address省略则等于0.0.0.0
    # 注意:如果需要绑定在非本机的IP,例如vip,需要开启内核参数:net.ipv4.ip_nonlocal_bind=1
    bind :80,:8080
    bind 10.0.0.7:10080,:8801-8810,10.0.0.17:9001-9010
    mode http|tcp  # 指定负载协议类型
    use_backend <backend_name>  # 调用的后端服务器组名称
    # 针对所有server配置,当前端服务器的连接数达到上限后的后援队列长度,注意:不支持backend
    backlog <backlog>

backend

backend magedu-test-http-nodes
    mode tcp # 指定负载协议类型,和对应的frontend必须一致
    default-server inter 1000 weight 6
    # server name address:port param* 为后端真实服务器指定一个内部名称,随便写一个即可
    server web1 10.0.0.27:80 check # 定义后端real server,必须指定IP和端口
    server web1 10.0.0.17:80 weight 2 check addr 10.0.0.117 port 8080

listen

listen web_port
    mode http
    bind 10.0.0.71:80
    log global # 开启日志功能,默认不记录日志
    server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5

listen stats # stats相关:状态页配置项
    mode http
    bind 0.0.0.0:9999
    log global
    stats enable # 于默认的参数启用stats page
    stats uri /haproxy-status # 自定义stats page uri,默认值:/haproxy?stats
    stats auth admin:123456  # 认证时的账号和密码,可定义多个用户,每行指定一个用户

使用子配置文件保存配置

按业务分类,将配置信息拆分,放在不同的子配置文件中,从而达到方便维护的目的

注意:配置文件必须为 cfg 后缀非.开头

mkdir /etc/haproxy/conf.d/

因为没有类似include的指令引入子配置,所以要在启动命令上指定子配置文件

systemctl 启动文件

[root@c71 ~]$vim /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target

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

[Install]
WantedBy=multi-user.target

[root@c71 ~]$useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy

HAProxy 调度算法

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-balance

HAProxy 的调度算法分为静态和动态两种:

  • 静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时修改权重(只能为 0 和 1,不支持其它值),只能靠重启 HAProxy 生效
  • 动态算法:基于后端服务器状态进行调度适当调整,优先调度至当前负载较低的服务器,且权重可以 haproxy 运行时动态调整无需重启

静态算法

static-rr

基于权重的轮询调度,不支持运行时利用 socat 进行权重的动态调整(只支持 0 和 1)及后端服务器慢启动,其后端主机数量没有限制,相当于 LVS 中的 wrr

listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode http
    log global
    balance static-rr
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5

first

根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务,因此会忽略服务器的权重设置,此方式使用较少

不支持用 socat 进行动态修改权重,可以设置 0 和 1

动态算法

socat 工具

Socat 是 Linux 下的一个多功能的网络工具,名字来由是 「Socket CAT」。其功能与有瑞士军刀之称的 Netcat 类似,可以看做是 Netcat 的加强版。

Socat 的主要特点就是在两个数据流之间建立通道,且支持众多协议和链接方式。如 IP、TCP、 UDP、IPv6、PIPE、EXEC、System、Open、Proxy、Openssl、Socket 等。

这里利用 socat 工具,对服务器动态权重和其它状态进行调整

socat stdio:对文件进行标准写入和标准读取

# 查看HAProxy帮助
echo "help" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看HAProxy信息
echo "show info" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看所有server的各种状态
echo "show servers state" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看指定server的weight
echo "get weight magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 修改指定server的weight,如果静态算法,如:static-rr,可以更改weight为0或1,但不支持动态更改为其它值
echo "set weight magedu-test-80/web2 2" | socat stdio /var/lib/haproxy/haproxy.sock
# 将指定后端服务器禁用
echo "disable server magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 启用指定后端服务器
echo "enable server magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 将后端服务器软下线,即weight设为0
echo "set weight magedu-test-80/web1 0" | socat stdio /var/lib/haproxy/haproxy.sock

一个进程就有一个 haproxy.socket 文件,以上的操作针对的都是单进程

roundrobin

基于权重的轮询动态调度算法,支持权重的运行时调整,不同于 lvs 中的 rr 轮训模式,HAProxy 中的 roundrobin 支持慢启动(新加的服务器会逐渐增加转发数),其每个后端 backend 中最多支持 4095 个 real server,支持对 real server 权重动态调整,roundrobin 为默认调度算法,此算法使用广泛

支持动态调整权重:

echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
1 (initial 1)
echo "set weight web_host/web1 3" | socat stdio /var/lib/haproxy/haproxy.sock
echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
3 (initial 1)

leastconn

leastconn 加权的最少连接的动态,支持权重的运行时调整和慢启动,即根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接),比较适合长连接的场景使用,比如:MySQL 等场景

random

1.9 版本增加,负载平衡算法,其基于随机数作为一致性 hash 的 key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用,支持 weight 的动态调整,weight 较大的主机有更大概率获取新请求

其他算法

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

source

源地址 hash,基于用户源地址 hash 并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端 web 服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过 hash-type 支持的选项更改

这个算法一般是在不插入 Cookie 的 TCP 模式下使用,也可给拒绝会话 cookie 的客户提供最好的会话粘性,适用于 session 会话保持但不支持 cookie 和缓存的场景

源地址有两种转发客户端请求到后端服务器的服务器选取计算方式:取模法、一致性 hash

map-base 取模法

对 source 地址进行 hash 计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变,hash-type 指定的默认值为此算法

listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode tcp
    log global
    balance source
    hash-type map-based  # 取模法
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 3
    server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 3
一致性 hash

当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n ,该 hash 算法是动态的,支持使用 socat 等工具进行在线权重调整,支持慢启动

listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode tcp
    log global
    balance source
    hash-type consistent # 一致性hash
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

uri

基于对用户请求的 uri 的左半部分或整个 uri 做 hash,再将 hash 结果对总权重进行取模后,根据最终结果将请求转发到后端指定服务器,适用于后端是缓存服务器场景,默认是静态算法,也可以通过 hash-type 指定 map-based 和 consistent,来定义使用取模法还是一致性 hash

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

listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode http
    log global
    balance uri
    # hash-type map-based   # 取模法,默认,可省略
    hash-type consistent   # 一致性hash
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

url_param

对用户请求的 url 中的 params 部分中的一个参数 key 对应的 value 值作 hash 计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个 real server,如果没有 key,将按 roundrobin 算法

# 假设:http://10.0.0.7/index.html?userid=<NAME_ID>&typeid=<TYPE_ID>
listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode http
    log global
    balance url_param userid  # 对userid的值取hash
    # hash-type map-based   # 取模法,默认,可省略
    hash-type consistent   # 一致性hash
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

hdr

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

listen web_host
    bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
    mode http
    log global
    balance hdr(User-Agent)
    # hash-type map-based   # 取模法,默认,可省略
    hash-type consistent   # 一致性hash
    server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

测试访问:

[root@c71 ~]$curl -vA 'firefox' http://10.0.0.7/index.html
[root@c71 ~]$curl -vA 'chrome' http://10.0.0.7/index.html

各种算法使用场景

first        # 使用较少
static-rr       # 做了session共享的web集群
roundrobin
random
leastconn       # 数据库
source        # 基于客户端公网IP的会话保持
Uri--------------->http   # 缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯
url_param--------->http    # 可以实现session保持
hdr        # 基于客户端请求报文头部做下一步处理
rdp-cookie       # 基于Windows主机,很少使用

高级功能及配置

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

# 配置示例:
listen web_port
    bind 10.0.0.7:80
    balance roundrobin
    mode http    # 注意:不支持 tcp
    log global
    cookie WEBSRV insert nocache indirect
    server web1 10.0.0.17:80 check inter 3000 fall 2 rise 5 cookie web1
    server web2 10.0.0.27:80 check inter 3000 fall 2 rise 5 cookie web2

HAProxy 状态页

通过 web 界面,显示当前 HAProxy 的运行状态

状态页配置项:

stats enable     # 基于默认的参数启用stats page
stats hide-version    # 将状态页中haproxy版本隐藏
stats refresh <delay>   # 设定自动刷新时间间隔,默认不自动刷新
stats uri <prefix>    # 自定义stats page uri,默认值:/haproxy?stats
stats realm <realm>   # 账户认证时的提示信息,示例:stats realm HAProxy\
Statistics
stats auth <user>:<passwd>  # 认证时的账号和密码,可定义多个用户,每行指定一个用户
stats admin { if | unless } <cond>  # 启用stats page中的管理功能
# 启用状态页
listen stats
    bind :9999
    stats enable
    #stats hide-version
    stats uri /haproxy-status   # 自定义stats page uri
    stats realm HAProxy\ Stats\ Page  # 账户认证时的提示信息
    stats auth haadmin:123456    # 两个用户
    stats auth admin:123456
    #stats refresh 30s
    stats admin if TRUE     # 安全原因,不建议打开

backend server 信息:

利用状态页实现 haproxy 服务器的健康性检查:

[root@centos8 ~]#curl -I http://haadmin:123456@10.0.0.7:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html

[root@centos8 ~]#curl -I -u haadmin:123456 http://10.0.0.7:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html

[root@centos8 ~]#echo $?
0

[root@haproxy ~]#systemctl stop haproxy

[root@centos8 ~]#curl -I http://haadmin:123456@10.0.0.7:9999/haproxy-status
curl: (7) Failed to connect to 10.0.0.7 port 9999: Connection refused

[root@centos8 ~]#echo $?
7

IP 透传

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

四层负载 和 七层代理

四层负载:

四层负载均衡设备不参与建立链接(实际上还是建立的,haproxy 和 lvs 不同,haproxy 是伪四层负载均衡)

七层代理:

七层负载均衡设备起到了代理服务器的作用,七层代理需要和 Client 以及后端服务器分别建立连接

四层 IP 透传

# haproxy 配置示例
listen web_http_nodes
    bind 172.16.0.100:80
    mode tcp     # 不支持http协议
    balance roundrobin
    server web1 to2b.cn:80 send-proxy check inter 3000 fall 3 rise 5 # 添加send-proxy

# nginx配置:在访问日志中通过变量$proxy_protocol_addr 记录透传过来的客户端IP
http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" "$proxy_protocol_addr"'
    server {
  listen 80 proxy_protocol; #启用此项,将无法直接访问此网站,只能通过四层代理访问
  server_name to2b.cn;
    ...

七层 IP 透传

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

HAProxy 配置:

在由 haproxy 发往后端主机的请求报文中添加“X-Forwarded-For”首部

option forwardfor [ except <network> ] [ header <name> ] [ if-none ]

[ except <network> ]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络
[ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For",示例:X-client
[ if-none ] 如果没有首部才添加首部,如果有使用默认值

示例:

defaults
    option forwardfor # 此为默认值,首部字段默认为:X-Forwarded-For
    # option forwardfor except 127.0.0.0/8 header X-client # 或者自定义首部,如:X-client

web 服务器日志格式配置:

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

示例:

# apache
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

# nginx
$proxy_add_x_forwarded_for:包括客户端IP和中间经过的所有代理的IP
$http_x_forwarded_For:只有客户端IP

# tomcat
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%{X-Forwarded-For}i %h %l %u %t &quot;%r&quot; %s %b" />

报文修改

在 http 模式下,基于实际需求修改客户端的请求报文与响应报文

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-http-request

# 请求头添加字段
http-request add-header <name> <fmt> [ { if | unless } <condition> ]
# 请求头修改字段
http-request set-header <name> <fmt> [ { if | unless } <condition> ]
# 请求头删除字段
http-request del-header <name> [ { if | unless } <condition> ]
# 响应头添加字段
http-response add-header <name> <fmt> [ { if | unless } <condition> ]
# 响应头删除字段
http-response del-header <name> [ { if | unless } <condition> ]

fmt:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4

注意:以上指令适用于 2.1 版本以上,2.1 版本以下的指令是 reqadd、reqdel 等

自定义日志格式

log global    # 开启记录日志,默认不开启
option httplog   # 开启记录httplog日志格式选项,一般不建议开启,加重HAProxy负载
capture cookie <name> len <length>  # 捕获请求和响应报文中的 cookie及值的长度,将之记录到日志
capture request header <name> len <length>  # 捕获请求报文中指定的首部内容和长度并记录日志
capture response header <name> len <length> # 捕获响应报文中指定的内容和长度首部并记录日志

# 示例
log global
option httplog
capture request header Host len 256
capture request header User-Agent len 512
capture request header Referer len 15
capture request header X-Forwarded-For len 15

压缩功能

对响应给客户端的报文进行压缩,以节省网络带宽,但是会占用部分 CPU 性能

建议在后端服务器开启压缩功能,而非在 HAProxy 上开启压缩

compression algo <algorithm> ... # 启用http协议中的压缩机制
compression type <mime type> ...  # 要压缩的文件类型

# 压缩算法<algorithm>支持下面类型
 identity      # debug调试使用的压缩方式
 gzip       # 常用的压缩方式,与各浏览器兼容较好
 deflate       # 有些浏览器不支持
 raw-deflate      # 新式的压缩方式

# 示例
compression algo gzip deflate
compression type text/html text/css text/plain

web 服务器状态监测

  • 四层:传输端口做状态监测,此为默认方式

    不断的发送 tcp 握手报文

  • 七层:指定 URI 做状态监测

    指定请求方式(默认 options),不断请求指定 URL(默认根),会在后端服务器生成大量访问日志

四层监测节省系统资源,但是不够准确,可能发生监测通过,但是网站打不开的情况,工作中更推荐使用七层监测

option httpchk # 启用七层健康性检测,对tcp和 http模式都支持
option httpchk <uri> # uri默认"/"
option httpchk <method> <uri> # method默认options,推荐设置为HEAD,节省网络流量
option httpchk <method> <uri> <version> # version默认HTTP/1.0,注意HTTP/1.1必须有Host字段

http-check expect [!] <match> <pattern> # 期望以上检查得到的响应码


# 示例
option httpchk HEAD /monitor/check.html HTTP/1.1\r\nHost:\ 10.0.0.7
http-check expect status 200
http-check expect ! rstatus ^5  # 支持正则表达式,响应码不以5开头

ACL ★★★

访问控制列表(ACL,Access Control Lists),特定过滤条件的集合

首先定义 ACL,然后调用 ACL

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7

ACL 配置选项

acl  <aclname>  <criterion>  [flags]  [operator]  [<value>] ...
acl  名称   匹配规范    匹配模式   具体操作符   操作对象类型
aclname

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中的domain name
    hdr_dir ([<name> [<occ>]]) # 路径匹配,header的uri路径
    hdr_len ([<name> [<occ>]]) # 长度匹配,header的长度匹配
    hdr_reg ([<name> [<occ>]]) # 正则表达式匹配,自定义表达式(regex)模糊匹配
    hdr_sub ([<name> [<occ>]]) # 子串匹配,header中的uri模糊匹配
base : string
 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
 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
url : string
 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
flags
-i : 不区分大小写
-f : 从文件中加载pattern匹配方法
-m : 使用指定的pattern匹配方法
-n : 不做DNS解析
-M : load the file pointed by -f like a map file.
-u : 禁止acl重名,否则多个同名ACL匹配或关系
-- : force end of flags. Useful when a string looks like one of the flags.

-f、-m 可选的 pattern 匹配方法:

- "found" : only check if the requested sample could be found in the stream,
            but do not compare it against any pattern. It is recommended not
            to pass any pattern to avoid confusion. This matching method is
            particularly useful to detect presence of certain contents such
            as headers, cookies, etc... even if they are empty and without
            comparing them to anything nor counting them.

- "bool"  : check the value as a boolean. It can only be applied to fetches
            which return a boolean or integer value, and takes no pattern.
            Value zero or false does not match, all other values do match.

- "int"   : match the value as an integer. It can be used with integer and
            boolean samples. Boolean false is integer 0, true is integer 1.

- "ip"    : match the value as an IPv4 or IPv6 address. It is compatible
            with IP address samples only, so it is implied and never needed.

- "bin"   : match the contents against a hexadecimal string representing a
            binary sequence. This may be used with binary or string samples.

- "len"   : match the sample's length as an integer. This may be used with
            binary or string samples.

- "str"   : exact match : match the contents against a string. This may be
            used with binary or string samples.

- "sub"   : substring match : check that the contents contain at least one of
            the provided string patterns. This may be used with binary or
            string samples.

- "reg"   : regex match : match the contents against a list of regular
            expressions. This may be used with binary or string samples.

- "beg"   : prefix match : check that the contents begin like the provided
            string patterns. This may be used with binary or string samples.

- "end"   : suffix match : check that the contents end like the provided
            string patterns. This may be used with binary or string samples.

- "dir"   : subdir match : check that a slash-delimited portion of the
            contents exactly matches one of the provided string patterns.
            This may be used with binary or string samples.

- "dom"   : domain match : check that a dot-delimited portion of the contents
            exactly match one of the provided string patterns. This may be
            used with binary or string samples.
operator

ACL 操作符

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

字符比较:

- exact match (-m str)   # 字符串必须完全匹配模式
- substring match (-m sub)  # 在提取的字符串中查找模式,如果其中任何一个被发现,ACL将匹配
- prefix match (-m beg)  # 在提取的字符串首部中查找模式,如果其中任何一个被发现,ACL将匹配
- suffix match (-m end)  # 将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配
- subdir match (-m dir)  # 查看提取出来的用斜线分隔(“/")的字符串,如其中任一个匹配,则ACL进行匹配
- domain match (-m dom)  # 查找提取的用点(“.")分隔字符串,如果其中任何一个匹配,则ACL进行匹配
value

value 类型

- Boolean       # 布尔值
- integer or integer range   # 整数或整数范围,比如用于匹配端口范围
- IP address / network    # IP地址或IP范围, 192.168.0.1 ,192.168.0.1/24
- string--> www.magedu.com
    exact   # 精确比较
    substring  # 子串
    suffix  # 后缀比较
    prefix  # 前缀比较
    subdir  # 路径, /wp-includes/js/jquery/jquery.js
    domain  # 域名,www.magedu.com
- regular expression    # 正则表达式
- hex block      # 16进制

多个 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 示例-域名匹配

# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl pc_domain hdr_dom(host) -i www.magedu.org
    acl mobile_domain hdr_dom(host) -i mobile.magedu.org
    ###################### acl hosts #################################
    use_backend pc_hosts if pc_domain # use_backend 调用后端服务器组
    use_backend mobile_hosts if mobile_domain
    default_backend pc_hosts  # 所有ACL都不匹配,则使用的默认backend

###################### backend hosts #############################
backend mobile_hosts
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-基于源 IP 或子网调度访问

# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl pc_domain hdr_dom(host) -i www.magedu.org
    acl mobile_domain hdr_dom(host) -i mobile.magedu.org
    acl ip_range_test src 172.18.0.0/16 10.0.0.6  # 基于源地址的ACL,定义多个ACL的顺序无关
    acl ip_range_test2 src 172.18.0.200
    ###################### acl hosts #################################
    use_backend pc_hosts if ip_range_test #放在前面的ACL规则优先生效,引用ACL时,严格的ACL应放在前面
    use_backend pc_hosts if pc_domain
    use_backend mobile_hosts if mobile_domain
    default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-基于源地址的访问控制

listen web_host
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl acl_deny_src src 10.0.0.6 192.168.0.0/24
    ###################### acl hosts #################################
    http-request deny if acl_deny_src  # http-request deny 拒绝请求,返回403
    #block if acl_deny_src #2.1版本后,不再支持block
    #http-request allow
    default_backend default_web

###################### backend hosts #############################
backend magedu_host
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend default_web
    mode http
    server web1 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-匹配浏览器类型

# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl acl_user_agent hdr_sub(User-Agent) -i curl wget  # 浏览器是curl和wget
    acl acl_user_agent_ab hdr_sub(User-Agent) -i ApacheBench # 浏览器是ApacheBench
    ###################### acl hosts #################################
    redirect prefix http://www.baidu.com if acl_user_agent  # 302 临时重定向至新URL
    http-request deny if acl_user_agent_ab  # 拒绝ab
    default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-基于文件后缀名实现动静分离

# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl acl_static path_end -i .jpg .jpeg .png .gif .css .js .html  # 静
    acl acl_php path_end -i .php # 动
    ###################### acl hosts #################################
    use_backend mobile_hosts if acl_static
    use_backend app_hosts if acl_php
    default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5
backend app_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-匹配访问路径实现动静分离

# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
    bind 10.0.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    ###################### acl setting ###############################
    acl acl_static path_beg -i /static /images /javascript  # 路径起始
    acl acl_static path_end -i .jpg .jpeg .png .gif .css .js .html .htm # 路径结尾
    acl acl_app path_beg -i /api
    ###################### acl hosts #################################
    use_backend static_hosts if acl_static
    use_backend app_hosts if acl_app
    default_backend app_hosts

###################### backend hosts #############################
backend static_hosts
    mode http
    server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend app_hosts
    mode http
    server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL 示例-预定义 ACL 使用

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7.4

ACL name Equivalent to Usage
FALSE always_false never match
HTTP req_proto_http match if protocol is valid HTTP
HTTP_1.0 req_ver 1.0 match HTTP version 1.0
HTTP_1.1 req_ver 1.1 match HTTP version 1.1
HTTP_CONTENT hdr_val(content-length) gt 0 match an existing content-length
HTTP_URL_ABS url_reg ^[^/:]*:// match absolute URL with scheme
HTTP_URL_SLASH url_beg / match URL beginning with “/“
HTTP_URL_STAR url * match URL equal to “*“
LOCALHOST src 127.0.0.1/8 match connection from local host
METH_CONNECT method CONNECT match HTTP CONNECT method
METH_DELETE method DELETE match HTTP DELETE method
METH_GET method GET HEAD match HTTP GET or HEAD method
METH_HEAD method HEAD match HTTP HEAD method
METH_OPTIONS method OPTIONS match HTTP OPTIONS method
METH_POST method POST match HTTP POST method
METH_PUT method PUT match HTTP PUT method
METH_TRACE method TRACE match HTTP TRACE method
RDP_COOKIE req_rdp_cookie_cnt gt 0 match presence of an RDP cookie
REQ_CONTENT req_len gt 0 match data in the request buffer
TRUE always_true always match
WAIT_END wait_end wait for end of content analysis

示例:

http-request deny if METH_TRACE HTTP_1.1  # 引用预定义的ACL,多个ACL默认为与关系

自定义 HAProxy 错误界面

errorfile <code> <file>  # 自定义错误页
    <code>  # HTTP status code.支持200, 400, 403, 405, 408, 425, 429, 500, 502,503,504
    <file>  # 包含完整HTTP响应头的错误页文件的绝对路径。 建议后缀".http",以和一般的html文件相区分

errorloc <code> <url>  # 错误页面重定向

示例:

defaults
 ...
    errorfile 400 /etc/haproxy/errorfiles/400badreq.http
    errorfile 403 /etc/haproxy/errorfiles/403forbid.http
    # errorfile 503 /etc/haproxy/errorfiles/503sorry.http
    errorloc 503 http://www.magedu.com/error_pages/503.html  # 重定向

HAProxy 四层负载

问题: 后端服务器和 haproxy 还是和客户端建立三次握手?

答:HAProxy,因为 HAProxy 是伪四层,LVS 是真四层,不过,即使是伪四层,四层负载也比七层代理性能要高

四层负载示例:

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

# 对MySQL服务实现四层负载
listen magedu_mysql
    bind 10.0.0.7:3306
    mode tcp
    balance leastconn
    server mysql1 10.0.0.17:3306 check
    server mysql2 10.0.0.27:3306 check   # 如果不写端口号,可以转发,但无法check状态

# 或者使用frontend和backend实现
frontend mysql
    bind :3306
    mode tcp   # 必须指定tcp模式
    default_backend mysqlsrvs
backend mysqlsrvs
    mode tcp   # 必须指定tcp模式
    balance leastconn
    server mysql1 10.0.0.17:3306
    server mysql2 10.0.0.27:3306
# ACL示例-四层访问控制
acl invalid_src src 192.168.1.0/24 10.0.0.8  # 定义acl
tcp-request connection reject if invalid_src  # tcp-request,走的是四层的拒绝

HAProxy https 实现

haproxy 可以实现 https 的证书安全,但基于性能考虑,生产中证书都是在后端服务器比如 nginx 上实现,在 HAProxy 上采用四层负载,监听 ip:443,然后直接转发,让后端服务器去处理 https 证书

本章重点总结

  • HAProxy 调度算法
  • 动静分离与客户端源 IP 透传
  • ACL 使用与报文修改
  • 服务器动态下线
  • 编写 shell 脚本,实现能够基于参数传递 Real Server 服务器 IP,并实现将其从多个 HAProxy 进程下线与上线

HAProxy
http://blog.lujinkai.cn/运维/HAProxy/HAProxy/
作者
像方便面一样的男子
发布于
2021年1月4日
更新于
2023年12月5日
许可协议