HAProxy 是一款高性能TCP/HTTP 反向代理负载均衡服务器,具有如下功能:
-
根据静态分配的cookies完成HTTP请求转发
-
在多个服务器间实现负载均衡,并且根据HTTP cookies 实现会话粘性
-
主备服务器切换
-
接受访问特定端口实现服务监控
-
实现平滑关闭服务,不中断已建立连接的请求响应,拒绝新的请求
-
在请求或响应HTTP报文中添加,修改,或删除首部信息
-
根据正则规则阻断请求
-
提供带有用户认证机制的服务状态报告页面
HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支持数以万计的 并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。
HAProxy 实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户端(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。
HAProxy实际工作中,它占用用户空间时间要比内核运行时间少20倍,所以对系统参数调优是十分必要的一项工作。
另外衡量一个负载均衡服务器主要考量三个指标
-
session rate
此项指标非常重要,它决定了一个load balancer 能不能分发所有接受的请求。这项指标通常是由CPU性能决定。测量指标的大小跟传输的每个对象的大小有关,通常用空对象来测试,Session rates 在 100,000 sessions/s 左右,使用 Xeon E5 在 2014测试。
-
session concurrency
该指标与前一指标相关联。这一指标与服务器内存和系统可以处理的文件描述符数量有关。 通常每个session占用34KB,即大概3W个session占用1GB内存空间,实际上,socket buffer也会占用内存空间,2W个session socket占用1GB内存。
-
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 ] 指定记录的日志最大长度
-
定义运行用户,所属组
-
username
group groupname
-
运行方式
-
意味着后台守护进程
3.1.2 参数调优
maxconn <number>:设定单haproxy进程的最大并发连接数;
maxconnrate <number>:设定单haproxy进程每秒接受的连接数;
maxsslconn <number>:设定单haproxy进程的ssl连接最大并发连接数;
maxsslrate <number>:单haproxy进程的ssl连接的创建速率上限;
spread-checks <0..50, in percent>:避免对于后端检测同时并发造成
的问题,设置错开时间比,范围0到50,一般设置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
-
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
Host: 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