对于负载均衡集群,前面学习了lvs,nginx也能实现负载均衡,这里再学习一种负载均衡工具haproxy。
HAProxy与Nginx的主要区别体现在核心定位、协议支持、性能特点和适用场景等方面,HAProxy专精负载均衡且更擅长TCP流量处理,基于TCP和HTTP的应用程序代理,HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数,而Nginx则兼具Web服务器功能并在HTTP协议优化上更具优势。

核心架构定位
HAProxy:专业级负载均衡器,专注流量分发与高可用性设计,原生支持L4/L7层负载均衡。
Nginx:多功能Web服务器集成反向代理与负载均衡功能,采用事件驱动架构,擅长HTTP协议处理和静态资源服务。
HAProxy与Nginx都是运行在用户空间。
haproxy两种工作模式:http和tcp(在用户空间模拟出基于TCP的方式进行调度,如https,mysql等协议)
HAProxy:代理(http),并非真正意义上的高可用,虽然其名字中有HA字样,所谓高可用,可以理解为当后端代理的服务器出现问题,HAProxy代理因为具有缓存功能和多后端服务器,可以保证暂时的服务不中断。HAProxy主要实现的均衡代理调度的功能,实现负载均衡。
代理的作用:
web缓存(加速作用)、反向代理、内容路由(根据流量及内容类型等将请求转发至特定服务器,这就是负载均衡功能)、转码器(明码转密码);
在代理服务器上添加Via首部;
缓存的作用:
减少冗余内容传输;
节省带宽、缓解网络瓶颈;
降低了对原始服务器的请求压力;
降低了传输延迟;
HAProxy:只是http协议的反向代理,不提供缓存功能;但额外支持对tcp层基于tcp通信的应用做LB;
HAProxy的一些特性:
客户端侧的长连接(client-side keep-alive)
TCP加速(TCP speedups)
响应池(response buffering)
RDP协议,Remote Desktop Protocol,远程桌面协议
基于源的粘性(source-based stickiness),session绑定
更好的统计数据接口(a much better stats interfaces)
更详细的健康状态检测机制(more verbose health checks)
基于流量的健康评估机制(traffic-based health)
支持HTTP认证
服务器管理命令行接口(server management from the CLI)
基于ACL的持久性(ACL-based persistence)
日志分析器
从三个因素来评估负载均衡器的性能:
会话率,回话建立的速度
会话并发能力,会话并发数量
数据率,数据传输速度
反向代理,接受客户端的请求报文,将请求拆封后,构建新的请求报文,以代理主机的名义向后端服务器发起请求,在接到服务器的响应报文后,将响应报文拆封后重新封装成新的响应报文,返回给客户端。在接收请求报文拆封重新封装时,以及接收到响应报文拆封重新封装响应报文时,代理服务器都可以做一些特定的操作。对于后端服务器,他所看到的请求报文的源地址都是代理服务器的地址,而不是真正的客户端地址。同样在客户端看来,其获得的响应报文的服务器地址是代理服务器的地址,而不是真正的后端服务器地址。这些过程对理解和设置日志很重要。
代理服务器是一手托两家。

HAProxy安装 :
yum install haproxy

配置文件:/etc/haproxy/haproxy.cfg
配置文件结构分为两大部分,全局配置和代理配置:
全局配置主要是配置HAProxy服务器程序自身工作特性的
代理配置主要是配置作为代理的特性,分为frontend、backend、listen、defaults等

haproxy配置:
frontend:前端,接收客户端请求,即配置监听在某个端口上,还要配置转发至后端的backend,
use_backend,前端转发给后端服务器的配置,是条件式配置方式,符合acl才调用此backend
default_backend,默认调用的后端
backend:后端,真正的后端服务器,提供服务,是定义一组服务器
balance
server
server
listen: 直接定义监听服务器,在后端只有一个server时,不必配置backend,直接listen
server
defaults:提供统一的默认参数。
HAProxy代理实践:
1、环境准备:
四台主机:
frontend:192.168.61.129/192.168.147.134
beckend,三台服务器:192.168.147.133、192.168.147.128、192.168.147.137
2、三台服务器上配置好并启动httpd服务
3、配置haproxy

4、启动服务:systemctl start haproxy.service


5、访问测试:

刷新一次,页面内容改变一次,在三台后端服务器中轮询。
至此,简单的haproxy负载均衡服务构建完成。
HAProxy配置信息深入理解:
global中的常用配置项 :
log 127.0.0.1 local2 :日志配置,日志是发送到日志服务器中的facility,所以,本地主机要启动日志服务,此种配置方法,可方便将日志记录到其他日志服务器中。
编辑:/etc/rsyslog.conf,启动日志服务,即让其监听在某端口:
修改如下配置:


增加一个facility:local2,如下:

重新启动rsyslog:

页面刷新几次,查看日志(这是HAProxy的日志):

查看后端服务器的日志:

可以看到,访问的客户端地址都是192.168.147.134,实际应该记录真正的客户端地址,这对以后的日志分析才有意义。
chroot /var/lib/haproxy :切换根目录,haproxy是被切换到/var/lib/haproxy目录下以安全运行。
pidfile /var/run/haproxy.pid:pid文件的位置
maxconn 4000 :最大连接数,haproxy本身最大可以并发连接客户端的数量,4000有点少了。
user haproxy:配置haproxy进程以哪个用户运行
group haproxy:配置haproxy进程以哪个组运行
daemon :haproxy启动为守护进程工作于后台,否则运行在前台。
stats socket /var/lib/haproxy/stats:打开状态的unix socket,即unix套接字,即本地访问stats时直接通过打开文件访问,基于共享内存的方式,而不是通过TCP/IP协议访问。
"global"配置中的参数为进程级别的参数,且通常与其运行的OS相关。配置参数分为两大类:
* 进程管理及安全相关的参数
-
chroot <jail dir>:修改haproxy的工作目录至指定的目录并在放弃权限之前执行chroot()操作,可以提升haproxy的安全级别,不过需要注意的是要确保指定的目录为空目录且任何用户均不能有写权限;
-
daemon:让haproxy以守护进程的方式工作于后台;
-
gid <number>:以指定的GID运行haproxy,建议使用专用于运行haproxy的GID,以免因权限问题带来风险;
-
group <group name>:同gid,指定运行的组名;
-
log <address> <facility> [max level [min level]]:定义全局syslog服务器,最多可定义两个;
-log-send-hostname [<string>] :在syslog信息的首部添加当前主机名,可以为"string"指定的名称,也可以缺省使用当前主机名;
-
nbproc <number> :指定启动的haproxy进程的个数,只能用于守护进程模式的haproxy;默认只启动一个进程,一般只在单进程仅能打开少数文件描述符的场景中才使用多进程模式;
-
pidfile:
-
uid:以指定的UID身份运行haproxy进程;
-ulimit-n :设定每进程能够打开的最大文件描述符数目,默认会自动进行计算,不推荐修改;
-
user:同uid,但使用的是用户名;
-
stats:
-
node:定义当前节点的名称,用于HA场景中多haproxy进程共享同一个IP地址时;
-
description:当前实例的描述信息;
* 性能调整相关的参数
-maxconn <number> :设定每个haproxy进程所接受的最大并发连接数;"ulimit -n"自动计算的结果正是参照此参数设定的;
-
maxpipes <number>:haproxy使用pipe完成基于内核的tcp报文重组,此选项则用于设定每进程所允许使用的最大pipe个数;每个pipe会打开两个文件描述符,因此,"ulimit -n"自动计算时会根据需要调大此值;默认为maxconn/4,其通常会显得过大;
-
noepoll:在Linux系统上禁用epoll机制;
-
nokqueue:在BSD系统上禁用kqueue机制;
-
nopoll:禁用poll机制;
-
nosepoll:在Linux禁用启发式epoll机制;
-
nosplice:禁止在Linux套接字上使用内核tcp重组,这会导致更多的recv/send系统调用;
-spread-checks <0..50, in percent> :在haproxy后端有着众多服务器的场景中,在精确的时间间隔后统一对众服务器进行健康状况检查可能会带来意外问题;此选项用于将其检查的时间间隔长度上增加或减小一定的随机时长;
-
tune.bufsize <number>:设定buffer的大小,同样的内存条件下,较小的值可以让haproxy有能力接受更多的并发连接,较大的值可以让某些应用程序使用较大的cookie信息;默认为16384,其可以在编译时修改,建议使用默认值;
-
tune.chksize <number>:设定检查缓冲区的大小,单位为字节;更大的值有助于在较大的页面中完成基于字符串或模式的文本查找,但也会占用更多的系统资源;不建议修改;
-
tune.maxaccept <number>:设定haproxy进程内核调度运行时一次性可以接受的连接的个数,较大的值可以带来较大的吞吐率,默认在单进程模式下为100,多进程模式下为8,设定为-1可以禁止此限制;一般不建议修改;
-
tune.maxpollevents <number>:设定一次系统调用可以处理的事件最大数,默认值取决于OS;其值小于200时可节约带宽,但会略微增大网络延迟,而大于200时会降低延迟,但会稍稍增加网络带宽的占用量;
-
tune.maxrewrite <number>:设定为首部重写或追加而预留的缓冲空间,建议使用1024左右的大小;在需要使用更大的空间时,haproxy会自动增加其值;
-
tune.rcvbuf.client <number>:
-
tune.rcvbuf.server <number>:设定内核套接字中服务端或客户端接收缓冲的大小,单位为字节;强烈推荐使用默认值;
-
tune.sndbuf.client:
-
tune.sndbuf.server:
* Debug相关的参数
-
debug
-
quiet
代理功能的配置项 :
一般分四大项:defaults ,frontend ,backend ,listen
-
defaults <name>
-
frontend <name>
-
backend <name>
-
listen <name>
"defaults"段用于为所有其它配置段提供默认参数,这配置默认配置参数可由下一个"defaults"所重新设定。
"frontend"段用于定义一系列监听的套接字,这些套接字可接受客户端请求并与之建立连接。
"backend"段用于定义一系列"后端"服务器,代理将会将对应客户端的请求转发至这些服务器。
"listen"段通过关联"前端"和"后端"定义了一个完整的代理,通常只对TCP流量有用。
balance :指明调度算法
动态算法:权重可动态调整
静态算法:翘筝权重不会实时生效
roundrobin:轮询,基于权重进行轮叫,是动态算法,每个后端主机最多支持4128个链接;
static-rr:轮询,静态算法,每个后端主机支持的链接无上限;
leastconn:新的连接请求被派发至具有最少连接数目的后端服务器;动态算法,仅适用于长连接的会话。
source:将请求的源地址进行hash运算,并由后端服务器的权重总数相除后派发至某匹配的服务器;可以使得同一个客户端IP的请求始终被派发至某特定的服务器;默认为静态,可以使用hash-type修改此特性;
对于hash-type,语法 hash-type <method>,其可用方法有map-based和consistent,默认一般是map-based。
map-based:取模法,静态
consistent:一致性哈希法,动态
测试:将前面的实验配置参数balance由roundrobin改为source
此时,在浏览器中访问haproxy的地址,无论刷新多少次,返回的一直是同一后端服务器的内容,即对同一客户端,请求被固定发送到同一台服务器。
uri:对URI的左半部分或整个URI进行hash运算,并由服务器at的总权重相除后派发至某匹配的服务器,这可以使得对同一个URI的请求总是被派发至某特定的服务器,也需要hash-type指定类型;这里要涉及URL的知识,URL的语法:
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
http://www.baidu.com/lookup.jsp?key=123
左边部分一般是指?号左边的部分。
测试:将balance 改为uri,同时hash-type设为consistent:

为各服务器创建不同的网页:
for i in {1..10}; do echo "<h1>Server On Node2:133</h1>" > test$i.html; done
然后进行访问测试,第一次访问一个网页,如http://192.168.61.129/test1.html,如果返回的是node2服务器,那么以后不论从哪个客户端访问test1.html,返回的都是node2服务器内容,即根据uri的hash值,被固定转发到node2上。
url_param:根据url中的指定参数的值进行调度,通过<argument>为URL指定的参数在每个HTTP GET请求中将会被检索;如果找到了指定的参数且其通过等于号"="被赋予了一个值,那么此值将被执行hash运算并被服务器的总权重相除后派发至某匹配的服务器;需要hash-type指定类型;
hdr(<name>):hdr代表header,即根据请求报文的某个首部字段(如user_agent,referer,hostname等)值进行调度;
测试:将balance改为hdr(User-Agent),同一种浏览器固定在同一台服务器上。
bind:绑定frontend的监听端口,也可用于listen
bind [<address>]:<port_range> [, ...]
bind [<address>]:<port_range> [, ...] interface <interface>
将配置文件中:
frontend main *:80
改成:可以绑定多个端口
frontend main
bind *:80
bind *:8080
mode{ tcp | http | health }:指明haproxy实例的运行模式或协议,默认tcp。
log :
log global
log <address> <facility> [<level> [<minlevel>]]
为每个实例启用事件和流量日志,因此可用于所有区段。每个实例最多可以指定两个log参数,不过,如果使用了"log global"且"global"段已经定了两个log参数时,多余了log参数将被忽略。
maxconn <conns>:设定一个前端的最大并发连接数,其不能用于backend区段。
default_backend <backend>:在没有匹配的"use_backend"规则时为实例指定使用的默认后端,因此,其不可应用于backend区段。在"frontend"和"backend"之间进行内容交换时,通常使用"use-backend"定义其匹配规则;而没有被规则匹配到的请求将由此参数指定的后端接收。
server<name> <address>[:port] [param*]:为后端声明一个server,因此,不能用于defaults和frontend区段。
param*参数:
backup:设定为备用服务器,仅在负载均衡场景中的其它server均不可用于启用此server;
check:启动对此server执行健康状态检查;
inter <delay>:设定健康状态检查的时间间隔,单位为毫秒,默认为2000;
rise <count>:健康检查中,某离线的server从离线状态转换至正常状态需要成功检查的次数;
fall <count>:确认server从正常状态转换为不可用状态需要检查的次数;
cookie <value>:为指定server设定cookie值,此处指定的值将在请求入站时被检查,第一次为此值挑选的server将在后续的请求中被选中,其目的在于实现持久连接的功能;
maxconn <maxconn>:指定此服务器接受的最大并发连接数;
maxqueue <maxqueue>:设定请求队列的最大长度;
observe <mode>:通过观察服务器的通信状况来判定其健康状态;
weight <weight>:权重,默认为1,最大值为256,0表示不参与负载均衡;
redir <prefix>:重定向功能,将发往此服务器的GET和HEAD请求均以302状态码响应;注意,在prefix后面不能使用/,且不能使用相对地址,以免造成循环;
对backend server做健康状态检查方法(仅对http服务):
option httpchk
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>:不能用于frontend段。
基于浏览器cookie实现session sticky(cookie参数用法):
cookie<name> [ rewrite | insert | prefix ] [ inderect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]:

这里的cookie不是server配置项中参数cookie,但要结合server中的cookie实现功能。
第一次请求:

可以看到response报文首部cookie中设置了SERVERID这个cookie,其值为Fnode,此后,客户端的请求报文中会携带这个cookie值, 所有带有此cookie的请求被转发至node节点服务器上,实现会话级绑定。

这里注意backend配置段中cookie参数中name,这里是SERVERID是要插入cookie中的key,而server配置项中的cookie参数指定特定服务器的cookie的值。
(1)每个server有自己唯一的cookie标识;(2)在backend中定义为用户请求调度完成后操纵其cookie。
启用HAProxy的高级功能
stats enable
启用基于默认设置的统计报告,不能用于"frontend"区段。只要没有另外的其它设定,它们就会使用如下的配置:
-
stats uri : /haproxy?stats #状态页的uri
-
stats realm : "HAProxy Statistics" #登录时的提示信息
-
stats auth : no authentication #认证
-
stats scope : no restriction #范围
建议修改默认配置,以免其依赖于默认设定而带来非法后果。
示例:
backend public_www
server websrv1 192.168.147.100:80
stats enable
stats hide-version
stats scope .
stats uri /haproxyadmin?stats
stats realm Haproxy\ Statistics
stats auth statsadmin:password
stats auth statsmaster:password
一般单独设置状态监控段,测试:

stats scope . 显示当前区段状态,即只显示了stattistics:

采用默认值,去掉stats scope .

配置访问路径和认证:


此时的状态页只是查看功能,在配置中增加stats admin语句,启动管理功能:


capture request header <name> len <length>:捕获并记录指定的请求首部最近一次出现时的第一个值,仅能用于"frontend"和"listen"区段。捕获的首部值使用花括号{}括起来后添加进日志中。
capture response header <name> len <length>:捕获被记录指定的响应首部的信息。其他同request。
option httplog:启用记录HTTP请求、会话状态和计时器的功能。丰富日志记录信息。
option logasap
no option logasap:启用或禁用提前将HTTP请求记入日志。
option forwardfor [ except <network> ] [ header <name> ] [ if-none ]:允许在发往服务器的请求首部中插入"X-Forwarded-For"首部。
errorfile <code> <file>:在用户请求不存在的页面时,返回一个页面文件给客户端而非由haproxy生成的错误代码;<code>:可用的状态码有200、400、403、408、500、502、503和504;
<file>:指定用于响应的页面文件,使用HAProxy主机的本地文件进行响应;
errorloc <code> <url>
errorloc302 <code> <url>
errorloc303 <code> <url>:请求错误时,返回一个HTTP重定向至某URL的信息给客户端。
http_request{ allow | deny | auth [realm <realm>]} [ { if | unless } <condition>]:访问控制
option http-server-close与no option http-server-close:服务器端断开连接
option http-pretend-keepalive与no option http-pretend-keepalive:长连接与否
resadd 与rspadd:自定义请求或响应报文首部
timeout xxx:超时配置
ACL的配置:
haproxy的ACL用于实现基于请求报文的首部、响应报文的内容或其它的环境状态信息来做出转发决策。其配置法则通常分为两步,首先去定义ACL,即定义一个测试条件,而后在条件得到满足时执行某特定的动作。定义ACL的语法格式如下:
acl <aclname> <criterion> [flags] [operator] <value> ...
be_sess_rate <integer>:用于测试指定的backend上会话创建的速率(即每秒创建的会话数)
fe_sess_rate <integer>:用于测试指定的frontend(或当前frontend)上的会话创建速率是否满足指定的条件
hdr(header) <string>:用于测试请求报文中的所有首部或指定首部是否满足指定的条件。
method <string>:测试HTTP请求报文中使用的方法。
path_beg <string>:用于测试请求的URL是否以<string>指定的模式开头。
path_end <string>:用于测试请求的URL是否以<string>指定的模式结尾。
hdr_beg <string>:用于测试请求报文的指定首部的开头部分是否符合<string>指定的模式。
hdr_end <string>:用于测试请求报文的指定首部的结尾部分是否符合<string>指定的模式。
url_beg 与 url_end:url是否开头或结尾是否符合指定的模式
path_reg与url_reg:path或url是否匹配正则表达式。
动静分离示例:
frontend http-in
bind *:80
mode http
log global
option httpclose
option logasap
option dontlognull
capture request header Host len 20
capture request header Referer len 60
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .jpeg .gif .png .css .js
use_backend static_servers if url_static
default_backend dynamic_servers
backend static_servers
balance roundrobin
server imgsrv1 172.16.200.7:80 check maxconn 6000
server imgsrv2 172.16.200.8:80 check maxconn 6000
backend dynamic_servers
cookie srv insert nocache
balance roundrobin
server websrv1 172.16.200.7:80 check maxconn 1000 cookie websrv1
server websrv2 172.16.200.8:80 check maxconn 1000 cookie websrv2
server websrv3 172.16.200.9:80 check maxconn 1000 cookie websrv3
通过上面的配置,可以看到,HAProxy的配置大多数与web服务,即与HTTP协议的知识有关,所以要熟练掌握HAProxy,需要对WEB服务相关知识进行学习。