反向代理软件之HAproxy高级功能及配置

基于cookie的会话保持

cookie value:为当前server指定cookie值,实现基于cookie的会话黏性,相对于基于 source 地址
hash 调度算法对客户端的粒度更精准,但同时也加重了haproxy负载,目前此模式使用较少, 已经被
session共享服务器代替
注意:不支持 tcp mode,使用 http mode

配置选项

cookie name [ rewrite | insert | prefix ][ indirect ] [ nocache ][ postonly ] [
preserve ][ httponly ] [ secure ][ domain ]* [ maxidle <idle> ][ maxlife ]
name: #cookie 的 key名称,用于实现持久连接
insert: #插入新的cookie,默认不插入cookie
indirect: #如果客户端已经有cookie,则不会再发送cookie信息
nocache: #当client和hapoxy之间有缓存服务器(如:CDN)时,不允许中间缓存器缓存cookie,
因为这会导致很多经过同一个CDN的请求都发送到同一台后端服务器

配置示例

listen web_port
    bind 172.31.0.7:80
    balance roundrobin
    mode http #不支持 tcp mode
    log global
    cookie WEBSRV insert nocache indirect
    server web1 172.31.0.17:80 check inter 3000 fall 2 rise 5 cookie web1
    server web2 172.31.0.27:80 check inter 3000 fall 2 rise 5 cookie web2

HAProxy状态页

通过web界面,显示当前HAProxy的运行状态
官方帮助:

http://cbonte.github.io/haproxy-dconv/2.4/configuration.html#4-stats%20admin
http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-stats%20admin

状态页配置项

stats enable   #基于默认的参数启用stats page
stats hide-version    #将状态页中haproxy版本隐藏
stats refresh <delay> #设定自动刷新时间间隔,默认不自动刷新,以秒为单位
stats uri <prefix>    #自定义stats page uri,默认值:/haproxy?stats
stats realm <realm>   #账户认证时的提示信息,示例:stats realm HAProxy\
Statistics
stats auth <user>:<passwd> #认证时的账号和密码,可定义多个用户,每行指定一个用户.默认:no
authentication
stats admin { if | unless } <cond>  #启用stats page中的管理功能

范例:配置状态页隐藏版本和动态刷新页面

[root@centos8 ~]# cat /etc/haproxy/conf.d/test.cfg
listen stats
    bind 0.0.0.0:9999
    stats enable
    log global
    stats uri /status
    stats auth haadmin:123456
    stats hide-version   #隐藏版本
    stats admin if TRUE  #配置
    stats refresh 10     #动态

listen web_port
    bind 0.0.0.0:80
    log global
    #server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
    server 172.31.0.17 172.31.0.17:80 check inter 3000 fall 2 rise 5
    server 172.31.0.27 172.31.0.27:80 check inter 3000 fall 2 rise 5

启用状态页

listen haproxy-status
    bind :9999
    stats enable
    #stats hide-version
    stats uri /haproxy-status #自定义stats page uri
    stats realm HAProxy\ Stats\ Page #账户认证时的提示信息
    stats auth haadmin:123456 #两个用户
    stats auth admin:123456
    #stats refresh 30
    stats admin if TRUE #安全原因,不建议打开

登录状态页信息

pid = 28134 (process #1, nbproc = 1, nbthread = 1) #pid为当前pid号,process为当前进
程号,nbproc和nbthread为一共多少进程和每个进程多少个线程
uptime = 0d 0h00m04s #启动了多长时间
system limits: memmax = unlimited; ulimit-n = 200029 #系统资源限制:内存/最大打开文件数/
maxsock = 200029; maxconn = 100000; maxpipes = 0 #最大socket连接数/单进程最大连接数/
最大管道数maxpipes
current conns = 2; current pipes = 0/0; conn rate = 2/sec; bit rate = 0.000 kbps
#当前连接数/当前管道数/当前连接速率
Running tasks: 1/14; idle = 100 % #运行的任务/当前空闲率
active UP: #在线服务器
backup UP: #标记为backup的服务器
active UP, going down: #监测未通过正在进入down过程
backup UP, going down: #备份服务器正在进入down过程
active DOWN, going up: #down的服务器正在进入up过程
backup DOWN, going up: #备份服务器正在进入up过程
active or backup DOWN: #在线的服务器或者是backup的服务器已经转换成了down状态
not checked: #标记为不监测的服务器
active or backup DOWN for maintenance (MAINT) #active或者backup服务器人为下线的
active or backup SOFT STOPPED for maintenance #active或者backup被人为软下线(人为将weight改成0)

backend server信息

session rate(每秒的连接会话信息): Errors(错误统计信息):
cur:每秒的当前会话数量 Req:错误请求量
max:每秒新的最大会话数量 conn:错误链接量
limit:每秒新的会话限制量 Resp:错误响应量
sessions(会话信息): Warnings(警告统计信息):
cur:当前会话量 Retr:重新尝试次数
max:最大会话量 Redis:再次发送次数
limit: 限制会话量
Total:总共会话量 Server(real server信息):
LBTot:选中一台服务器所用的总时间 Status:后端机的状态,包括UP和DOWN
Last:和服务器的持续连接时间 LastChk:持续检查后端服务器的时间
Wght:权重
Bytes(流量统计): Act:活动链接数量
In:网络的字节输入总量 Bck:备份的服务器数量
Out:网络的字节输出总量 Chk:心跳检测时间
Dwn:后端服务器连接后都是DOWN的数量
Denied(拒绝统计信息): Dwntme:总的downtime时间
Req:拒绝请求量 Thrtle:server 状态
Resp:拒绝回复量
范例:通过curl 命令对haproxy的状态页的访问实现健康检查
[root@centos8 ~]# curl -I -u haadmin:123456 http://172.31.0.7:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html

[root@centos8 ~]# echo $?
0

[root@haproxy ~]# systemctl stop haproxy

[root@centos8 ~]# curl -I http://haadmin:123456@172.31.0.7:9999/haproxy-status
curl: (7) Failed to connect to 172.31.0.7 port 9999: Connection refused

[root@centos8 ~]# echo $?
6

IP透传

web服务器中需要记录客户端的真实IP地址,用于做访问统计、安全防护、行为分析、区域排行等场景。

四层:IP+PORT转发

七层:协议+内容交换

四层负载

在四层负载设备中,把client发送的报文目标地址(原来是负载均衡设备的IP地址),根据均衡设备设置的
选择web服务器的规则选择对应的web服务器IP地址,这样client就可以直接跟此服务器建立TCP连接并
发送数据,而四层负载自身不参与建立连接,而和LVS不同,haproxy是伪四层负载均衡,因为haproxy
需要分别和前端客户端及后端服务器建立连接

七层代理

七层负载均衡服务器起了一个反向代理服务器的作用,服务器建立一次TCP连接要三次握手,而client要
访问Web Server要先与七层负载设备进行三次握手后建立TCP连接,把要访问的报文信息发送给七层负
载均衡;然后七层负载均衡再根据设置的均衡规则选择特定的 Web Server,然后通过三次握手与此台
Web Server建立TCP连接,然后Web Server把需要的数据发送给七层负载均衡设备,负载均衡设备再把
数据发送给client;所以,七层负载均衡设备起到了代理服务器的作用,七层代理需要和Client和后端服务器分别建立连接

四层IP透传

#haproxy 配置:
listen web_http_nodes
    bind 10.1.0.100:80 #因下面longxuan.vip的站点建立在阿里云有防火墙,此处必须用80端口,如无防火墙可以使用其它端口
    mode tcp #不支持http协议
    balance roundrobin
    server web1 www.longxuan.vip:80 send-proxy check inter 3000 fall 3 rise 5 #添加send-proxy
    
#nginx 配置:在访问日志中通过变量$proxy_protocol_addr 记录透传过来的客户端IP
http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" "$proxy_protocol_addr"'
    server {
        listen 80 proxy_protocol; #启用此项,将无法直接访问此网站,只能通过四层代理访问
    server_name www.longxuan.vip;
......

范例: nginx 开启四层日志功能

#在nginx服务器上开启日志格式和proxy_protocal
[root@VM_0_10_centos ~]# vim /apps/nginx/conf/nginx.conf
http {
    .......
    log_format main '$remote_addr - $remote_user [$time_local] "$request" "$proxy_protocol_addr"'
    sendfile on;
    keepalive_timeout 65;
    client_max_body_size 100m;
    server {
        listen 80 default_server proxy_protocol;
......

七层IP透传

当haproxy工作在七层的时候,也可以透传客户端真实IP至后端服务器

HAProxy配置
在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For"首部,其值为前端客户端的地址;用于向后端主发送真实的客户端IP

option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
[ except <network> ]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络
[ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For",示例:X-client
[ if-none ] 如果没有首部才添加首部,如果有使用默认值

范例:

#haproxy 配置
defaults
#此为默认值,首部字段默认为:X-Forwarded-For
option forwardfor
#或者自定义首部,如:X-client
option forwardfor except 127.0.0.0/8 header X-client

#listen配置
listen web_host
    bind 172.31.0.7:80
    mode http
    log global
    balance random
    server web1 172.31.0.17:80 weight 1 check inter 3000 fall 2 rise 5
    server web2 172.31.0.27:80 weight 1 check inter 3000 fall 2 rise 5

后端 web 服务器日志格式配置

配置web服务器,记录负载均衡透传的客户端IP地址

#apache 配置:需要修改才支持
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%
{User-Agent}i\"" combined
#nginx 日志格式:默认就支持
$proxy_add_x_forwarded_for:包括客户端IP和中间经过的所有代理的IP
$http_x_forwarded_For:只有客户端IP
log_format main '"$proxy_add_x_forwarded_for" - $remote_user [$time_local]
"$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" $http_x_forwarded_For';

[root@centos8 ~]# tail /var/log/nginx/access.log

#tomcat 配置:conf目录下的server.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%{X-Forwarded-For}i %h %l %u %t &quot;%r&quot; %s %b" />

报文修改

在http模式下,基于实际需求修改客户端的请求报文与响应报文,通过reqadd和reqdel在请求报文添加
删除字段,通过rspadd与rspidel在响应报文中添加与删除字段。
注意:此功能的以下相关指令在2.1版本中已经取消
官方文档:参看2.0的帮助文档

http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-rspadd

范例

#在向后端服务器转发的请求报文尾部添加指定首部
reqadd <string> [{if | unless} <cond>]
示例:reqadd X-Via:\ HAproxy #注意只能有一个空格,并需要转义
#在向后端服务器转发的请求报文中删除匹配正则表达式的首部
reqdel <search> [{if | unless} <cond>]
reqidel <search> [{if | unless} <cond>] #忽略大小写
示例:
reqidel user-agent
reqidel X-Forwarded-For #无法删除
#在向前端客户端转发的响应报文尾部添加指定首部
rspadd <string> [{if | unless} <cond>]
示例:
rspadd X-Via:\ HAproxy
rspadd Server:\ wanginx
#从向前端客户端转发的响应报文中删除匹配正则表达式的首部
rspdel <search> [{if | unless} <cond>]
rspidel <search> [{if | unless} <cond>] #忽略大小写
示例:
rspidel ^server:.* #从响应报文删除server信息
rspidel X-Powered-By:.* #从响应报文删除X-Powered-By信息,一般此首部字段保存php版
本信息 #

2.1版本以上用下面指令http-request和http-response代替
官方文档:

http://cbonte.github.io/haproxy-dconv/2.4/configuration.html#4-http-request
http://cbonte.github.io/haproxy-dconv/2.4/configuration.html#4-http-response

配置说明:

http-request add-header <name> <fmt> [ { if | unless } <condition> ]
#示例:http-request add-header X-Haproxy-Current-Date %T
http-request del-header <name> [ { if | unless } <condition> ]
http-response add-header <name> <fmt> [ { if | unless } <condition> ]
http-response del-header <name>
#示例:http-response del-header Server

范例: 添加向后端服务器发送的请求报文首部

[root@haproxy ~]# vim /etc/haproxy/conf.d/test.cfg
frontend m44_web_80
    bind 172.31.0.100:80
    use_backend m44_webservers1
    http-request add-header X-Haproxy-Current-Date %T
    
[root@haproxy ~]# systemctl restart haproxy.service
[root@rs1 ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"% {X-Forwarded-For}i\" \"%{X-Haproxy-Current-Date}i\" " combined

[root@rs1 ~]# tail /var/log/httpd/access_log

范例: 添加向客户端发送的响应报文首部

[root@haproxy ~]# vim /etc/haproxy/conf.d/test.cfg
frontend m44_web_80
    bind 172.31.0.100:80
    use_backend m44_webservers1
    http-request add-header X-Haproxy-Current-Date %T
    http-response add-header X-via haproxy-M520

[root@haproxy ~]# systemctl restart haproxy.service

范例: 修改向客户端发送的响应报文首部,实现自定义Server首部

[root@haproxy ~]# vim /etc/haproxy/conf.d/test.cfg
frontend m44_web_80
    bind 172.31.0.100:80
    use_backend m44_webservers1
    http-request add-header X-Haproxy-Current-Date %T
    http-response add-header X-via haproxy-M520
    http-response del-header server
    http-response add-header server M520webserver #注意和上面指令的前后顺序

范例:

#添加向后端报务器发起的请求报文首部
[root@haproxy ~]# vim haproxy.cfg
frontend main *:80
    # bind *:80
    default_backend websrvs
    reqadd testheader:\ haproxyserver #加此行,注意只能有一个空格,并需要转义
    
#在后端httpd服务器
[root@haproxy ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%{testheader}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

#查看日志
[root@haproxy ~]# tail –f /var/log/httpd/acesss_log

范例:

#添加响应报文首部
[root@haproxy ~]# vim haproxy.cfg
frontend main *:80
    # bind *:80
    default_backend websrvs
    rspadd X-Via:\ HAproxy-1 #加此行
    maxconn 5000
#客户端访问调试模式,查看reponse headers,看到
Server: Apache/2.4.37 (centos) #系统自带显示
X-Via: HAproxy-1

范例:

#删除响应报文中的server首部
[root@haproxy ~]# vim haproxy.cfg
frontend main *:80
    # bind *:80
    default_backend websrvs
    rspadd X-Via:\ HAproxy-1
    rspdel Server 或者 rspidel server #加此行 ,忽略大小写
    rspidel X-Powered-By:.* #删除Php版本
    maxconn 5000
#客户端访问调试模式,查看reponse headers,看到
Server: Apache/2.4.37 (centos) #此行消失
X-Via: HAproxy-1

范例:

#增加响应报文的首部,实现伪装Server首部
[root@haproxy ~]# vim haproxy.cfg
frontend main *:80
    # bind *:80
    default_backend websrvs
    rspadd X-Via:\ HAproxy-1
    rspdel Server #或者用 rspidel server
    rspadd Server:\ longg #增加此行
    
[root@internet ~]# curl -i 10.1.0.100
HTTP/1.1 200 OK
date: Thu, 09 Apr 2021 08:32:10 GMT
last-modified: Thu, 09 Apr 2021 01:23:18 GMT
etag: "f-5a2d17630635b"
accept-ranges: bytes
content-length: 15
content-type: text/html; charset=UTF-8
server: longg
RS1 172.31.0.17

范例:

[root@centos7 ~]# vim /etc/haproxy/haproxy.cfg
listen web_port
    bind 10.0.0.7:80
    http-request add-header X-Haproxy-Current-Date %T #添加首部
    http-response del-header server #删除首部
    mode http
    log global
    option httpchk
    http-check expect status 200
    server web1 172.31.0.17:80 check inter 3000 fall 2 rise 5
    server web2 172.31.0.27:80 check inter 3000 fall 2 rise 5
    
#查看后端服务器日志
[root@centos7 ~]# tail –f /var/log/httpd/acesss_log

自定义日志格式

log global 开启日志功能,默认只会在记录下面格式的日志
option httplog 可以采用 http 格式记录下来,并且可以使用相关指令将特定信息记录在haproxy的日志中但一般不建议开启,这会加重 HAProxy 负载

配置选项

log global #开启记录日志,默认不开启
option httplog #开启记录httplog日志格式选项
capture cookie <name> len <length> #捕获请求和响应报文中的 cookie及值的长度,
将之记录到日志
capture request header <name> len <length> #捕获请求报文中指定的首部内容和长度并记录日
志
capture response header <name> len <length> #捕获响应报文中指定的内容和长度首部并记录日
志
#示例:
log global
option httplog
capture request header Host len 256
capture request header User-Agent len 512
capture request header Referer len 15
capture request header X-Forwarded-For len 15

同时开启日志功能log global和option httplog,记录日志格式如下

[root@haproxy ~]# tail /var/log/haproxy.log
Apr 9 19:42:02 localhost haproxy[60236]: 10.1.0.200:54630
[09/Apr/2021:19:42:02.623] web_prot_http_nodes web_prot_http_nodes/web1
0/0/1/1/2 200 4264 - - ---- 1/1/0/0/0 0/0 "GET / HTTP/1.1"

配置示例

listen web_host
    bind 172.31.0.7:80
    mode http
    balance roundrobin
    log global #开启日志功能
    option httplog #开启httplog日志格式选项
    capture request header User-Agent len 512 #记录日志信息,记录User-Agent首部值的前512个字符
    capture request header Host len 256 #记录日志信息,记录Host首部值
    cookie SERVER-COOKIE insert indirect nocache
    server web1 172.31.0.17:80 cookie web1 check inter 3000 fall 3 rise 5
    server web2 72.31.0.27:80 cookie web2 check inter 3000 fall 3 rise 5

压缩功能

对响应给客户端的报文进行压缩,以节省网络带宽,但是会占用部分CPU性能
建议在后端服务器开启压缩功能,而非在HAProxy上开启压缩

配置选项

compression algo <algorithm> ... #启用http协议中的压缩机制,常用算法有
gzip,deflate
#压缩算法<algorithm>支持下面类型:
identity #debug调试使用的压缩方式
gzip #常用的压缩方式,与各浏览器兼容较好
deflate #有些浏览器不支持
raw-deflate #新式的压缩方式
compression type <mime type> ... #要压缩的文件类型
#示例:
compression algo gzip deflate
compression type text/html text/css text/plain

配置示例

listen web_host
    bind 172.31.0.7:80
    mode http
    balance roundrobin
    log global
    option httplog
    compression algo gzip deflate #启用压缩和指定算法
    compression type compression type text/plain text/html text/css text/xml
    text/javascript application/javascript #指定压缩文件类型
    server web1 172.31.0.17:80 cookie web1 check inter 3000 fall 3 rise 5
    server web2 172.31.0.27:80 cookie web2 check inter 3000 fall 3 rise 5
    
#后端服务器准备一个文本文件
[root@centos7 ~]# ll /var/www/html/m520.txt -h
-rwxr-xr-x 1 root root 772K Apr 2 12:56 /var/www/html/m520.txt

验证压缩功能

[root@centos6 ~]# curl -is --compressed 172.31.0.7/m520.txt | less

web服务器状态监测

三种状态监测方式

基于四层的传输端口做状态监测,此为默认方式
基于指定 URI 做状态监测,需要访问整个页面资源,占用更多带宽
基于指定 URI 的request请求头部内容做状态监测,占用较少带宽,建议使用此方式

基于应用层http协议进行健康性检测

基于应用层http协议,采有不同的监测方式,对后端real server进行状态监测
注意: 此方式会导致在后端服务器生成很多的HAProxy发起的访问日志

option httpchk #启用七层健康性检测,对tcp 和 http 模式都支持,默认为:OPTIONS /
HTTP/1.0
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>
#期望以上检查得到的响应码
http-check expect [!] <match> <pattern>
#示例:
http-check expect status 200
http-check expect ! rstatus ^5 #支持正则表达式
#关于HTTP/1.1的说明
<version> is the optional HTTP version string. It defaults to "HTTP/1.0" but
some servers might behave incorrectly in HTTP 1.0, so turning it to HTTP/1.1 may
sometimes help. Note that the Host field is mandatory in HTTP/1.1, and
as a trick, it is possible to pass it after "\r\n" following the version string.

配置示例

listen web_host
    bind 172.31.0.7:80
    mode http
    balance roundrobin
    #option httpchk GET /monitor/check.html #默认HTTP/1.0
    #option httpchk GET /monitor/check.html HTTP/1.0
    #option httpchk GET /monitor/check.html HTTP/1.1 #注意:HTTP/1.1强制要求必须有Host字段
    option httpchk HEAD /monitor/check.html HTTP/1.1\r\nHost:\ 172.31.0.7 #使用HEAD减少网络流量
    cookie SERVER-COOKIE insert indirect nocache
    server web1 172.31.0.17:80 cookie web1 check inter 3000 fall 3 rise 5
    server web2 172.31.0.27:80 cookie web2 check inter 3000 fall 3 rise 5
    
#在所有后端服务建立检测页面
[root@backend ~]# mkdir /var/www/html/monitor/
[root@backend ~]# echo monitor > /var/www/html/monitor/check.html
#关闭一台Backend服务器
[root@backend1 ~]# systemctl stop httpd

验证http监测
查看到状态页,可以看到启用了七层检测功能:LastChk字段:L7

#后端服务器查看访问日志
[root@backend ~]# tail /var/log/httpd/access_log
127.31.0.7 - - [02/Apr/2021:14:25:22 +0800] "HEAD /monitor/check.html HTTP/1.1"
200 - "-" "-"
posted @ 2021-07-31 20:39  空白的旋律  阅读(1413)  评论(0编辑  收藏  举报