HAproxy简单概述

HAProxy是免费极速且可靠的用于为TCP和基于HTTP应用程序提供代理服务的解决方案。是工作在应用空间的程序,跟Nginx一样受限于套接字。且只类同于Nginx四层和七层代理服务器仅此而已。但更多用在web反向代理上!!! 
HAProxy还可以将后端的服务器与网络隔离,起到保护后端服务器的作用。HAProxy的负载均衡能力虽不如LVS,但也是相当不错。而且由于其工作在7层,可以对http请求报文做深入分析,按照自己的需要将报文转发至后端不同的服务器(例如动静分离),这一点工作在4层的LVS无法完成。 
其中一点点不同Nginx的是一个Master Worker模型负责接收用户请求,装载配置文件,平滑升级…但是更多的用户请求是通过worker来实现。而HAProxy是单一进程模型,支持多进程,但更多建议使用单一进程模型。使用单个进程直接响应用户多个请求,不启用子进程。也能够支持单进程巨大并发连接数,且必须支持事件驱动,如果不支持事件驱动,并发效率是可想而知。

HAproxy工作模式

HAProxy的工作模式一般有两种 
tcp模式:实例运行于TCP模式,在客户端和服务器端之间将建立一个全双工的连接,且不会对7层报文做任何类型的检查,只能以简单模式工作。此为默认模式,通常用于SSL、SSH、SMTP等应用。 
http模式:实例运行于HTTP模式,客户端请求在转发至后端服务器之前将被深度分析,所有不与RFC格式兼容的请求都会被拒绝。 
注意:当实现内容交换时,前端和后端必须工作于同一种模式(一般都是HTTP模式),否则将无法启动实例。 
工作模式可通过mode参数在default,frontend,listen,backend中实现定义。 
两种模块分别是:通过mod_tcp来限定反代模式和mod_http来实现负载均衡 
帮助文档地址:http://cbonte.github.io/haproxy-dconv/,其它的地址就别想了

HAproxy演示环境

实验平台:Windows10_64 
虚拟环境:基于Win系统搭建的VMware虚拟主机实现 
所需要的虚拟主机:三台,CentOS 7.2或者是CentOS 6.8,偏移CentOS 7.2 
虚拟主机各IP:node1–10.1.15.40, node2–10.1.15.41 node3–10.1.15.42 
node1节点–10.1.15.40,安装haproxy,其它两个节点做后端轮询。 
base收录版本1.5,但是马上要废弃了。官方最新版是1.7开发板(演示1.5)

HAproxy的安装

haproxy已经收录到了base仓库,所以我们直接yum安装就可以了,因为我这里实验使用的Centos7系统,所以还是使用YUM,其它发行版本可另行抉择。 
如果遇到yum安装报错You could try using –skip-broken to work around the problem 
可移除yum.repos.d目录下的所有文件,只留base.repo文件然后yum clean all 
再执行yum install

[root@localhost yum.repos.d]# yum install haproxy -y

HAproxy程序环境:

配置文件:/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:前后端的直接组合;

简单实现用haproxy实现后端主机代理,简单均衡 
1、> vim /etc/haproxy.cfg 在haprxoy 10.1.15.40主机上修改以下内容 
frontend main 
bind *:80,*:8080 
default_backend web 
# static backend for serving up p_w_picpaths, stylesheets and such 
backend web 
balance roundrobin 
server web1 10.1.15.41:80 check 
server web2 10.1.15.42:80 check 
2、在后端两台主机安装httpd启动服务,给指定网页分别叫backend1和backend2 
10.1.15.41 
vim /var/www/html 
<h1> backend 1 <h1> 
10.1.15.42 
vim /var/www/html 
<h1> backend 2 <h1> 

HAproxy相关配置参数全局段:

global配置参数: 
进程及安全配置相关的参数:user/uid, group/gid, nbproc, ulimit-n, ca-base, … 
log < address > [len < length >] < facility > [ max level [min level]] 
定义日志相关配置属性address是日志服务器的地址, [len < length >]是每行日志记录的最大长度 
举例:如何记录haproxy的日志呢? 
1、 让haproxy服务器启用接收远程主机所传来的日志信息 
2、 如果启用local2.,指明local2.日志记录于何处 
3、如果启用日志,使用udp和tcp都可以,我们这里使用的udp,把注释去掉

vim /etc/rsyslog.conf 第一步
#Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

指明日志记录到哪里去  第二步
#Save boot messages also to boot.log
local2.*   /var/log/haproxy.log
systemctl restart rsyslog.service,确保UDP 514端口被监听
[root@localhost log]# cat haproxy.log 
Nov 12 13:26:24 localhost haproxy[23610]: 10.1.15.85:6703 [12/Nov/2016:13:26:24.829] main web/web1 0/0/0/3/3 200 280 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1"

性能调优相关参数 
maxconn < number>:设定单haproxy进程的最大并发连接数; 
maxconnrate < number>:设定单haproxy进程每秒所能接受的连接数; 
maxsslconn < number>:设定单haproxy进程的ssl连接最大并发连接数; 
maxsslrate < number>:单haproxy进程的ssl连接的创建速率上限; 
spread-checks <0..50, in percent> 
向后端主机做健康状态检测时,该如何散开检测机制 
tune.bufsize < number> 缓冲池大小 
tune.rcvbuf.client < number> 接收客户端请求时缓冲池大小 
tune.rcvbuf.server < number> 接收后端服务器的响应时缓冲池大小 
tune.sndbuf.client < number> 向客户端发送响应 
tune.sndbuf.server < number> 向服务端发送请求 
tune.ssl.cachesize < number> ssl的缓存大小 
tune.ssl.lifetime < timeout> ssl的缓存会话的有效时长 
Debugging: 
debug 尽量输出详细信息 
quiet 不输出详细信息 
Userlists:定义用户、组及用户列表; 
userlist < listname > 
group < groupname > [users < user >,< user >,(…)] 
user < username > [password|insecure-password < password >] 
strong text[groups < group >,< group >,(…)] 
Peers:把多个haproxy定义构建为同步集群 
peer 
peers 
其它未尽详细的参数请参考官方帮助文档 
http://cbonte.github.io/haproxy-dconv/1.5/configuration.html#3.1

HAproxy相关配置参数代理段:

关于代理段的详细参数在这里就不多说,还是请参考官方文档,太多了看起来头疼 
简要说下HAproxy的工作模式”Mode” 
Mode可以工作在default,listen.backend和frontend中,用来定义HAproxy到底工作在应用层还是传输层 
Mode主要用来定义haproxy的三种工作模型: 
tcp:基于layer4实现代理,可代理大多数基于tcp的应用层协议,例如ssh/mysql/pgsql等; 
http:工作在应用层,客户端的http请求会被深度解析; 
health:工作为健康状态检查响应模式,当请求到达时仅回应“OK”即断开连接;

HAproxy调度算法:

定义负载均衡的算法除了listen和backend段中也可以放在defaults段中,定义格式: 
balance < algorithm > [ < arguments > ] 
balance url_param [check_post [< max_wait >]] 
常见的调度算法 
roundrobin: 
动态算法:支持权重的运行时调整,支持慢启动;仅支持最大4095个后端活动主机 
server后面使用weight来定义权重; 
static-rr: 
基于权重进行轮询与roundrobin类似静态算法:不支持权重的运行时调整及慢启动;但后端主机数量无限制; 
leastconn: 
新的连接请求被派发至具有最少连接数目的后端服务器,动态算法,适用于较长时间会话的场景。 
source: 
将请求的源地址进行hash运算,并与后端服务器的总权重作取模运算后调度至某台服务器;同一IP地址的请求将始终被调度至某特定的服务器,可以使用hash-type修改此特性来确定动态还是静态算法 
hash表如何建立映射:除权重取模法,一致性hash 
hash-type: 
map-based:取模法,hash数据结构是静态数组; 
consistent:一致性哈希,哈希的数据结构是“树”; 
uri: 
对URI的左半部分(“?”之前的部分)或整个URI进行hash运算,并与后端服务器的总权重作取模运算后调度至某台服务器;同一URI的请求将始终被调度至某特定的服务器,静态算法,可以使用hash-type修改此特性;

hdr(< name >): 
根据用户请求报文中指定的http首部的值进行调度,常用于实现对同一个虚拟主机的请求始终发往同个backend server。 
first: 
先到先得,服务器名称标识字符最短的优先调用。一台用完才用第二台,忽略权重。

**演示示例**

更改调度算法为source,把同一IP地址的请求将始终被调度至某特定的服务器
vim /etc/haproxy/haproxy.cfg
backend web
    balance     roundrobin //修改为source
    server       web1 10.1.15.41:80 check
    server       web2 10.1.15.42:80 check
systemctl reload haproxy.service //测试:http://19.1.15.40 

更改调度算法为uri,把同一页面请求通过调度算法发往到后端指定服务器
vim /etc/haproxy/haproxy.cfg
backend web
    balance     roundrobin //修改为uri
    server       web1 10.1.15.41:80 check
    server       web2 10.1.15.42:80 check
for i in {1..10}; do echo "test page $i at BACKEND 1 " > /var/www/html/test$i.html; done
systemctl reload haproxy.service

测试:
for i in {1..10}; do curl http://10.1.15.40/test$i.html;done
结果:把同一个被请求到的页面始终发往到指定的后端服务器上

更改调度算法为hdr,把同一浏览器的请求,始终发往到后端指定服务器上
vim /etc/haproxy/haproxy.cfg
backend web
    balance     roundrobin //修改为hdr(User-Agent)
    server       web1 10.1.15.41:80 check
    server       web2 10.1.15.42:80 check
systemctl reload haproxy.service
测试:
for i in {1..10}; do curl http://10.1.15.40/test$i.html;done 
结果:只要浏览器一样,请求始终被调度到指定后端服务器上

调整服务器的最大并发连接数,并启用stats页面做认证

定义并发数有两种方法: 
一种,全局定义,一种默认定义 
vim /etc/haproxy/haproxy.cfg 
frontend main //在此下面定义maxconn 10000 
bind * :80, * :8080 
maxconn 10000 
定义stats页面,在代理配置段四项中都可以定义 
vim /etc/haproxy/haproxy.cfg 
frontend main 
bind :80,:8080 
maxconn 10000 //最大并发连接数 
stats enable // 开启stats页面 
stats uri /admin?stats //自定义stats页面 
default_backend web //默认的后端主机标识web 
stats realm stats\ page\ area //开启认证界面 
stats auth admin:admin //认证用户名密码 
stats hide-version //隐藏版本信息 
stats refresh 5s //指定stats页面5秒刷新一次 
stats admin if TRUE //内建访问控制列表 
测试:http://10.1.15.40/haproxy?stats

对后端服务器做健康状况检测

check为server的参数,可启动对此server执行健康状态的检测。check借助其额外的参数可实现更精细的监测机制。 
inter < delay>: 
健康状态检测的时间间隔,单位为毫秒,默认为2000,可以使用fastinter和downinter来根据服务器端状态优化此时间延迟 
rise < count>: 
健康状态检测中某离线的server从离线状态转换至正常状态需要成功检查的次数 
fall < count>: 
确认server从正常状态转换为不可用状态需要检查的次数 
默认为传输层检测,即探测端口是否能响应。 
需要执行应用层检测,则需要 
httpchk, smtpchk, mysql-check, pgsql-check, ssl-hello-chk; 
vim /etc/haproxy/haproxy.cfg 
backend web //下面修改内容 
balance roundrobin 
server web1 10.1.15.41:80 weight 2 maxconn 5000 check inter 1 rise 1 fall 2 
server web2 10.1.15.42:80 weight 1 maxconn 3000 check inter 1 rise 1 fall 2 
测试:10.1.15.40/haproxy?stats

对后端服务器做加权轮询

vim /etc/haproxy/haproxy.cfg
backend web //在后端主机下列加入
balance roundrobin
server  web1 10.1.15.41:80 check weight 2 maxconn 5000 cookie web1
server  web2 10.1.15.42:80 check weight 1 maxconn 3000 cookie web2
systemctl reload haproxy.service

测试:
for i in {1..10}; do curl http://10.1.15.40/index.html;done 
结果:权重weight,并发maxconn,指定的值cookie 
注意:修改后端服务器的调度算法:一定要重启haproxy的服务,reload是不成功

基于cookie的session绑定

在响应报文中添加cookie信息,下一次的客户请求会带上这个cookie信息,服务器端根据cookie将请求始终定向至后端的某一台服务器,可用于保持session会话。 
而cookie信息该怎么插入进来 
rewrite: 重新改写原有的所有cookie 
insert: 在原有cookie信息当中插入 
prefix: 在原有cookie附加为前缀

举例:把基于浏览器的用户会话访问,对当前服务器讲第一次调度到某个主机,那么就调度某主机
vim /etc/haproxy/haproxy.cfg
backend web //在后端主机下列加入
balance roundrobin
cookie webserver insert nocache indirect
server  web1 10.1.15.41:80 check weight 2 maxconn 5000 cookie web1
server  web2 10.1.15.42:80 check weight 1 maxconn 3000 cookie web2
服务器第一次为某客户度挑选中的主机,会把webserver中的参数值赋值到web1上或者web2上
systemctl reload haproxy.service

测试:
for i in {1..10}; do curl http://10.1.15.40;done 
[root@localhost haproxy]# curl -I 10.1.15.40

自定义haproxy错误页面

vim /etc/haproxy/haproxy.cfg
frontend  main 
bind *:80,*:8080
maxconn 10000
stats enable
default_backend web
stats realm stats\ page\ area
stats auth admin:admin
stats hide-version
stats refresh 5s
stats admin if TRUE
#errorfile 503 /etc/haproxy/errorfiles/503sorry.http //直接以文件形式显示错误页面
errorloc 503 http://10.1.15.40:9527/errorpagetest.html // 直接以url形式显示错误页面,重定向302
errorloc 503 http://www.baidu.com
mkdir /etc/haproxy/errorfiles      
vim  /etc/haproxy/errorfiles/503sorry/http
< h1 >sorry page home < h1 >
systemctl reload haproxy.service

测试:stop后端所有主机,然后请求haproxy前端主机地址 :10.1.15.40option forwardfor

客户端的请求经前端的代理服务器转发至后端的web服务器,代理服务器在转发时将目标地址改为后端的某台web服务器地址,将源地址由client ip(客户端地址)改为自己面向后端服务器的地址。后端的web服务器若使用默认格式记录日志,则记录的客户端IP地址都为前端的代理服务器地址。这时需要在发往后端的请求报文中添加内容为客户端IP地址的首部,以便后端的web服务器能够正确获取客户端地址。 
x-forwardfor 
在配置文件默认段里已经定义了转发,所以我们直接用就可以了。 
vim /etc/haproxy/haproxy.cfg 
defaults 
option forwardfor except 127.0.0.0/8 
然后修改两台后端主机的httpd.conf文件,vim /etc/httpd/conf/httpd.conf 
把LogFormat %h修改为{X-forwarded-For}i 
重启服务systemctl restart httpd.service 
在haproxy前端主机上刷新下页面。然后在后端主机看日志是否记录的是客户端的真实地址就可以。 
tail /var/log/httpd/access.log看请求的是真实的客户端地址功能就实现了。

修改请求或响应报文首部相关:

增加响应报文相关信息 
frontend main 
rspadd X-Via:\ HAProxy/1,5 
rspidel Server.* 
rspadd X-Via:\ HAProxy/1,5 
客户端请求看一看在Response Headers报文里有没有值:X-Via:HAProxy/1,5 
删除响应报文Server相关信息 
rspidel Server.*

ACL basics

HAPAroxy的ACL能够通过检测请求报文的首部、响应报文的内容或其他的环境状态信息作出转发决策,增强了其配置弹性。 
配置分两步骤:首先定义ACL,即定义一个测试条件,再定义动作,即满足测试条件的情况下执行的某特定动作。 
语法格式: 
acl < aclname> < criterion> [flags] [operator] [< value>] … 
取值类型: 
– boolean 
– integer or integer range 
– IP address / network 
– string (exact, substring, suffix, prefix, subdir, domain) 
– regular expression 
– hex block 
匹配的操作符:数值匹配,字符串匹配,逻辑条件等… 
注意注意:语法太多,让人很蛋疼,简直太头疼,咋么那么多 
注意注意:自定义ACL全部都是小写

四层匹配,举例:
vim /etc/haproxy/haproxy.cfg
在 frontend  main 中定义
acl myhost src 10.1.15.85
acl myport dst_port 8080
block if !myhost myport
任何人试图去访问8080端口时,但不是来自于myhost主机就全部拒绝 
七层匹配,举例:
acl text_file  path_end  -i  .txt
block if text_file
任何人去试图访问txt文件,结尾不区分大小写的全部拒绝
举例:匹配某个浏览器类型
acl chrome hdr_reg(User-Agent) -i .*chrome.*$
block if chrome

HTTP层访问控制指令

acl valid_method method GET HEAD
http-request deny if ! valid_method 
举例:
acl myhost src 10.1.15.40
http-request deny if url_admin !myhost

动静分离

frontend main *:5000 
acl url_static path_beg -i /static /p_w_picpaths /javascript /stylesheets 
acl url_static path_end -i .jpg .gif .png .css .js 
use_backend static if url_static //如果上面的条件满足调至backend static主机 
default_backend app //否则调至app主机 
backend static 
balance roundrobin 
server static 127.0.0.1:4331 check 
round robin balancing between the various backends 
backend app 
balance roundrobin 
server app1 127.0.0.1:5001 check 
server app2 127.0.0.1:5002 check 
server app3 127.0.0.1:5003 check 
server app4 127.0.0.1:5004 check 

done!!!