本章概要
- HAProxy介绍
- HAProxy配置
- ACL
1、HAProxy介绍
HAProxy介绍
- 前言
lvs性能强,功能弱;nginx(haproxy)功能多,性能相对较弱,因此,一般情况下,在前端使用LVS四层调度器加haproxy七层调度的结构 - HAProxy: 是法国人Willy Tarreau开发的一个开源软件,是一款应对客户
端10000以上的同时连接的高性能的TCP和HTTP负载均衡器。其功能
是用来提供基于cookie的持久性,基于内容的交换,过载保护的高级流
量管制,自动故障切换,以正则表达式为基础的控制运行时间,基于
Web的报表,高级日志记录以帮助排除故障的应用或网络及其他功能 - LB Cluster:
四层:lvs, nginx(stream),haproxy(mode tcp)
七层:http: nginx(http), haproxy(mode http), httpd... - 官网:
http://www.haproxy.org
https://www.haproxy.com - 文档:https://cbonte.github.io/haproxy-dconv/
HAProxy功能
-
HAProxy是TCP / HTTP反向代理服务器,尤其适合于高可用性环境
可以针对HTTP请求添加cookie,进行路由后端服务器
可平衡负载至后端服务器,并支持持久连接
支持基于cookie进行调度
支持所有主服务器故障切换至备用服务器
支持专用端口实现监控服务
支持不影响现有连接情况下停止接受新连接请求
可以在双向添加,修改或删除HTTP报文首部
支持基于pattern实现连接请求的访问控制
通过特定的URI为授权用户提供详细的状态信息 -
版本:1.4、1.5、1.6、1.7、1.8、1.9
-
HAProxy结构
-
支持http反向代理
-
支持动态程序的反向代理
-
支持基于数据库的反向代理
2、HAProxy配置
HAProxy组成
- 程序环境:
主程序:/usr/sbin/haproxy
配置文件:/etc/haproxy/haproxy.cfg
Unit file:/usr/lib/systemd/system/haproxy.service - 配置段:
global:全局配置段
进程及安全配置相关的参数
性能调整相关参数
Debug参数
proxies:代理配置段
defaults:为frontend, backend, listen提供默认配置
frontend:前端,相当于nginx中的server {}
backend:后端,相当于nginx中的upstream {}
listen:同时拥有前端和后端,适用于一对一环境
示例1:
frontend web
bind *:80
default_backend websrvs
backend websrvs
balance roundrobin
server srv1 192.168.0.101:80 check
server srv2 192.168.0.102:80 check
示例2:
RS1:
[root@rs1 conf.d]# vim vhosts.conf
<VirtualHost *:80>
ServerName www.a.com
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
RS2:
[root@rs1 conf.d]# vim vhosts.conf
<VirtualHost *:80>
ServerName www.a.com
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
haproxy服务器:
删除或注释defaults以下的所有配置,重新配置haproxy配置文件
vim /etc/haproxy/haproxy.cfg
frontend web
bind *:80
default_backend websrvs
backend websrvs
balance roundrobin 轮询调度算法
server srv1 192.168.32.130:80
server srv2 192.168.32.131:80
重新加载服务
systemctl reload haproxy
查看端口号,发现默认5000端口消失,而80端口被监听,这里的80端口就是frontend定义的80端口,即frontend定义哪个端口,系统将会自动监听该端口
测试:while true ;do curl www.a.com;sleep 0.5 ;done;
注意:haproxy即使在server后不加check,也具有健康监测功能,当关闭后端服务器RS1和RS2中的某一台服务器时,会自动调度到另外一台服务器上,且返回代码为503
global配置
- global配置参数:
- 进程及安全管理:chroot, deamon,user, group, uid, gid
nbproc <number> 要启动的haproxy的进程数量,系统默认单进程,要求使用daemon模式
ulimit-n <number> 每个haproxy进程可打开的最大文件数,系统自动会指定,不建议设置
daemon 后端方式运行,建议使用
log 定义全局的syslog服务器;最多可以定义两个
log <address> [len <length>] <facility> [max level [min level]]
address: rsyslog服务器地址
len: 记录日志的长度,默认1024
日志系统
- log:
log global
log <address> [len <length>] <facility> [<level> [<minlevel>]]
length 日志行的长度,默认1024
no log
注意:
默认发往本机的日志服务器
(1) local2.* /var/log/local2.log
(2) $ModLoad imudp
$UDPServerRun 514 - log-format <string>:
课外实践:参考文档实现combined格式的记录
示例:
haproxy配置文件中,日志配置如下:
global
log 127.0.0.1 local2
注意:local2是指保存本地日志local0-loca7文件中的一个,由于指定的日志路径为本地ip地址127.0.0.1,因此必须开启网络功能才能保存haproxy日志
在rsyslog日志配置文件中配置haproxy日志:
vim /etc/rsyslog.conf
$ModLoad imudp 启用日志模块
$InputTCPServerRun 514 监听udp/514端口,接收日志
local2.* /var/log/haproxy.log 设置保存日志的文件
重启日志服务
systemctl restart rsyslog
查看日志:
cat /var/log/haproxy.log
注意:由于haproxy的日志是基于网络指向其他主机来保存日志,
因此当改变日志保存位置时,需要把ip地址指向保存日志的主机ip,
并开启网络日志功能以及监听514端口
日志管理
- 将特定信息记录在日志中
- capture cookie <name> len <length>
捕获请求和响应报文中的 cookie并记录日志 - capture request header <name> len <length>
捕获请求报文中指定的首部并记录日志
示例:
capture request header X-Forwarded-For len 15
- capture response header <name> len <length>
捕获响应报文中指定的首部并记录日志
示例:
capture response header Content-length len 9
capture response header Location len 15
性能调整
- 性能调整:
maxconn <number>:设置每个haproxy进程所能接受的最大并发连接数
maxconnrate <number>:设置每个进程每秒种所能建立的最大连接数量
maxsessrate <number>:设置每个进程每秒种所能建立的最大会话数量
注意:连接数和会话数的区别在于,连接属于OSI七层参考模型的传输层,会话属于ISO参考模型的应用层
maxsslconn <number>: 每进程支持SSL的最大连接数量
spread-checks <0..50, in percent> 健康检测延迟时长百分比,建议2-5之间
健康检测延迟时长百分比,建议2-5之间
注意:当进行健康监测时,会向后端服务器发送健康监测数据,如果后端服务器数量较多,并且同时发送检测数据,会对haproxy服务器造成较大的负载压力,因此可以把检测延时发送,分开来发即可减轻haproxy的压力
另外,如果延时设置为50%,那么前一个延时50%,后一个提前50%,会造成检测数据发送时间正好相连接,也会造成瞬间数据的负载压力,因此推荐延时为2%-5%之间
配置段
- 代理配置段:
- defaults <name>
- frontend <name>
- backend <name>
- listen <name>
- Frontend段:指定接收客户端连接侦听套接字设置
- Backend段:指定将连接请求转发至后端服务器的相关设置
- Listen段:指定完整的前后端设置
- proxy 名称:使用字母 数字 - _ . : 并区分字符大小写
配置参数
- 配置参数:
- bind:指定一个或多个前端侦听地址和端口
bind [<address>]:<port_range> [, ...] [param*]
示例:
listen http_proxy
bind :80,:443
bind 10.0.0.1:10080,10.0.0.1:10443
bind /run/ssl-frontend.sock user root mode 600 accept-proxy
Balance配置
- balance:后端服务器组内的服务器调度算法
balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post] - 调度算法:
(1) roundrobin:基于权重轮询,动态算法,支持权重的运行时调整,支持慢启动;每个后端backend中最多支持4095个server
server options: weight #
动态算法,更改配置文件后,无需重启服务,只需重新加载服务即可
慢启动,当新上线一台服务器后,把集群中其他服务器的访问量缓慢的调度到该服务器上,让该服务器动态平滑的负载访问数据,避免把大量的访问量瞬间调度到该服务器而导致服务器崩溃
示例:roundrobin算法
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
balance roundrobin
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
重新加载服务(动态算法,无需重启,只需重新加载即可)
systemctl reload haproxy.service
(2) static-rr:基于权重轮询,静态算法,不支持权重的运行时调整及慢启动;后端主机数量无上限
(3) leastconn:加权最少连接,动态算法,最少连接的后端服务器优先分配接收新连接,相同连接时轮询,推荐在较长会话的场景使用,例如MySQL,LDAP等,不适合http
(4) first:根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务,不支持权重
(5) source:源地址hash,新连接先按权重分配,后续连接按source分配请求,实现会话绑定
需要维护客户端ip地址hash值与服务器的对应关系表,消耗服务器内存,因此先把hash值对权重取模,按照取模所得到的值进行调度,无需记录对应关系表
缺点:(1)源地址通过snat转换,可能存在大量私网ip客户端,无法精确进行调度。(2)一旦权重发生变化,会对服务器调度产生很大影响,需要使用一致性hash算法解决
(6) uri:
对URI的左半部分或整个uri做hash计算,并除以服务器总权重取模,以后派发至某挑出的服务器,适用于后端缓存服务器
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分:/<path>;<params>
整个uri:/<path>;<params>?<query>#<frag>
示例:
在后端服务器生成不同的html页面,请求不同的html页面可能会调度到不同的服务器
注意:uri调度算法可以跟上hash算法
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
balance uri
hash-type consistent 一致性hash算法
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
重新加载服务(动态算法,无需重启,只需重新加载即可)
systemctl reload haproxy.service
(7) url_param:
对用户请求的uri中的<params>部分中的参数的值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个Backend Server
http://www.magedu.com/bbs/hello;type=title
(8) hdr(<name>):对于每个http请求,此处由<name>指定的http首部将会被取出做hash计算;并由服务器总权重相除以后派发至某挑出的服务器;无有效值的会被轮询调度
hdr(Cookie)
示例:hdr算法
根据请求报文头部信息做hash运算进行调度
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
balance hdr(Host) 根据请求报文主机头信息进行调度
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
请求报文头部信息包括:
Host 主机头 请求同一个域名,调度到固定的服务器
User-Agent 浏览器类型 同一个浏览器类型,调度到固定的服务器
curl命令可以模拟浏览访问服务器
curl -A ie10 http://www.a.com
curl -A chrome http://www.a.com
好处:可以根据不同的浏览器类型,把请求调度到不同的服务器,如可以把手机浏览器和PC浏览的访问请求分开调度到不同的服务器
cookie 根据cookie调度,绑定session会话,进行会话保持
会话保持的三种方案:
记录调度选择
根据源ip地址判断
根据cookie信息判断用户信息,把用户调度到固定的服务器上,cookie属于应用层,因此需要应用层反向代理服务器
复制会话 把cookie复制到全部的服务器,会造成数据冗余
会话服务器 设置专门的session服务器
缺点:后端服务器出现故障,仍然会向该服务器调度访问
因此,一般情况下,session信息不会存放到后端服务器上,会单独设置存储session信息的服务器,目前主流的解决方案:redis服务器集群
(9) rdp-cookie 远程桌面相关
(10) rdp-cookie(<name>)
哈希算法
- hash-type:哈希算法
hash-type <method> <function> <modifier>
method:
map-based:除权取余法,哈希数据结构是静态数组
consistent:一致性哈希,哈希数据结构是一棵树
function : 哈希函数,取值:sdbm,djb2,wt6
modifier: 取值avalanche时,将修改哈希值,而非直接使用 - default_backend <backend>
无use_backend 匹配时,使用默认的backend,用于frontend中 - default-server [param*]
为backend中的各server设定默认选项
支持配置段:defaults,listen,backend
示例1:default_backend
frontend web
bind 172.20.120.255:80
default_backend websrvs 收到请求报文,默认发送给websrvs服务器组
backend websrvs
balance hdr(Host)
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
注意:可以根据条件判断,把满足条件的请求数据发送到默认后端服务器,不满足条件的请求数据发送到其他非默认的后端服务器,这样就可以对数据进行分流,即一个调度规则可对应多个服务器组
示例2:default-server
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
default-server inter 1000 weight 10 为服务器组设置默认值
balance roundrobin
server srv1 192.168.32.130:80 check weight 2
server srv2 192.168.32.131:80 check 没有设置权重,默认为10
示例3:listen指令
使用listen指令,可以把调度规则和后端服务器形成一一对应关系,而无需使用前端frontend和后端backend,不推荐这种写法
listen http
#frontend web
bind 172.20.120.255:80
#default_backend websrvs
#backend websrvs
balance roundrobin
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
配置
- server <name> <address>[:[port]] [param*]
定义后端主机的各服务器及其选项
server <name> <address>[:port] [settings ...]
default-server [settings ...]
<name>:后端服务器在haproxy上的内部名称;出现在日志及警告信息
<address>:后端服务器地址,支持使用主机名
[:[port]]:端口映射;省略时,表示同bind中绑定的端口
[param*]:参数
weight <weight>:权重,默认为1
maxconn <maxconn>:当前后端server的最大并发连接数
backlog <backlog>:当server的连接数达到上限后的后援队列长度
backup:设定当前server为备用服务器Sorry Server
示例:设置backup服务器,即sorryserver
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
default-server inter 1000 weight 10
balance roundrobin
server srv1 192.168.32.130:80 check weight 2
server srv2 192.168.32.131:80 check
server backupsrv 127.0.0.1:8080 check backup
注意:设置backup服务器时不能添加backlog参数,即后援队列,否则本机将无法监听80端口,另外,backup服务器的ip地址可以设置为haproxy服务器的本地回环网口的ip,内网ip地址或外网ip地址,推荐设置为内网或本地lo网卡的ip地址
健康状态检测
- check:对当前server做健康状态检测,只用于四层检测
注意:httpchk,“smtpchk”, “mysql-check”, “pgsql-check” and “ssl-hello-chk” 用于定义应用层检测方法
addr :检测时使用的IP地址
port :针对此端口进行检测
inter <delay>:检测之间的时间间隔,默认为2000ms
rise <count>:连续多少次检测结果为“成功”才标记为可用;默认为2
fall <count>:连续多少次检测结果为“失败”才标记为不可用;默认为3 - disabled:标记为不可用
应用程序版本升级,系统维护时使用
蓝绿发布 两套相同的环境,相互替换使用
金丝雀发布 应用程序升级时,先升级一部分服务器供vip使用,待应用程序稳定运行一段时间后,再全部切换
灰度发布 部分切换,逐步完成全部切换 - redir <prefix>:将发往此server的所有GET和HEAD类的请求重定向至指定的URL
示例:redir 重定向
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
balance roundrobin
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check redir http://172.20.120.255:8080
注意:使用curl命令测试时,由于redir为把请求重定向到其他链接,因此需要使用-L选项才能显示重定向的内容
另外,重定向指定的主机ip地址必须为客户端能够访问的ip地址(即外网地址),否则客户端将无法访问而造成重定向失败
cookie配置
- cookie <value>:为当前server指定cookie值,实现基于cookie的会话黏性
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
<name>:cookie名称,用于实现持久连接
rewrite:重写
insert:插入
prefix:前缀
nocache:当client和hapoxy之间有缓存时,不缓存cookie
当client和hapoxy之间有缓存时,如果cookie被缓存,当客户端访问时,会根据缓存的cookie信息把所有的请求调度到同一台服务器上,nocache是指不缓存cookie信息
注意:每个后端服务器的cookie是固定的,后端服务器有多少个,cookie值就有多少个
示例1:基于cookie的session sticky的实现
backend websrvs
cookie WEBSRV insert nocache 定义cookie的名称,相当于K/V中的key,通过调用此名称,调用cookie值
server srv1 172.16.0.6:80 weight 2 check rise 1 fall 2 maxconn 3000 cookie srv1 给cookei赋值,相当于K/V中的value
server srv2 172.16.0.7:80 weight 1 check rise 1 fall 2 maxconn 3000 cookie srv2 给cookei赋值,相当于K/V中的value
示例2:
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
cookie WEBSRV insert nocache
server srv1 192.168.32.130:80 check cookie rs1
server srv2 192.168.32.131:80 check cookie rs2
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check redir http://172.20.120.255:8080
使用curl命令测试,使用-b选项在测试时添加cookie
curl -I 172.20.120.255 查看cookie值
curl -b WEBSRV=rs1 172.20.120.255 根据cookie值进行调度到固定服务器
统计接口启用相关的参数
- stats enable
启用统计页;基于默认的参数启用stats page - stats hide-version 隐藏版本
- stats refresh <delay>
设定自动刷新时间间隔 - stats uri <prefix>
自定义stats page uri,默认值:/haproxy?stats - stats realm <realm>
认证时的realm,示例:stats realm : HAProxy\ Statistics - stats auth <user>:<passwd>
认证时的账号和密码,可使用多次,默认:no authentication - stats admin { if | unless } <cond>
启用stats page中的管理功能
示例:
listen stats
bind :9527
stats enable
stats hide-version
stats uri /hastats
stats realm HAPorxy\ Stats\ Page
stats auth admin1:password1
stats auth admin1:password2
stats refresh 3s
stats admin if TRUE
知识扩展:
启用状态页
frontend web
bind 172.20.120.255:80
default_backend websrvs
stats enable 启用状态页
stats hide-version 隐藏状态页haproxy的版本号
stats refresh 3 设置自动刷新间隔为3s
stats uri /hastatus 自定义访问状态页的uri
stats realm HAproxy\ statistics 设置验证提示信息,注意在配置文件中空格要进行转义才能被识别,因此HAproxy\ statistics中的\是指转义字符,是对空格进行转义,而且每一个空格都要用转义字符进行转义
stats auth root:centos 设置验证用户名密码
stats admin if TRUE 开启状态页管理功能,并可以设置acl权限,即什么条件下才会开启管理功能,if TRUE是指永远成立,注意TRUE为大写
backend websrvs
cookie WEBSRV insert nocache
server srv1 192.168.32.130:80 check cookie rs1
server srv2 192.168.32.131:80 check cookie rs2
默认访问路径:http://haproxy服务器ip/haproxy?stats
注意:haproxy服务器的ip是指配置文件中监听的ip地址,即bind指令绑定的ip地址
另外,如果状态页的ip地址为公网地址,为了确保安全,除了可以改变状态的uri,还可以对状态页进行验证
DRAIN 排干状态,把访问逐渐清除,清除完毕后再把机器安全下线,如果直接把服务器标记为下线会对正在访问的用户造成影响
MAINT 维护状态
haproxy状态页并不需要调度到后台服务器,因此状态页可以单独定义
可将ip地址绑定在内网地址并监听一个不常用的端口如9527,只用于内部人员管理服务器,以增加安全性
listen haproxy
bind 192.168.32.129:9527
stats enable
stats hide-version
stats refresh 30
stats uri /hastatus
stats realm HAproxy\ statistics
stats auth root:centos
stats admin if TRUE
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
cookie WEBSRV insert nocache
server srv1 192.168.32.130:80 check cookie rs1 maxconn 5000
server srv2 192.168.32.131:80 check cookie rs2
工作模式
- maxconn <conns>:为指定的frontend定义其最大并发连接数;默认为3000
- mode { tcp|http|health }
定义haproxy的工作模式
tcp:基于layer4实现代理;可代理mysql, pgsql, ssh, ssl等协议,https时使用此模式,默认模式
http:仅当代理协议为http时使用,CentOS中haproxy实际的默认模式
health:工作为健康状态检查的响应模式,当连接请求到达时回应“OK”后即断开连接,较少使用
示例:TCP模式
listen ssh
bind :22222 定义监听端口,由于没有绑定具体ip地址,该端口会发布外网,为了防止外网访问,因此使用不常用的端口
balance leastconn 最少连接算法适用于长连接,ssh远程连接属于长连接
mode tcp defaults配置段默认为http协议,使用四层调度,必须指定为tcp协议
server sshsrv1 192.168.32.130:22 check 调度到后端服务器的22端口
server sshsrv2 192.168.32.131:22 check 调度到后端服务器的22端口
在客户端进行访问测试
ssh 172.20.120.255 -p 22222 -p选项指定端口
注意:haproxy的tcp四层调度属于伪四层调度,因为一般真正的tcp四层调度会直接转发报文,而不会更改报文中的ip地址信息代替客户端访问服务器,
但haproxy把客户端的请求报文解封装,然后把自己的ip地址封装进报文,代替客户端访问后端服务器,实现四层调度,
这就改变了请求报文中源ip地址,而后端服务器看到的源ip地址就是haproxy服务器的内网地址。
健康状态检测
- 对后端服务器做http协议健康状态检测:通常用于backend
option httpchk 默认向后端服务器发请求:OPTIONS / HTTP/1.0
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>
定义基于http协议的7层健康状态检测机制
http-check expect [!] <match> <pattern>
http协议健康状态检测响应内容或指定响应码
注意:haproxy默认自带健康检测功能,但却是基于四层的健康检测,只针对ip地址,端口号等信息进行检测,无法识别应用层的数据。如果数据不存在,但ip地址和端口没有问题,仍然会向该服务器调度数据,因此并不完善。
因此需要对应用层进行健康检测,即基于七层的健康检测
关闭后端服务器网页,使用tcpdump命令抓包查看:
tcpdump -nn -i ens33 port 80
示例:
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www 定义健康检测的内容和方法,注意每一个空格都要用转义字符
http-check expect status 200 定义获取200状态码表示服务器处于健康状态,继续后续的调度
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
注意:option httpchk是基于http模式进行健康检测,因此defaults配置段中的mode必须是http模式,才能够进行健康检测
forwardfor配置
- option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部,其值为前端客户端的地址;用于向后端主发送真实的客户端IP
[ except <network> ]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络
[ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For”
[ if-none ] 如果没有首部才添加首部,如果有使用默认值 - 为指定的MIME类型启用压缩传输功能
compression algo <algorithm> ...:启用http协议的压缩机制,指明压缩算法gzip, deflate
compression type <mime type> ...:指明压缩的MIMI类型
示例:
客户端的请求报文到达haproxy服务器后,haproxy会代替客户端访问后端服务器,
因此后端服务器看到的源ip地址是haproxy服务器的ip地址,无法看到真正的客户端ip地址。
在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部,其值为前端客户端的地址;用于向后端主发送真实的客户端IP。
haproxy配置文件中defaults配置段中,已经默认添加“X-Forwarded-For”首部信息:
option forwardfor except 127.0.0.0/8 本机访问后端服务器时不添加首部信息
在后端服务器http配置文件日志格式中添加“X-Forwarded-For”字段,记录日志时能够显示该字段
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Ag
ent}i\" \"%{clientip}i\" \"{X-Forwarded-For}\"" combined
也可以自定义“X-Forwarded-For”字段的名称
option forwardfor except 127.0.0.0/8 header testheader 把“X-Forwarded-For”更改为testheader
在后端服务器http配置文件日志格式中调用该名称即可
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Ag
ent}i\" \"%{clientip}i\" \"{testheader}\"" combined
重启httpd服务
systemctl reload httpd
查看日志:
tail -f /var/log/httpd/access_log
192.168.32.129 - - [13/Nov/2018:21:19:07 +0800] "GET / HTTP/1.1" 200 11 "-"
"curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" 172.20.120.193
错误页配置
- errorfile <code> <file> 自定义错误页
<code>:HTTP status code.
支持200, 400, 403, 408, 500, 502, 503, 504.
<file>:错误页文件路径 - errorloc <code> <url>
相当于errorloc302 <code> <url>,利用302重定向至指URL
示例1:
errorfile 400 /etc/haproxy/errorfiles/400badreq.http
errorfile 408 /dev/null # workaround Chrome pre-connect bug
errorfile 403 /etc/haproxy/errorfiles/403forbid.http
errorfile 503 /etc/haproxy/errorfiles/503sorry.http
示例2:errorloc 503 http://www.magedu.com/error_pages/503.html
示例3:
自定义错误页
[root@centos7 ~]# mkdir errorfile
[root@centos7 ~]# cd errorfile/
[root@centos7 errorfile]# vim 503.html
<h1>503,forbidden</h1>
errorloc 重定向调度
当返回错误码时,调度到指定的服务器上,把错误页面集中存放在某个服务器上
构建503错误进行测试
示例:
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
errorloc 503 http://192.168.32.129:9527
errorfile 503 /etc/haproxy/errorfiles/403.http
注意:503.html 需要关闭全部的后端服务器,才能够现实503错误页面
修改报文首部
- 在请求报文尾部添加指定首部
reqadd <string> [{if | unless} <cond>] - 在响应报文尾部添加指定首部
rspadd <string> [{if | unless} <cond>]
示例:rspadd X-Via:\ HAPorxy - 从请求报文中删除匹配正则表达式的首部
reqdel <search> [{if | unless} <cond>]
reqidel <search> [{if | unless} <cond>] 不分大小写 - 从响应报文中删除匹配正则表达式的首部
rspdel <search> [{if | unless} <cond>]
rspidel <search> [{if | unless} <cond>] 不分大小写
示例: rspidel server.*
示例1:添加请求报文头部信息
frontend web
bind 172.20.120.255:80
default_backend websrvs
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
reqadd X-Via:\ HAPorxy 注意:空格要用转义字符,每一个空格都要用一个转义字符
重启服务
systemctl restart haproxy
在后端服务器配置文件日志格式中调用该头部字段“X-Via”
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Ag
ent}i\" \"%{clientip}i\" %{testheader}i %{X-Via}i" combined
查看日志:
192.168.32.129 - - [13/Nov/2018:22:17:25 +0800] "GET / HTTP/1.1" 200 11 "-"
"curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" 172.20.120.193 HAPorxy
示例2:删除响应报文头部信息
配置端:前端后端都支持
frontend web
bind 172.20.120.255:80
default_backend websrvs
rspidel server 删除响应报文中的server信息,rspidel指不区分大小写
rspadd server:\ testapache 自定义服务器名
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
reqadd X-Via:\ HAPorxy
重启服务
systemctl restart haproxy
查看响应报文头部信息,不存在server头部信息,并显示自定义的服务器名称
[root@centos6 ~]# curl -I 172.20.120.255
HTTP/1.1 200 OK
Date: Tue, 13 Nov 2018 14:35:19 GMT
Last-Modified: Mon, 12 Nov 2018 00:38:15 GMT
ETag: "b-57a6ceaa55e56"
Accept-Ranges: bytes
Content-Length: 11
Content-Type: text/html; charset=UTF-8
server: testapache 自定义服务器名称
连接超时
- timeout client <timeout>
客户端最长空闲连接超时时长 默认单位是毫秒 - timeout server <timeout>
后端服务器最长空闲连接超时时长 - timeout http-keep-alive <timeout>
持久连接的持久时长 - timeout http-request <timeout>
一次完整的HTTP请求的最大等待时长 - timeout connect <timeout>
成功连接后端服务器的最大等待时长 - timeout client-fin <timeout>
客户端半连接的空闲时长 - timeout server-fin <timeout>
后端服务器半连接的空闲时长
3、ACL
ACL
- acl:访问控制列表(ACL)的使用提供了一个灵活的解决方案来执行内容交换,并且通常基于从请求中提取的内容、响应或任何环境状态进行决策
acl <aclname> <criterion> [flags] [operator] [<value>] ...
<aclname>:ACL名称,可使用字母 数字 : . - _ ,区分字符大小写
<criterion>: 比较的标准和条件 - <value>的类型:
- boolean
- integer or integer range
- IP address / network
- string (exact, substring, suffix, prefix, subdir, domain)
- regular expression
- hex block - <flags>
-i 不区分大小写
-m 使用指定的pattern匹配方法
-n 不做DNS解析
-u 强制每个ACL必须唯一ID,否则多个同名ACL或关系
-- 强制flag结束. 当字符串和某个flag相似时使用 - [operator]
匹配整数值:eq、ge、gt、le、lt
匹配字符串:
- exact match (-m str) :字符串必须完全匹配模式
- substring match (-m sub) :在提取的字符串中查找模式,如果其中任何一个被发现,ACL将匹配
- prefix match (-m beg) :在提取的字符串首部中查找模式,如果其中任何一个被发现,ACL将匹配
- suffix match (-m end) :将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配
- subdir match (-m dir) :查看提取出来的用斜线分隔(“/”)的字符串,如果其中任何一个匹配,则ACL进行匹配
- domain match (-m dom) :查找提取的用点(“.”)分隔字符串,如果其中任何一个匹配,则ACL进行匹配 - acl作为条件时的逻辑关系:
- 与:隐式(默认)使用
- 或:使用“or” 或 “||”表示
- 否定:使用“!“ 表示
示例:
if invalid_src invalid_port 与关系
if invalid_src || invalid_port 或
if ! invalid_src 非
- <criterion> :各种条件
dst 目标IP
dst_port 目标PORT
src 源IP
src_port 源PORT
示例:acl invalid_src src 172.16.100.200
示例:
listen haproxy
bind 192.168.32.129:9527
acl valid_src src 192.168.32.1 定义acl规则valid_src
stats enable
stats hide-version
stats refresh 30
stats uri /hastatus
stats realm HAproxy\ statistics
stats auth root:centos
stats admin if valid_src 调用acl规则valid_src,只有满足该规则才能查看状态页
取反:
stats admin if ! valid_src 对该命令取反是指不满足条件的主机无法使用管理功能,即图形界面下的可选框功能
- base : string
返回第一个主机头和请求的路径部分的连接,该请求从第一个斜杠开始,并在问号之前结束,对虚拟主机有用
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
base : exact string match
base_beg : prefix match
base_dir : subdir match
base_dom : domain match
base_end : suffix match
base_len : length match
base_reg : regex match
base_sub : substring match - path : string
提取请求的URL路径,该路径从第一个斜杠开始,并在问号之前结束(无主机部分)
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
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
示例:ACL path
path_beg /images/
path_end .jpg .jpeg .png .gif
path_reg ^/images.\*\.jpeg$
path_sub image
path_dir jpegs
path_dom magedu
/images/jpegs/20180123/logo.jpg
- url : string
提取请求中的URL。一个典型的应用是具有预取能力的缓存,以及需要从数据库聚合多个信息并将它们保存在缓存中的网页门户入口,推荐使用path
url :exact string match
url_beg : prefix match
url_dir : subdir match
url_dom : domain match
url_end : suffix match
url_len : length match
url_reg : regex match
url_sub : substring match - req.hdr([<name>[,<occ>]]) : string
提取在一个HTTP请求报文的首部
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
示例:
acl bad_agent hdr_sub(User-Agent) -i curl wegt
block if bad_agent
- status : integer
返回在响应报文中的状态码
示例:
自定义错误页
[root@centos7 ~]# mkdir errorfile
[root@centos7 ~]# cd errorfile/
[root@centos7 errorfile]# vim 503.html
<h1>503,forbidden</h1>
客户端ip:172.20.120.193
(1)定义acl规则为无效的源ip地址
frontend web
bind 172.20.120.255:80
default_backend websrvs
rspidel server
rspadd server:\ testapache
acl invalid_src src 172.20.120.193 定义acl规则无效的源ip地址
block if invalid_src 调用acl规则,block意为阻止该acl规则
errorfile 403 /etc/haproxy/errorfiles/403.http
(2)定义acl规则为阻止以test为前缀的访问页面
frontend web
bind 172.20.120.255:80
default_backend websrvs
rspidel server
rspadd server:\ testapache
acl invalid_page path_beg /test 定义acl规则为以test为前缀的访问请求
block if invalid_page 调用acl规则,block意为阻止该acl规则
errorfile 403 /etc/haproxy/errorfiles/403.http
(3)定义浏览器类型
frontend web
bind 172.20.120.255:80
default_backend websrvs
rspidel server
rspadd server:\ testapache
acl bad_agent hdr_sub(User-Agent) -i curl wegt 定义acl规则为curl和wget浏览器类型,并且不区分大小写
block if bad_agent 调用acl规则,block意为阻止该acl规则
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
测试:
wget -O - -q http://www.a.com/index.html
curl http://www.a.com
预定义ACL
ACL名称 | 等价于 | 说明 |
---|---|---|
TRUE | always_true | 总是匹配 |
FALSE | always_false | 从不匹配 |
HTTP | req_proto_http | 匹配HTTP协议 |
HTTP_1.0 | req_ver 1.0 | 匹配HTTP协议1.0 |
HTTP_1.1 | req_ver 1.1 | 匹配HTTP协议1.1 |
HTTP_CONTENT | hdr_val(content-length) gt 0 | 匹配已存在内容长度 |
HTTP_URL_ABS | url_reg ^[^/:]*:// | 匹配URL绝对路径 |
HTTP_URL_SLASH | url_beg / | 匹配URL相对路径 |
HTTP_URL_STAR | url * | 匹配 URL 等于 "*" |
LOCALHOST | src 127.0.0.1/8 | 匹配从localhost来的连接 |
METH_CONNECT | method CONNECT | 匹配HTTP CONNECT方法 |
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 |
WAIT_END | wait_end | wait for end of content analysis |
示例:
frontend web
bind 172.20.120.255:80
default_backend websrvs
block if METH_HEAD 调用并阻止预定义aclMETH_HEAD
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
使用curl命令测试:
可以查看页面内容,但不能查看报文头部信息
[root@centos6 ~]# curl 172.20.120.255
RS1 Server
[root@centos6 ~]# curl -I 172.20.120.255
HTTP/1.0 403 Forbidden
Cache-Control: no-cache
Connection: close
Content-Type: text/html
配置
- use_backend <backend> [{if | unless} <condition>]
当if/unless一个基于ACL的条件匹配时切换指定backend - block { if | unless } <condition>
阻止7层请求if/unless一个条件匹配
示例:
acl invalid_src src 172.16.200.2
block if invalid_src
errorfile 403 /usr/share/haproxy/403.http
- http-request { allow | deny |add-header <name> <fmt> |set-header <name> <fmt> } [ { if | unless } <condition> ]
对7层请求的访问控制
示例:基于ACL的动静分离
frontend web \*:80
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
use_backend staticsrvs if url_static
default_backend appsrvs
backend staticsrvs
balance roundrobin
server staticsrv1 192.168.32.129:80 check
backend appsrvs
balance roundrobin
server app1 192.168.32.130:80 check
server app1 192.168.32.131:8080 check
配置
- tcp-request connection {accept|reject} [{if | unless} <condition>]
根据第4层条件对传入连接执行操作
示例:
listen ssh
bind :22222 定义监听端口,由于没有绑定具体ip地址,该端口会发布外网,为了防止外网访问,因此使用不常用的端口
balance leastconn 最少连接算法适用于长连接,ssh远程连接属于长连接
mode tcp defaults配置段默认为http协议,使用四层调度,必须指定为tcp协议
server sshsrv1 192.168.32.130:22 check 调度到后端服务器的22端口
server sshsrv2 192.168.32.131:22 check 调度到后端服务器的22端口
在客户端进行访问测试
ssh 172.20.120.255 -p 22222 -p选项指定端口
注意:haproxy的tcp四层调度属于伪四层调度,因为一般真正的tcp四层调度会直接转发报文,而不会更改报文中的ip地址信息代替客户端访问服务器,
但haproxy把客户端的请求报文解封装,然后把自己的ip地址封装进报文,代替客户端访问后端服务器,实现四层调度,
这就改变了请求报文中源ip地址,而后端服务器看到的源ip地址就是haproxy服务器的内网地址。
支持https协议
- 配置HAProxy支持https协议:
- 支持ssl会话;
bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE
crt 后证书文件为PEM格式,且同时包含证书和所有私钥
cat demo.crt demo.key > demo.pem - 把80端口的请求重向定443
bind *:80
redirect scheme https if !{ ssl_fc } - 向后端传递用户请求的协议和端口(frontend或backend)
http_request set-header X-Forwarded-Port %[dst_port]
http_request add-header X-Forwared-Proto https if { ssl_fc }
注意:haproxy服务器对外使用https进行加密传输确保安全,对内则使用http协议传输即可(由于内网能够确保安全性),因此haproxy服务器也可以称为https的卸载器
示例:HAProxy支持https协议
生成自签名证书:
cd /etc/pki/tls/certs
[root@centos7 certs]# make a.pem
umask 77 ; \
PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
/usr/bin/openssl req -utf8 -newkey rsa:2048 -keyout $PEM1 -nodes -x509 -days 365 -out $PEM2 ; \
cat $PEM1 > a.pem ; \
echo "" >> a.pem ; \
cat $PEM2 >> a.pem ; \
rm -f $PEM1 $PEM2
Generating a 2048 bit RSA private key
...............................................................................................................................................................................................................................+++
.................+++
writing new private key to '/tmp/openssl.vZ5uCN'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:www.a.com
Email Address []:
cp a.pem /etc/haproxy/
vim /etc/haproxy/haproxy.cfg
listen haproxy
bind 192.168.32.129:9527
acl valid_src src 192.168.32.1
stats enable
stats hide-version
stats refresh 30
stats uri /hastatus
stats realm HAproxy\ statistics
stats auth root:centos
stats admin if valid_src
frontend web
bind 172.20.120.255:80
bind 172.20.120.255:443 ssl crt /etc/haproxy/a.pem 使用ssl加密传输,监听443端口,调用证书crt并指定证书路径
redirect scheme https if ! { ssl_fc } 使用redirect指令把http重定向到https
default_backend websrvs
backend websrvs
server srv1 192.168.32.130:80 check
server srv2 192.168.32.131:80 check
测试:
curl -k https://www.a.com 测试访问https协议
在后端服务器查看日志,发现传输协议仍然是http协议
知识扩展:http重定向https
实现http重定向https
vim /etc/haproxy/haproxy.cfg
frontend web
bind 172.20.120.255:80
bind 172.20.120.255:443 ssl crt /etc/haproxy/a.pem 使用ssl加密传输,调用证书crt并指定证书路径
redirect scheme https if ! { ssl_fc } 使用redirect指令把http重定向到https
default_backend websrvs
测试:
[root@centos6 ~]# curl -I http://www.a.com 查看报文头部是否有返回重定向响应码
HTTP/1.1 302 Found 302状态码表示重定向
Cache-Control: no-cache
Content-length: 0
Location: https://www.a.com/
Connection: close
知识扩展:自定义添加报文头部信息X-Forwarded-Port
自定义添加报文头部信息X-Forwarded-Port
vim /etc/haproxy/haproxy.cfg
frontend web
bind 172.20.120.255:80
bind 172.20.120.255:443 ssl crt /etc/haproxy/a.pem
redirect scheme https if !{ ssl_fc }
http_request set-header X-Forwarded-Port %[dst_port]
default_backend websrvs
在后端服务器配置文件日志格式中添加自定义字段X-Forwarded-Port
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Ag
ent}i\" \"%{clientip}i\" %{testheader}i %{X-Via}i %{X-Forwarded-Port
}i" combined
测试:
在客户端测试
curl -kL http://www.a.com
curl -kL https://www.a.com
在后端服务器查看日志,是否记录80和443端口号
文章评论