
一、介绍¶
HAProxy 是法国人 Willy Tarreau 开发的一个开源软件,是一款应对 C10K 以上的同时连接的高性能的 TCP 和 HTTP 负载均衡器。其功能是用来提供基于 cookie 的持久性,基于内容的交换,过载保护的高级流量管制,自动故障切换,以正则表达式为基础的控制运行时间,基于 Web 的报表,高级日志记录以帮助排除故障的应用或网络及其他功能.
和 Nginx 一样,都支持在 4 层把 https 请求负载均衡至后端,或者在 7 层作为 https 会话卸载器

不同于 Nginx 的是,HAproxy 只能做 TCP/UDP/HTTP 的反向代理,自身不能做 Web 站点。
二、HAProxy 程序的组成¶
安装¶
CentOS 的 Base 源已经收录 HAProxy,可使用 YUM 直接安装。
1 | yum install haproxy -yum |
程序环境¶
- 主程序:
/usr/sbin/haproxy - 主配置文件:
/etc/haproxy/haproxy.cfg - Unit file:
/usr/lib/systemd/system/haproxy.service
配置文件¶
大体分为 2 个配置段
- global:全局配置段,主要负责:
- 进程及安全配置相关的参数
- 性能调整相关参数
- Debug 参数
- 用户列表
- peers
- proxies:代理配置段,主要负责:
defaults:为frontend,listen,backend提供默认配置。frontend:前端,相当于 nginx 中的server {}。backend:后端,相当于 nginx 中的upstream {}。listen:前端和后端一致。
简单的调度配置示例¶

1 | frontend web *:80 |
三、配置参数详解¶
参数可用位置矩阵图:http://cbonte.github.io/haproxy-dconv/1.5/configuration.html#4.1
global 配置段参数¶
进程及安全管理¶
chroot, user, group, uid, gid, daemon:这几条都能根据名字鉴别之意。
官方强烈建议这两条配置默认即可,HAProxy 会自动调整,所以不建议配置:
nbproc <number>:要启动的 haproxy 的进程数量,官方建议单进程。ulimit-n <number>:每个 haproxy 进程可打开的最大文件数。
性能调整¶
maxconn 4000:设定每个 HAProxy 程序进程 所能接受的最大并发连接数,默认是4000。- 所以总的并发连接数等于:
nbproc*maxconn
- 所以总的并发连接数等于:
maxconnrate <number>:设定每个 HAProxy 进程每秒种所能创建的非 ssl 的最大连接数量。maxsessrate <number>:设定每个 HAProxy 进程每秒种所能创建的最大会话(session)数量。maxsslconn <number>:设定每个 HAProxy 进程所能接受的 ssl 的最大并发连接数。spread-checks <0..50, in percent>:为了分散健康状态检测时的压力,设置检测延迟,最多不能超过检查周期长度的 50%。
proxies 配置段参数¶
HAProxy 可以通过配置文件,进行颗粒度很细很精准的各种代理配置,但随之而来的是配置项会相当复杂,难度提高,不过可以结合官方文档来学习。
proxies 的配置段有如下 四个区域:
defaults <name>:为frontend,listen,backend提供默认配置。frontend <name>:前端,相当于 nginx 中的server {}。backend <name>:后端,相当于 nginx 中的upstream {}。listen <name>:前端和后端一致。
注意:同一个参数可用于多个区域,但是生效的作用域不同,生效范围越小的优先级越高。
具体可用位置建议结合官网的 参数可用位置矩阵图 来使用
- http://cbonte.github.io/haproxy-dconv/1.5/configuration.html#4.1 ( Version 1.5.18 )
常用的一些参数¶
default_backend¶
default_backend <backend>:可用在除了 backend 的其他区域中,指定要使用的 backend。
bind¶
bind [IP:PORT],[:PORT]:指定前端要监听的端口,也可以直接接在 frontend 后边指定,没有区别,如 frontend web *:80
balance 调度算法¶

定义要在后端使用的负载均衡算法:
这里先说一下动态算法和静态算法的区别:
- 动态算法特性:新加服务器后或调整权重后,reload 即可生效,且支持慢启动,流量缓慢增加。
- 静态算法特性:新加服务器后或调整权重后,必须重启服务生效,且不支持慢启动。
语法:balance <algorithm> [ <arguments> ]
<algorithm>:指定调度算法。round-robin:轮询。(动态算法)static-rr:静态轮询。leastconn:最少连接优先,适用于长会话的场景。(动态算法)first:根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务器。source:源地址 HASH。hdr <name>:对于每个 http 请求,此处由<name>指定的 http 首部将会被取出做 HASH 计算,没有有效值的会被轮询调度。uri:对 uri 的左半部分做 HASH 计算。uri-param:对用户请求的 uri 的<params>部分中的参数的值作 HASH 计算。通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个 Backend Server。
1 | <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag> |
定义 HASH 类型¶
定义 source, hdr, uri, uri-param 这几种调度算法的 HASH 方式。
语法:hash-type:
map-based:取模算法。(静态算法)consistent:一致性 HASH。(动态算法)
动态算法特性:新加服务器后或调整权重后,reload 即可生效,且支持慢启动,流量缓慢增加。
静态算法特性:新加服务器后或调整权重后,必须重启服务生效,且不支持慢启动。
配置 server¶
定义后端主机¶
定义后端主机的各服务器及其选项。
语法:server <name> <address>[:[port]] [param*]
<name>:自定义服务器在 haproxy 上的内部名称,出现在日志及警告信息中。<address>:服务器地址,支持使用主机名。[:[port]]:端口映射,省略时,表示同 bind 中绑定的端口。[param*]:其他参数,包括:maxconn <maxconn>:当前 server 的最大并发连接数。backlog <backlog>:当前 server 的连接数达到上限后的后援队列长度。backup:设定当前 server 为备用服务器。check:对当前 server 做健康状态检测。addr:健康检测时使用的 IP 地址。port:针对此端口进行健康检测。inter <delay>:连续两次健康检测之间的时间间隔,默认为 2000ms。rise <count>:连续多少次健康检测结果为“成功”才标记服务器为可用,默认为2。fall <count>:连续多少次健康检测结果为“失败”才标记服务器为不可用,默认为3。- HAProxy 默认在 4 层做健康检测,也支持一些应用层的检测方法:
option httpchk,smtpchk,mysql-check,pgsql-check,ssl-hello-chk。
cookie <value>:为当前后端 server 定义 cookie 值,用于实现基于 cookie 的会话绑定disabled:标记为不可用,同 Nginx 中的down。error-limit:定义 server 健康检测连续失败的次数,到达后,执行on-error的设定,默认是10次。on-error <mode>:后端 server 故障时的如何处理。fastinter:开始快速检测,加快检测频率,并进行请求排干。fail-check:模拟一个失败的检查,后强制执行fastinter,并进行请求排干,默认为此配置。sudden-death:模拟一次致命错误,如再失败一次,则将 server 置为 down,并强制fastinter。mark-down:直接把 server 置为 down 状态,并强制执行fastinter。
redir <prefix>:将发往此 server 的所有 GET 和 HEAD 类的请求重定向至指定的 URL。weight <weight>:定义权重,默认为 1。
Cookie¶
使用 cookie 进行会话绑定。
1 | cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] |
rewirte:重写insert:插入prefix:前缀
基于 cookie 的 session sticky 的实现:
1 | backend websrvs |
启用监控状态统计页面的参数¶
HAProxy 自带了一个强大的监控页,而且能统计数据。
stats enable:启用统计页;基于默认的参数启用 stats page。stats hide-version:隐藏头部信息。stats scope:定义显示哪些区域,默认为全部显示stats uri <prefix>:自定义 stats page 的 uri。stats realm <realm>:认证时显示的内容。stats auth <user>:<passwd>:认证时的账号和密码,可使用多次。stats refresh <delay>:设定自动刷新时间间隔。stats admin { if | unless } <cond>:启用 stats page 中的管理功能。stats refresh <s>:自动刷新时间。
默认值:
stats uri : /haproxy?statsstats realm : "HAProxy Statistics"stats auth : no authenticationstats scope : no restriction
配置示例:
1 | // 公开访问 (但是只显示此后端) |
设定 mode 模式¶
mode { tcp|http|health }:定义 HAProxy 的工作模式
tcp:基于 layer4 实现代理,可代理mysql,pgsql,ssh,ssl等协议。http:仅当代理的协议为 http 时使用。health:工作为健康状态检查的响应模式,用于检查前端,当连接请求到达时回应 “OK” 后即断开连接。
示例:
1 | listen ssh |
自定义错误页¶
errorfile <code> <file>:读取本地文件系统上的某个文件当做错误页的内容。
示例:
1 | errorfile 400 /etc/haproxy/errorfiles/400badreq.http |
errorloc <code> <url>:直接响应一个新的 URL 给客户端。
示例:
1 | errorloc 403 http://www.monster.com/error_pages/403.html |
自定义报文首部¶
添加 X-Forwarded-For 首部¶
可以在数据报文离开 HAProxy 主机前,添加自定义报文首部,比如可以把发往后端主机的请求报文中添加 X-Forwarded-For 首部,值为前端客户端的地址,用于向后端主发送真实的客户端IP。
语法:option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
[ except <network> ]:请求报请来自此处指定的网络时不予添加此首部。[ header <name> ]:使用自定义的首部名称,而非X-Forwarded-For。
示例:
1 | // 请求报请来自本机时不添加此首部 |
添加自定义报文首部¶
还可以添加一些自定义的首部。
reqadd <string> [{if | unless} <cond>]:添加一个自定义的请求报文首部。rspadd <string> [{if | unless} <cond>]:添加一个自定义的响应报文首部。<string>:定义一个首部和其对应的值,任何空格或已知的分隔符必须使用反斜杠\转义。[{if | unless} <cond>]:可使用由 ACL 定义的可选匹配条件。
示例:
给客户端响应一个自定义的首部 X-Via,值为 HAPorxy。
1 | rspadd X-Via:\ HAPorxy |
隐藏首部¶
reqdel <search> [{if | unless} <cond>]:删除通过正则表达式,匹配到的请求报文首部。reqidel <search> [{if | unless} <cond>]:删除通过正则表达式,匹配到的请求报文首部,并忽略大小写。rspdel <search> [{if | unless} <cond>]:删除通过正则表达式,匹配到的响应报文首部。rspidel <search> [{if | unless} <cond>]:删除通过正则表达式,匹配到的响应报文首部,并忽略大小写。
示例:
隐藏所有响应报文中包含 Server.* 的首部。
1 | rspidel Server.* |
compression 压缩¶
为指定的 MIME 类型启用压缩传输功能
-
compression algo <algorithm> ...:启用 http 协议的压缩机制。<algorithm>:指明压缩算法:gzip,deflate
-
compression type <mime type> ...:指明压缩的 MIME 类型。<mime type>:常见的适用于压缩的类型为文本类型。
示例:
1 | compression algo gzip deflate |
定义基于 7 层的健康状态检测¶
对后端服务器做 http 协议的健康状态检测
option httpchk:不做任何定义时,只会使用OPTIONS请求方法来检测主页,注意默认不是使用GET做检测。option httpchk <uri>:对指定的 uri 使用OPTIONS请求方法来检测,注意默认不是使用GET做检测。option httpchk <method> <uri>:对指定的 uri,用指定的请求方法做检测。option httpchk <method> <uri> <version>:对指定的 uri,用指定的请求方法,对指定的协议版本做检测。
自定义检测机制:
http-check expect [!] <match> <pattern>:检查 HTTP 响应的内容,或特定的状态代码。
注意:有空格需要使用 \ 来转移,可以使用正则表达式匹配,但是会影响性能。
示例:
1 | // only accept status 200 as valid |
定义连接超时时长¶
定义连接超时时长,默认单位是 毫秒。
timeout client <timeout>:定义客户端非活动时间;timeout server <timeout>:定义后端服务的非活动时间;timeout http-keep-alive <timeout>:定义持久连接的持久时长;timeout http-request <timeout>:等待完整 HTTP 请求的最大允许时间timeout connect <timeout>:定义与后端服务器尝试连接成功的最长时间。timeout client-fin <timeout>:定义与客户端的半关闭(half-closed)连接的非活动超时时间。timeout server-fin <timeout>:定义与后端服务器的半关闭(half-closed)连接的非活动超时时间。
关于日志¶
HAProxy 中可以定义将日志发往专门的日志收集服务器中
注意:一个配置段,最多只能定义两个日志收集服务器。
log global:默认,发往 global 中定义的 log server。log <address> [len <length>] <facility> [<level> [<minlevel>]]:定义把日志发往哪里的 log server。no log:不记录日志。
自定义要记录的日志信息¶
从 cookie 中记录指定的信息¶
语法:capture cookie <name> len <length>
<name>:指定要记录的 name<length:指定记录的信息长度。
从请求报文中记录指定的信息¶
语法:capture request header <name> len <length>
<name>:指定要记录的name。<length:指定记录的信息长度。
示例:
1 | capture request header X-Forwarded-For len 15 |
从响应报文中记录指定的信息¶
语法:capture response header <name> len <length>
<name>:指定要记录的name。<length:指定记录的信息长度。
示例:
1 | capture response header Content-length len 9 |
使用 rsyslog 记录 HAProxy 日志¶
默认情况下,HAProxy 中定义的是将日志发往本机的 rsyslog 服务来记录日志,如下:
vim /etc/haproxy/haproxy.cfg
1 | ... |
但是 rsyslog 中没有 local2 这个区域,如果想生效此日志配置,需要配置并启用 rsyslog。
1、编辑 rsyslog 的配置文件,取消注释 几行配置来启用 TCP/UDP 端口,并添加一行配置。
vim /etc/rsyslog.conf
1 | ... |
2、重启 rsyslog 服务。
1 | systemctl restart rsyslog |
3、就能看到日志了。
tail -f /var/log/rsyslog.log
1 | Aug 9 01:26:10 localhost haproxy[11080]: 192.168.50.3:10025 [09/Aug/2019:01:26:10.168] web webcon/con1 0/0/0/0/0 200 247 - - ---- 2/2/0/1/0 0/0 "GET / HTTP/1.1" |
注意:HAProxy 日志最多可以定义两个,超出无效。所以可以一个发往本地,另一个发往日志中心。
四、ACL¶
HAProxy 能够从请求报文、响应报文、客户端或者服务端信息,从表、环境信息等等中提取数据。提取这样的数据的动作我们称之为获取样本。进行检索时,这些样本可以用来实现各种目的,最常用的用途是,根据预定义的模式来进行匹配。
访问控制列表(ACL) 提供了一个灵活方案进行内容切换,可以从请求,响应,任何环境中提取数据,并做出响应动作。
控制列表的工作步骤很简单:
- 从数据流,表,环境中提取数据样本;
- 对提取的样本可选地应用格式转换;
- 对一个样本应用一个或多个的模式匹配;
- 当模式匹配到样本时执行动作。
而可以执行的动作通常有:
- 阻断匹配的请求;
- 调度选择一个后端服务器;
- 添加一个 HTTP 首部。
而且,获取的样本数据不光可以使用在 ACL 中,也可以使用别处,例如记录在 log 中。
总的来说,ACL 其实只对特定数据进行比较并过滤,而过滤后的内容如何来操作,是其他参数工作的。
定义 ACL 的语法¶
语法:acl <aclname> <criterion> [flags] [operator] [<value>] ...
<aclname>:自定义一个 ACL 的名称,可使用字母、数字、:、.、-、_,区分大小写。不同的 ACL 默认可以重名,重名的两个 ACL 规则之间是or关系,满足其一即可。<criterion>:定义要比较什么类型的数据,如uri、source ip等。flags:定义一些额外的要求,如“不区分大小写”。operator:定义比较的运算方法,如“数值比较”,“字符串匹配”等。<value>:定义要比较此类型的什么值。
由于 ACL 可以控制的非常细致,所以参数特别多,这里分类介绍一些可能会用到的参数。
1. flags 的可用参数¶
定义一些额外的要求。
-i:忽略大小写。-m:指定特殊匹配模式。-n:禁止做 DNS 解析。-u:不允许两个 ACL 使用的名字相同。--:强制结束 flag,避免了字符串中含有的-引起混淆。
2. operator 的可用参数¶
定义比较的运算方法。
- 匹配整数值:
eq(=),ge(>=),gt(>),le(<=),lt(<) - 匹配字符串:
-m str:精确比较 exact-m sub:字串比较 substring-m end:后缀比较 suffix-m beg:前缀比较 prefix-m dir:对 uri 中以/分割的各子目录做精确比较 subdir-m dom:对域名中以.分割的字符串做精确比较 domain
3. value 的可用参数¶
定义要比较什么值,如 /images/*.jpg、192.168.0.0/24 等,可用类型有:
- 布尔型;
- 一个整数或整数范围;
- 网络地址或 IP 地址;
- 字符串;
- 正则表达式匹配模式;
- 十六进制的数据块。
4. criterion 的可用参数¶
定义要比较什么数据类型,可用参数如下:
网络地址¶
dst:目标 IPdst_port:目标端口src:源 IPsrc_port:源端口
path(URI)¶
基于字符串检查 URI 路径 /path;<params>,两种表达方式,path_end = path -m end,效果一样。
path:exact string match(精确比较)path_sub:substring match(字串比较)path_end:suffix match(后缀比较)path_beg:prefix match(前缀比较)path_dir:subdir match(子目录精确比较)path_dom:domain match(域名字符串精确比较)path_len:length match (长度比较)path_reg:regex match (正则表达式模式比较)
示例:
1 | path_beg /images/ |
URL¶
基于字符串检查 URL,比 path 范围更广,两种表达方式,url_end = url -m end,效果一样。
url:exact string match(精确比较)url_sub:substring match(字串比较)url_end:suffix match(后缀比较)url_beg:prefix match(前缀比较)url_dir:subdir match(子目录精确比较)url_dom:domain match(域名字符串精确比较)url_len:length match (长度比较)url_reg:regex match (正则表达式模式比较)
req.hdr¶
基于字符串检查请求报文首部,两种表达方式,hdr_end = hdr -m end,效果一样。
hdr([<name>[,<occ>]]):exact string match(精确比较)hdr_sub([<name>[,<occ>]]):substring match(字串比较)hdr_end([<name>[,<occ>]]):suffix match(后缀比较)hdr_beg([<name>[,<occ>]]):prefix match(前缀比较)hdr_dir([<name>[,<occ>]]):subdir match(子目录精确比较)hdr_dom([<name>[,<occ>]]):domain match(域名字符串精确比较)hdr_len([<name>[,<occ>]]):length match (长度比较)hdr_reg([<name>[,<occ>]]):regex match (正则表达式模式比较)
示例:
1 | acl bad_curl hdr_sub(User-Agent) -i curl |
status¶
根据响应报文中的状态码进行匹配,应该是一个整数。
status <integer>
5.一些预定义的 ACL¶
不必提前声明即可使用,为了避免混淆,名字都用大写字母,如下所示。
| 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_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_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 |
动作¶
在匹配命中的这些数据的基础上,做什么动作。
首先了解一下,ACL 在作为条件时,逻辑关系有如下 3 种:
- AND 与运算
- OR 或运算
- ! 取反,优先级高
可使用德摩根定律
例如:
1 | if invalid_src invalid_port // 必须同时匹配 src 和 port |
可用动作:
-
use_backend <backend> [{if | unless} <condition>]
基于 ACL 的条件,如果 匹配 | 不匹配,就调度到指定后端。 -
block { if | unless } <condition>
如果匹配 | 如果不匹配,就阻塞第 7 层请求。 -
http-request { allow | deny } [ { if | unless } <condition> ]
也是阻塞第 7 层请求,不过和block比,可以自定义控制方式是allow还是deny。省略{ if | unless }的话代表全部。 -
tcp-request connection {accept|reject} [{if | unless} <condition>]
可以自定义 4 层的访问控制方式。
五、HAProxy 在企业中的实现¶
负载均衡 4 层 sshd 服务¶
1 | listen sshd |
基于 ACL 实现动静分离的配置示例¶
1 | frontend web *:80 |
配置 HAProxy 支持 https 协议¶
1、支持 ssl 会话
1 | bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE |
2、crt 后的证书文件要求 PEM 格式,且同时包含证书和与之匹配的所有私钥,把二者合一。
1 | cat demo.crt demo.key > demo.pem |
3、把 80 端口的请求重向定 443
1 | bind *:80 |
- 另一种配置:对非 ssl 的任何 url 的访问统统定向至 https 主机的主页。
1 | redirect location https://172.16.0.67/ if !{ ssl_fc } |
4、向后端传递用户请求的真实协议和端口。(看业务需求,可不配置)
1 | http_request set-header X-Forwarded-Port %[dst_port] |