HAProxy常用配置介绍,ACL详解

时间:2022-12-09 23:22:40

1、HAProxy简介

HAProxy 是一款高性能TCP/HTTP 反向代理负载均衡服务器,具有如下功能:

  • 根据静态分配的cookies完成HTTP请求转发

  • 在多个服务器间实现负载均衡,并且根据HTTP cookies 实现会话粘性

  • 主备服务器切换

  • 接受访问特定端口实现服务监控

  • 实现平滑关闭服务,不中断已建立连接的请求响应,拒绝新的请求

  • 在请求或响应HTTP报文中添加,修改,或删除首部信息

  • 根据正则规则阻断请求

  • 提供带有用户认证机制的服务状态报告页面

HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支持数以万计的 并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。

HAProxy 实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户端(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。

HAProxy实际工作中,它占用用户空间时间要比内核运行时间少20倍,所以对系统参数调优是十分必要的一项工作。

另外衡量一个负载均衡服务器主要考量三个指标

  1. session rate

    此项指标非常重要,它决定了一个load balancer 能不能分发所有接受的请求。这项指标通常是由CPU性能决定。测量指标的大小跟传输的每个对象的大小有关,通常用空对象来测试,Session rates 在 100,000 sessions/s 左右,使用 Xeon E5 在 2014测试。
  2. session concurrency

    该指标与前一指标相关联。这一指标与服务器内存和系统可以处理的文件描述符数量有关。 通常每个session占用34KB,即大概3W个session占用1GB内存空间,实际上,socket buffer也会占用内存空间,2W个session socket占用1GB内存。
  3. data forwarding rate

    这一指标与 session rate 相对立,它的衡量单位通常是 Megabytes/s (MB/s), 或者 Gigabits/s (Gbps)。传输较大的对象有利于该指标的提升,因为较大的对象传输可以减少session建立和关闭浪费的时间。而测量session rate 则在传输小对象时有利于指标提升。haproxy 在2014年使用 Xeon E5 测试成绩为40 Gbps。

2、HAProxy程序环境

本文环境:CentOS7.2 haproxy 1.5 通过yum 安装

程序环境:
配置文件:/etc/haproxy/haproxy.cfg
Unit File: haproxy.service
主程序:/usr/sbin/haproxy

配置文件:
global:全局配置段
进程及安全配置相关的参数
性能调整相关的参数
Debug相关的参数
proxies:代理配置段
defaults:为frontend, backend以及listen提供默认配置;
frontend:前端,相当于Nginx中的server{ ... };
backend:后端,相当于nginx中的upstream { ... };
listen:前后端的直接组合;
**关于前端与后端的关系:一个前端可以指向多个后端;同时一个后端可以被多个调用。

3、HAProxy配置详解

3.1 global配置段

3.1.1 进程相关配置

  • 定义日志系统相关属性

log <address> [len <length>] <facility> [max level [min level]]

harpoxy 将日志发送到指定的rsyslog服务器,在本地记录也要开启rsyslog服务;
全局端最多可配置两个log 服务器;
< address> :日志服务器地址
[ len ] 指定记录的日志最大长度

  • 定义运行用户,所属组

  1. username

group groupname

  • 运行方式

  1. 意味着后台守护进程

3.1.2 参数调优

    maxconn <number>:设定单haproxy进程的最大并发连接数;
maxconnrate <number>:设定单haproxy进程每秒接受的连接数;
maxsslconn <number>:设定单haproxy进程的ssl连接最大并发连接数;
maxsslrate <number>:单haproxy进程的ssl连接的创建速率上限;
spread-checks <0..50, in percent>:避免对于后端检测同时并发造成
的问题,设置错开时间比,范围050,一般设置2-5较好。

3.1.3 用户列表

用于对haproxy 状态监控页面的用户认证。至少要定义一个用户列表并且添加一个用户密码可以加密或明文。

Example:

userlist L1
group G1 users tiger,scott
group G2 users xdb,scott

user tiger password $6$k6y3o.eP$JlKqe4(...)xHSwRv6J.C0/D7cV91
user scott insecure-password elgato
user xdb insecure-password hello

userlist L2
group G1
group G2

user tiger password $6$k6y3o.eP$JlKBx(...)xHSwRv6J.C0/D7cV91 groups G1
user scott insecure-password elgato groups G1,G2
user xdb insecure-password hello groups G2

3.2 proxy配置段

这部分配置在下列定义区域下使用

        - defaults < name >
- frontend < name >
- backend < name >
- listen < name >

“defaults” 区域定义了frontend,backend,listen 的默认参数
“frontend“ 区域描述了接收客户端请求的监听配置
”backend“ 区域描述接受请求处理的后端服务器配置
”listen“ 区域描述一组前端和后端直接一对一绑定的组配置

HAProxy 配置的关键字与区域限制特性,即有些关键字在某个区域不可以使用下面开始讲解关键字的用法

3.2.1 常用配置指令

1. bind [<address>]:<port_range> [, ...] [param*]

仅在frontend和listen区域使用。定义服务监听端口地址等参数
[ param* ] 参数根据系统而定,一般不需要指定
example:

bind :80     #监听本机所有IP的80端口
bind *:80 #监听本机所有IP的80端口
bind 192.168.12.1:8080,10.1.0.12:8090

2. mode {tcp|http|health}

tcp:基于layer4实现代理,可代理大多数基于tcp的应用层协议,例如ssh/mysql/pgsql等;
http:客户端的http请求会被深度解析;
health:工作为健康状态检查响应模式,当请求到达时仅回应“OK”即断开连接;


3. balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post]

在backend区域定义调度算法< algorithm > 如下:

  • roundrobin

    带有权重的轮询调度算法;
    server后面使用weight来定义权重;
    动态算法:支持权重的运行时调整,支持慢启动(缓慢接收大量请求在刚启动时);仅支持最大4095个后端活动主机
  • static-rr 静态的roundrobin算法;

不支持权重的运行时调整及慢启动;但后端主机数量无限制;

  • leastconn带权重的最少连接分配动态算法;

适用长连接应用协议,如ssh等

  • first第一优先算法;

如果第一个服务端可接受请求则总是把连接分配给它,直到第一个服务端处于繁忙,分配给下一个,顺序按服务端的数字ID从小到大排列

  • source 源IP hash 算法;

该算法保证在后端服务器组没有减少或增加的情况下,能将来自同一客户端IP的请求分配至同一个服务端;
该算法适合在无法使用cookie插入的TCP模式下使用
动态算法或静态算法取决于hash-type;

  • uri
    uri hash 算法;
    该算法hash uri 的查询标记的左侧部分,或者指定whole 参数时hash全部uri;
    该算法保证访问同一uri的请求分配至同一服务端,适用于后端为缓存服务器的情况,以提高缓存命中率;
    动态算法或静态算法取决于hash-type;
    另外:该算法支持追加参数[ < arguments > ]:
    (1) whole :hash完整uri 
    (2) len number:hash指定uri的长度
    (3) depth nubmer:hash指定目录深度,每个"/"代表一个深度

  • uri_param

  1. param hash 算法;

对用户请求的url中的< param >部分中的指定的参数的值(uri中"="部分)作hash计算;
该算法适用于有用户识别参数的uri ,它保证同一user id 的请求分配至同一服务端;
若果check_post 标识启用,则在uri中没有找到"?"参数时,对HTTP Post 请求实体查找参数声明;
动态算法或静态算法取决于hash-type;
Example:

balance url_param userid
balance url_param session_id check_post 64
  • hdr(< name >)HTTP 首部字段hash算法;

指定的http首部将会被取出做hash计算。如果没有值,则降至轮询调度;动态算法或静态算法取决于hash-type;


4. hash_type < method >

在balance 指令中选定与hash 有关的算法,都会受此影响。
默认采取的方法为map-based
< method > 如下:

  • map-based:取模法,hash数据结构是静态数组;该hash是静态的,不支持在线调整权重,不支持慢启动;

该算法调度平滑,后端服务器能够均匀承受负载;缺点也是明显的:当服务器的总权重发生变化时,即有服务器上线或下线,都会导致调度结果整体改变。如果想避免此种情况应采用consistent 方法;

  • consistent:一致性哈希,哈希的数据结构是“树”;该hash是动态的,支持在线调整权重,支持慢启动

每一个server 会在"树"中出现多次, 在树中查找hash key,并选择最近的server;
该方法的优点在于,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动。所以十分适合缓存服务器
缺点:该算法不够平滑,很容易导致后端服务器负载不均衡。所以很有必要对服务器的权重以或者服务器ID进行调整;
为保持均匀负载,应该保证所有服务器ID保持一致


5. server <name> <address>[:[port]] [param*]
default-server [param*]

server用于在backend和listen中定义一个主机;default-server 用于设定server的默认参数;

[param*] 如下:

  • weight < weight >:当前server的权重;

  • id < number > :设定server ID

  • cookie < value >:为当前server指定其cookie值,此值会在收到请求报文时进行检测,其功能在于实现基于cookie会话保持;

  • check:对当前server进行健康状态检测;
    inter < delay >:时间间隔;
    rise < count >:判定为“健康”状态需要检测的次数,默认2;
    fall < count >:判定为“不健康”状态需要检测的次数,默认3;
    addr <ipv4|ipv6>:健康状态检测时使用的地址;
    port < port >:健康状态检测时使用的端口;

    注意:默认为传输层检测,即探测端口是否能响应;需要执行应用层检测,则需要httpchk, smtpchk, mysql-check, pgsql-check, ssl-hello-chk;

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

  • maxqueue <maxqueue>:当前server的等待队列的最大长度;

  • disabled:将主机标记为不可用;

  • redir <prefix>:将发往当前server的所有请求GET和HEAD类的请求均重定向至指定的URL;

Examples :
server first 10.1.1.1:1080 id 3 cookie first check inter 1000 maxconn 10000 maxqueue 2000
server second 10.1.1.2:1080 id 4 cookie second check inter 1000

6. option httpchk
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>

基于http协议作7层健康状态检测机制,默认是基于tcp层进行检测;
TCP 模式也可以使用该检测机制
< method > < uri > < version >:请求报文的超始行;
method 默认方法为 OPTIONS;返回状态码2XX,3XX意味成功;

Examples :
# Relay HTTPS traffic to Apache instance and check service availability
# using HTTP request "OPTIONS * HTTP/1.1" on port 80.
backend https_relay
mode tcp
option httpchk OPTIONS /index.html HTTP/1.1\r\nHost:\ www
server apache1 192.168.1.1:443 check port 80

7. http-check expect [!] <match> <pattern>

定义检测有效期望值; ! 表示认定的错误值;< match > 可取值为:

  • status < string >

  • rstatus < regex > 正则方式

  • string < string >

  • rstring < regex >

Examples :
# only accept status 200 as valid
http-check expect status 200

# consider SQL errors as errors
http-check expect ! string SQL\ Error

# consider status 5xx only as errors
http-check expect ! rstatus ^5

# check that we have a correct hexadecimal tag before /html
http-check expect rstring <!--tag:[0-9a-f]*</html>

8. cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]

启用基于cookie的会话黏性,要结合server指定的cookie参数一起实现;常用形式:cookie WEBSRV insert nocache indirect

Example:
backend websrvs
balance roundrobin
cookie WEBSRV insert nocache indirect
server web1 10.1.0.68:80 check weight 2 maxconn 5000 cookie web1
server web2 10.1.0.69:80 check weight 1 maxconn 3000 cookie web2

9. default_backend <backend>

当use_backend 的使用规则没有被匹配时,由default_backend 指定默认服务器组;关于use_backend 使用后续会在acl 章节中讲解;


3.2.2 log 相关

为frontend或backend定义日志记录机制;

log global  :使用全局定义的日志记录方式
log <address> [len <length>] <facility> [<level> [<minlevel>]]:自定义
no log :不记录
capture request header <name> len <length>
-->记录请求报文中的指定的首部的值于日志中;len用于指定要记录的信息的长度;
capture response header <name> len <length>
-->记录响应报文中的指定的首部的值于日志中;len用于指定要记录的信息的长度;
示例:
capture request header Referer len 30

3.2.3 自定义错误页面

- errorfile <code> <file>

< code > 指定HTTP返回的状态码。200, 400, 403, 408, 500, 502, 503, and 504 可使用;
< file > 指定一个文件代替HTTP响应; 
Example:errorfile 503 /etc/haproxy/errorfiles/503sorry.http

- errorloc <code> <url>
- errorloc302 <code> <url>

发生错误时由haproxy重定向至指定url,以上两个命令等同,响应状态码为302
Example:errorloc 503 http://www.mydomain.com/index...

- errorloc303 <code> <url>

响应状态码为303,表示以GET方法重新请求页面


3.2.4 修改请求或响应报文首部

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

HAProxy把请求报文发往后端主机之前在请求报文添加“X-Forwared-For”首部
目的为使后端服务器可记录发出请求客户端的IP地址
[ except < network> ] :选择排除的网络地址
[ header < name> ] :不使用X-Forwared-For,自定义名称
[ if-none ]:有时请求原来带有该字段,此时不再更改
Example:option forwardfor if-none

reqadd <string> [{if | unless} <cond>]
rspadd <string> [
{if | unless} <cond>]

在HTTP请求或响应首部内容尾部添加值Example:rspadd X-Via: HAProxy/1.5

reqdel <search> [{if | unless} <cond>]
reqidel <search> [
{if | unless} <cond>] (不区分大小写)

删除HTTP请求中正则匹配的所有首部

rspdel <search> [{if | unless} <cond>]
rspidel <search> [
{if | unless} <cond>] (不区分大小写)

删除HTTP响应中正则匹配的所有首部。属于安全加强策略,删除一些服务器版本信息,防止针对攻击Example:rspidel Server.*


3.2.5 超时时长设定

timeout client <timeout>

设定客户端最大非活动时长, 默认单位是ms;最好与timeout server一致


timeout server <timeout>

设定服务端最大非活动时长, 默认单位是ms;


timeout connect <timeout>

设定最大与服务端建立连接的时长


timeout http-keep-alive <timeout>

设定最大等待新请求的空闲时长,默认单位为ms;


timeout client-fin <timeout>

在客户端侧设定半关闭连接非活动超时


timeout server-fin <timeout>

在服务端侧设定半关闭连接非活动超时Example:

    
defaults http
timeout connect 5s
timeout client 30s
timeout server 30s
timeout client-fin 10s
timeout http-keep-alive 500

4、使用ACLs和获取样本

Haproxy 能够从请求报文,响应报文,从客户端或者服务端信息,从表,环境信息等等中提取数据。提取这样的数据的动作我们称之为获取样本。进行检索时,这些样本可以用来实现各种目的,比如作为粘滞表的键,最常用的用途是,根据预定义的模式来进行匹配。访问控制列表(ACL)提供一个灵活方案进行内容切换,或者在从请求,响应,任何环境状态中提取的数据基础之上做出决策。控制列表的原则很简单:

  • 从数据流,表,环境中提取数据样本

  • 对提取的样本可选地应用格式转换

  • 对一个样本应用一个或多个模式匹配

  • 当模式匹配样本时才执行动作

执行的动作通常是阻断请求,选择一个后端服务器或者添加一个HTTP首部
需要提醒的是,获取的样本数据不光可以使用在acl中,也可以使用别处,例如记录log中
定义ACL的语法为

acl <aclname> <criterion> [flags] [operator] [<value>] ...

这样一条语句建立了一个acl 测试;
这些测试应用在请求或响应中被"标准"< criterion > 部分所指定的内容,而且可以指定[ flags] 进行特性调整,有些< criterion > 支持操作符[operator] 进行运算,同时一些转换格式的关键字可以跟在< criterion >后面,使用" , "隔开。而值[< value >] 要求被
< criterion > 所支持的数据形式,多个值使用空格分隔。
< criterion > 通常是指获取样本方法的名称。使用一个获取样本方法,暗含着其输出样本的类型,类型是以下列出的一种:

  • boolean

  • integer (signed or unsigned)

  • IPv4 or IPv6 address

  • string

  • data block

ACL引擎匹配数据使用的模式类型如下:

  • boolean

  • integer or integer range

  • IP address / network

  • string (exact, substring, suffix, prefix, subdir, domain)

  • regular expression

  • hex block

ACL flags 可用列表如下:

  • -i : 忽略大小写

  • -f filename : 从文件中载入模式

  • -m method : 指定模式匹配方法

  • -n : 禁止DNS解析

  • -M : -f 载入的文件作为映射文件使用

  • -u : 强制ACL的名称唯一

  • -- : 强制结束flag结束,避免了字符串中含有的- 引起混淆


其中flag中的 -m 选项可使用的模式匹配方法如下,需要说明的是有些方法已被默认指定无需声明,例如int,ip

  • "found" : 只是用来探测数据流中是否存在指定数据,不进行任何比较

  • "bool" : 检查结果返回布尔值。匹配没有模式,可以匹配布尔值或整数,不匹配0和false,其他值可以匹配

  • "int" : 匹配整数类型数据;可以处理整数和布尔值类型样本,0代表false,1代表true

  • "ip" : 匹配IPv4,IPv6地址类型数据。该模式仅被IP地址兼容,不需要特别指定

  • "bin" : 匹配二进制数据

  • "len" : 匹配样本的长度的整数值

  • "str" : 精确匹配,根据字符串匹配文本

  • "sub" : 子串匹配,匹配文本是否包含子串

  • "reg" : 正则匹配,根据正则表达式列表匹配文本

  • "beg" : 前缀匹配,检查文本是否以指定字符串开头

  • "end" : 后缀匹配,检查文本是否以指定字符串结尾

  • "dir" : 子目录匹配,检查部分文本中以" / "作为分隔符的内容是否含有指定字符串

  • "dom" : 域匹配。检查部分文本中以" . "作为分隔符的内容是否含有指定字符串

如果获取样本值为整数,数值比较符可使用,
eq : true if the tested value equals at least one value
ge : true if the tested value is greater than or equal to at least one value
gt : true if the tested value is greater than at least one value
le : true if the tested value is less than or equal to at least one value
lt : true if the tested value is less than at least one value

想必前面一堆理论性的论述已经把大家搞的晕头转向,下面结合获取样本方法和访问控制动作指令具体阐述ACL使用方法
先介绍控制动作指令

  • layer 4 传输层控制指令

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

对tcp请求控制指令
< condition > 即为ACL定义的访问控制列表
< action > 常用值有 "accept", "reject"

  • layer 7 应用层控制指令

#阻断符合ACL的访问请求
block
{ if | unless } <condition>
#http请求的控制指令
http-request
{ allow | deny} [ { if | unless } <condition> ]
  • 后端主机调用

#根据条件来调用指定后端
use_backend <backend> [
{if | unless} <condition>]
  • 由ACL定义的多个< condition > 组成联合条件,逻辑符为

    • and (默认操作符,可省略)

    • or (或者使用 "||")

    • ! (取反)

4.1 获取内部状态样本

# 与后端建立会话速率,每秒钟建立的新会话
be_sess_rate([<backend>]) : integer

Example :

# 某后端被请求过于繁忙,则重定向至错误页
mode http
acl being_scanned be_sess_rate gt 100
redirect location /denied.html if being_scanned

4.2 获取layer 4 样本

在传输层获取样本,通常是TCP/IP 协议的IP和端口,以及建立连接速率等等。而且此部分样本通常用于"tcp-request connection"指令中的规则之中。

        dst : ip             #目标地址
dst_port : integer
src : ip #源地址
src_port : integer

Example:

#阻断来自非指定IP的访问8080端口的请求
acl myhost src 10.1.0.200
acl myport dst_port 8080
tcp-request connection reject if !myhost myport

4.3 获取layer 7 样本

/1

path : string

提取请求url的地址信息,从第一个"/"开始,不包含host,不包含参数
ACL 衍生,即包含了-m 选项中匹配模式方法 :
path : exact string match
path_beg : prefix match
path_dir : subdir match
path_dom : domain match
path_end : suffix match
path_len : length match
path_reg : regex match
path_sub : substring match

Example:

#请求资源为图片,则调用图片服务器后端
acl picture path_end -i .jpg .png .gif
use_backend server_pic if picture

/2

url : string

提取URL的全部内容,包含host和参数ACL 衍生类似,不再列举


/3

req.hdr([<name>[,<occ>]]) : string

提取http请求的指定首部字段值,< occ >可指定出现的位置ACL 衍生 :

  hdr([<name>[,<occ>]])     : exact string match
hdr_beg([<name>[,<occ>]]) : prefix match
hdr_dir([<name>[,<occ>]]) : subdir match
hdr_dom([<name>[,<occ>]]) : domain match
hdr_end([<name>[,<occ>]]) : suffix match
hdr_len([<name>[,<occ>]]) : length match
hdr_reg([<name>[,<occ>]]) : regex match
hdr_sub([<name>[,<occ>]]) : substring match

Example:

#阻断火狐浏览器发送的请求
acl firefox hdr_reg(User-Agent) -i .*firefox.*
block if firefox

/4

method : integer + string

提取请求报文中的请求方法Example:

#拒绝GET HEAD 方式之外的HTTP请求
acl valid_method method GET HEAD
http-request deny if ! valid_method

4.4 内建ACL

HAProxy有众多内建的ACLs,这些ACLs可直接调用,例如

  • LOCALHOST 匹配来自本地IP的连接,127.0.0.1/8

  • HTTP_1.1 匹配http版本1.1

  • METH_GET 匹配http请求GET或HEAD方法

  • TRUE

  • FALSE

Example:

#拒绝GET HEAD 方式之外的HTTP请求
http-request deny if ! METH_GET