4. HAproxy调度算法
HAproxy通过固定参数balance指明对后端服务器的调度算法, 该参数可以配置在listen或backend选项中. 一般配置在listen中.
HAproxy的调度算法分为静态和动态调度算法, 但是有些算法可以根据参数在静态和动态算法中相互转换.
默认算法是动态的roundrobin, 基于权重轮询算法.
4.1 静态算法
静态算法: 按照事先定义好的规则轮询, 公平调度, 不关心后端服务器的当前负载, 连接数, 响应速度等, 且不能实时修改权重, 只能靠重启HAproxy生效.
如何动态调整后端服务器权重:
利用网络工具socat, 其特点是在两个数据流之间建立通道, 且支持众多协议和链接方式. 如, IP, TCP, UDP, IPv6, Socket文件等. 在HAproxy中使用就是查看和设置HAproxy服务器的信息.
举例: 查看HAproxy服务器的信息
[root@Haproxy-1:~]# echo "show info" | socat stdio /var/lib/haproxy/admin.sock
Name: HAProxy
Version: 2.2.4-de45672
Release_date: 2020/09/30
...
举例: 查看某个群组下, 某台后端服务器的权重
root@HAproxy-1:~# echo "get weight web1/10.0.0.39" | socat stdio /var/lib/haproxy/admin.sock
1 (initial 1)
# 服务器组名/服务器名称
listen web1 # web1就是服务器组名
bind 10.0.0.19:80
mode http
server 10.0.0.39 10.0.0.39:8080 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 check inter 3000 fall 3 rise 5 # server后面跟的10.0.0.49就是这个后端服务器的名称
举例: 动态设置服务器的权重
root@HAproxy-1:~# echo "set weight web1/10.0.0.39" 2 | socat stdio /var/lib/haproxy/admin.sock
root@HAproxy-1:~# echo "get weight web1/10.0.0.39" | socat stdio /var/lib/haproxy/admin.sock
2 (initial 1)
注意: 如果haproxy开启了多进程, 那么使用上面的set weight命令只能修改单个进程中, 该服务器的权重, 其他的进程还是认为10.0.0.39的权重是没有改变的, 因此haproxy在将请求进行调度时, 该服务器在不同的进程中调度时权重是不一样的. 之后会介绍如何解决该问题
另外, 通过socket文件修改的配置是不会永久保存的, 重启服务的话就恢复原始状态了.
工作中一般会给每个进程都开启一个socket文件, 因此,给服务器下线时, 需要在每个进程里都给服务器下线, 不能只在一个进程里下线
4.1.1: static-rr
static-rr: 基于权重的轮询调度, 不支持权重的运行时调整及后端服务器慢启动, 其每个后端服务器群组中的主机数量没有限制, 而roundrr的限制是4095个.
服务器慢启动: haproxy的独有特性, 当服务器被新或者重新加入haproxy时, 由于其稳定性还不可而知, 那么haproxy不会直接把该服务器和其他服务器一起调度, 而是先将一部分请求转发给服务器进行处理, 直到运行稳定后, 再和其他服务器一起统一调度
运行时调整: 就是通过socket文件进行后端服务器权重的调整
配置:
listen web1
bind 10.0.0.19:80
balance static-rr
mode http
server 10.0.0.39 10.0.0.39:8080 weight 2 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 check inter 3000 fall 3 rise 5
重启ha后观察轮询效果
#可以看到由于238设置了权重为2, 因此调度比例是2:1
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-2 10.0.0.49
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-2 10.0.0.49
此时如果想动态调整权重是不允许的
root@HAproxy-1:~# echo "set weight web1/10.0.0.39 3" | socat stdio /var/lib/haproxy/admin.sock
Backend is using a static LB algorithm and only accepts weights '0%' and '100%'.
4.1.2 first
根据服务器在列表中的位置, 自上而下进行调度, 但是其只会在列表中的第一台服务器的连接数达到上限(maxconn), 新的请求才会分配给下一台服务器, 因此忽略服务器权重的设置. 这种调度算法基本不会用
配置实例:
listen web1
bind 10.0.0.19:80
balance static-rr
mode http
server 10.0.0.39 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 weight 5 check inter 3000 fall 3 rise 5
效果演示:
#可以看到即使10.0.0.49设置了权重为5, 也不会被调度
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
root@web-2:~# curl 10.0.0.19
web-1 10.0.0.39
4.2 动态算法
动态算法: 基于后端服务器状态进行调度, 适当调整, 比如优先调度至当前负载较低的服务器, 且权重可以在ha运行时动态调整无需重启
4.2.1 roundrobin
基于权重的轮询动态调度算法, 支持权重的运行时调整, 不需要重启ha服务器, 与lvs的rr轮训不完全一样, ha中的roundrobin是支持慢启动的(新加的服务器主机逐渐增加转发数), 其每个后端backend中最多支持4095个real server, roundrobin为默认调度算法, 且支持对real server权重动态调整
配置案例:
listen web1
bind 10.0.0.19:80
balance roundrobin
mode http
server 10.0.0.39 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 weight 5 check inter 3000 fall 3 rise 5
访问测试:
root@web-2:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-1 10.0.0.39
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-1 10.0.0.39
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-1 10.0.0.39
注意: haproxy也是支持reload的, reload并不会中断当前用户的访问, 而是会生成新的进程, 新的进程有新的配置信息, 旧的进程等到用户访问请求结束会被回收, 像nginx一样, 工作中不要使用restart, 这样会中断当前用户的访问
systemctl reload haproxy
roundrobin调度效果看起来和static-rr的效果一样, 但是支持动态权重调整和慢启动
如果此时修改权重, 将10.0.0.49的权重降低为1, 那么会立即生效, 1:1调度
修改权重后, 访问测试
root@HAproxy-1:~# echo "set weight web1/10.0.0.49 1" | socat stdio /var/lib/haproxy/admin.sock
root@web-2:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-1 10.0.0.39
web-2 10.0.0.49
web-1 10.0.0.39
web-2 10.0.0.49
web-1 10.0.0.39
web-2 10.0.0.49
因此, static-rr基本不会使用., roundrobin和它效果一样, 而且还额外支持服务器慢启动和服务器权重动态调整
4.2.2 leastconn
leastconn加权的最少连接的动态, 支持权重的运行时调整和慢启动, 即当前后端服务器正在连接数最少的优先调度(对于新的客户端连接). 比较适合长连接的场景使用, 比如MySQL等场景.
如果是实验环境, 只用一个客户端去访问时, 看到的效果是和轮询一样的, 因为请求被调度到A服务器后, 这时B服务器的连接就是最少的, 下一次用户请求就会被调度到B, 这时A就没有连接了, 那么用户下一次再访问就又会被调度回A
4.3 其他算法
其他部分算法即可作为静态算法, 又可以通过选项修改成为动态算法
4.3.1 source
源地址hash: 基于用户源地址hash并将请求转发到后端服务器, 默认为静态, 即取模方式, 但是可以通过hash-type支持的选项更改, 后续同一个源地址请求将被转发至同一个后端web服务器, 比较适用于session保持/缓存业务场景, 或者应用程序没有实现Session共享
源地址有两种转发客户端请求到后端服务器的服务器选取计算方式, 分别是取模法和一致性hash. 但都是基于客户端源ip地址进行调度.
基于source源地址hash的配置
listen web1
bind 10.0.0.19:80
mode http
balance source
server web1 10.0.0.238:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.228:8080 weight 2 check inter 3000 fall 3 rise 5
访问测试
#可以看到从同一个客户端ip地址发出的请求都被调度到了238上
root@web-2:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
注意: 基于源地址hash的算法在工作中要慎用, 因为有可能造成转发不是很平均的情况. 比如有8个后端tomcat服务器, 有的可能有800的连接, 有的就只有1-200的连接, 非常不平均.
原因:
客户端访问都是基于SNAT的, 一旦某个公司人数多时, 当第一个人发起请求后, ha经过运算,比如调度到了服务器A, 那么之后该公司所有人只要出网ip是和第一个人是同一个的, 那么就都会被调度到同一个服务器上. 如果另一个公司得人比较少, 那么就会出现不同的服务器负载不同的情况
4.3.1.1 map-base取模法
取模法是默认的源地址hash算法, 而且是静态算法.
取模法: 基于后端服务器的总权重的hash数组取模, 取模结果就是0-(总权重-1), 之后按照这个取模的结果, 把请求调度到和这个取模值一样的权重的服务器上, 这样结果肯定会落到某一个服务器上.
默认情况下无法动态修改后端服务器权重. 如果想修改, 需要加参数, 修改为一致性hash就可以动态修改服务器的权重了.
如果后端服务器上线或下载, 那么服务器的总权重就会发生变化, 那么ha就会重新取模, 计算调度方法.
4.3.1.2 一致性hash
一致性hash的源地址hash算法是动态的, 可以动态在线调整权重, 支持慢启动. 优点在于当服务器的总权重发生变化时, 对调度结果影响是局部的, 不会引起大的变动.
一致性hash会把所有服务器落在一个环上, 总长度是2^32-1, 当经过一致性hash计算, 如果客户端ip经过hash运算结果落在了两个服务器之间, 那么它会顺延, 把请求调度到它就近的下一个服务器上. 因此当某个服务器上线或下线时, 总权重发生的变化对调度结果只是产生局部的影响.
比如: 经过计算, 某个ip落在了1000-2000之间, 但是2000又刚好被下线了, 那么一致性hash就会把该请求转发到下一个就近的服务器上, 可能是3000. 这样即使总的权重发生了变化, 也不需要重新计算调度方法, 只需要将请求调度到下一个在环上就近的服务器即可. 因此一致性hash比map-base取模功能好.
配置案例
listen web1
bind 10.0.0.19:80
balance source
mode http
hash-type consistent #源地址hash默认是取模法, 加了hash-type consistent参数后就变成了一致性hash
server 10.0.0.39 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 weight 5 check inter 3000 fall 3 rise 5
重新加载后即可生效, 并且可以动态修改权重了
root@web-1:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
root@HAproxy-1:~# echo "set weight web1/10.0.0.49 3" | socat stdio /var/lib/haproxy/admin.sock
root@HAproxy-1:~# echo "get weight web1/10.0.0.49 " | socat stdio /var/lib/haproxy/admin.sock
3 (initial 5)
4.3.2 uri
- url和uri区别
能统一定为到服务器上某个资源的就是uri
uri是包含url的
url必须包含协议, 比如http, ftp等
而uri是可以包含也协议, 也可只写一个服务器的路径
http://example.org/absolute/URI/with/absolute/path/to/resource.txt #URI/URL
ftp://example.org/resource.txt #URI/URL
/relative/URI/with/absolute/path/to/resource.txt #URI
基于uri的算法会对用户请求的uri进行hash运算, 根据用户访问的资源来调度, 无论是哪个用户从哪个ip访问, 只要是访问同一个资源, 就会被调度到同一个服务器, 生产环境一般很少使用. 一般CDN厂商会用uri, 因为它们要缓存很多静态资源, 图片, 文本等, 那么用户访问同一个资源时就会被调度到同一个服务器
基于uri的算法也包括取模法和一致性hash算法
4.3.2.1 uri取模法配置示例
listen web1
bind 10.0.0.19:80
balance uri
mode http
#hash-type consistent
server 10.0.0.39 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 weight 5 check inter 3000 fall 3 rise 5
访问测试;
- 从10.0.0.84访问主页面
root@web-2:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
- 从10.0.0.83访问主页面
root@web-1:~# while true; do curl 10.0.0.19; sleep 1 ; done
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
web-2 10.0.0.49
4.3.2.2 uri一致性hash配置示例
listen web1
bind 10.0.0.19:80
balance uri
mode http
hash-type consistent
server 10.0.0.39 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server 10.0.0.49 10.0.0.49:8080 weight 5 check inter 3000 fall 3 rise 5
4.3.3 url_param
url_param对用户请求的url中的params部分中的参数name或其他自定义的参数比如keyword对应的value值(来做hash计算, 并由后端服务器总权重相除以后派发至某挑出来的服务器; 始终发往同一个后端服务器. 一般电商公司用的比较多, 电商公司会把产品信息缓存到不同服务器上, 然后针对用户搜索的参数进行调度, 来加快访问速度
假设访问url = http://www.jd.com/foo/bar/index.phpp?k1=v1&k2=v2
则: host = "www.jd.com'
url_param = "k1=v1&k2=v2"
4.3.3.1 url_param取模法配置示例
listen web1
bind 10.0.0.19:80
mode http
balance url_param keyword #基于url中的keyword参数对应的value值的hash结果进行调度, 也可以基于多个参数, 格式为 name,age,...
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 2 check inter 3000 fall 3 rise 5
访问测试:
#由于实验环境没有后端实际页面, 因此利用curl命令测试, 在主页面后面加?然后跟keyword=数字或者字母即可. 如果是基于多个参数, 那么每个参数之间用&&或者&隔开
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=1 # 每次访问的url, keyword对应的value都是1, 那么就往同一个服务器调度
web-2 10.0.0.49
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=1
web-2 10.0.0.49
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=1
web-2 10.0.0.49
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=1
web-2 10.0.0.49
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=111111111111
web-1 10.0.0.39
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=111111111111
web-1 10.0.0.39
root@HAproxy-1:~# curl http://10.0.0.19/index.html?keyword=111111111111
web-1 10.0.0.39
如下图: keyword, enc这些都是url_param, url_param算法就是对这些指定的url_param对应的value值做hash运算
4.3.3.2 url_param一致性hash配置示例
listen web1
bind 10.0.0.19:80
mode http
balance url_param keyword #基于url中的keyword参数对应的value值的hash结果进行调度, 也可以基于多个参数, 格式为 name,age,...
hash-type consistent
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 2 check inter 3000 fall 3 rise 5
4.3.4 hdr
针对http请求报文头部中的指定字段对应的值做hash, 此处由name指定的http首部(name, User-Agent等)对应的值将会被取出来并做hash计算, 然后由服务器总权重相除以后派发至某挑出来的服务器, 假如无有效的值, 则会使用默认的roundrobin轮询调度.
一般针对浏览器, 尤其是ie浏览器, 由于每个浏览器的兼容性不同, 可以根据请求报文header中的User-Agent字段进行调度, 现在由于ie浏览器很少使用了, 一般是用于区别调度电脑端浏览器和手机端浏览器. 不过现在的开发代码里可以基于请求的域名进行匹配客户端还是移动端,然后基于移动端或者客户端域名进行转发 所以其实用的也不多
4.3.4.1 hrd取模法配置示例
配置示例:
listen web1
bind 10.0.0.19:80
mode http
balance hdr(User-Agent)
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 2 check inter 3000 fall 3 rise 5
测试:
- 从FireFox浏览器访问
- 从Edge浏览器访问
4.3.4.2 一致性hash配置示例
listen web1
bind 10.0.0.19:80
mode http
balance hdr(User-Agent)
hash-type consistent
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 2 check inter 3000 fall 3 rise 5
4.3.5 rdp-cookie
rdp-cookie使⽤客户端cookie保持会话,实现对windows远程桌⾯的负载等
rdp-cookie只支持tcp负载
4.3.5.1 rdp-cookie取模法配置实例
listen RDP
bind 10.0.0.19:3389
balance rdp-cookie
mode tcp
server rdp0 10.0.0.39:3389 check fall 3 rise 5 inter 2000 weight 1
4.3.5.2 rdp-cookie一致性hash配置实例
listen RDP
bind 10.0.0.19:3389
balance rdp-cookie
hash-type consistent
mode tcp
server rdp0 10.0.0.39:3389 check fall 3 rise 5 inter 2000 weight 1
4.3.5.3 基于iptables实现rdp的调度
开启ip_forward转发
net.ipv4.ip_forward = 1
iptables -t nat -A PREROUTING -d 192.168.7.101 -p tcp --dport 3389 -j DNAT -
-to-destination 172.18.139.20:3389
iptables -t nat -A POSTROUTING -s 172.18.139.20/21 -j SNAT --to-source
192.168.7.101
4.3.6 random
在1.9版本开始增加⼀个叫做random的负载均衡算法,其基于⼀个随机数作为⼀致性hash的key,随机负载均衡对于⼤型服务器或经常添加或删除服务器⾮常有⽤
4.3.6.1 random配置实例
listen web_host
bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010
mode http
log global
balance random
server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5
server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5
4.3.7 算法总结
static-rr--------->tcp/http 静态
first------------->tcp/http 静态
roundrobin-------->tcp/http 动态
leastconn--------->tcp/http 动态
random------------>tcp/http 动态
source------------>tcp/http
uri--------------->http
url_param--------->http 取决于hash_type是否consistent
hdr--------------->http
rdp-cookie-------->tcp
4.3.8 各算法使用场景
first #使⽤较少
static-rr #做了session共享的web集群
roundrobin
random
leastconn #数据库
source #基于客户端公⽹IP的会话保持
uri--------------->http #缓存服务器,CDN服务商,蓝汛、百度、阿⾥云、腾讯
url_param--------->http
hdr #基于客户端请求报⽂头部做下⼀步处理
rdp-cookie #很少使⽤
4.3.9 Layer 4 与 Layer 7
四层: IP+端口转发
七层: 协议+内容交换
一般情况, HA做四层, 七层用Nginx来做
HA的4层和7层区别
HA四层转发时, 负载均衡服务器会把客户端发的请求报文的目的地址, 从负载均衡器的ip地址, 修改为后端服务器的ip地址, 目的端口号根据需要也可以修改. 源地址根据不同的调度器也可能会被修改为调度器的ip地址(Nginx和HA无论是四层还是七层都会修改请求报文源地址, LVS则不会). 后端服务器处理完请求把响应报文发给负载均衡器, 负载均衡器再把响应报文的目的地址和端口号改为客户端的ip地址和端口号, 源ip和端口号也是根据不同的负载均衡器去做相应处理. 这时http请求的发起者还是客户端, 负载均衡器没有替代客户端向后端服务器发起另一个http请求. 客户端和后端服务器之间只需要一次TCP三次握手建立连接, 因此, 请求报文达到负载均衡器后, 负载均衡器会把报文转发给后端服务器, 客户端可后端服务器直接握手
HA七层负载均衡模式下, 除了会修改报文的源目ip和源目端口, 还会替代用户, 把负载均衡器自己作为请求的发起者, 向后端服务器发起请求. 这时客户端会认为请求的发起者就是负载均衡器. 后端服务器把响应报文构建完毕发给负载均衡器, 但是因为负载均衡器本身就是请求者, 因此它会把报文重新构建, 把源ip源端口替换成自己的ip和端口, 目的ip和端口替换成客户端的ip和端口, 再给客户端发回去. 此时客户端先和负载均衡器建立三次握手, 然后负载均衡器在和后端服务器建立三次握手
HA的4层和7层都会修改报文的源目ip和源目端口号, 但是四层模式下, HA不会代替用户向后端服务器发起请求, 而只是把请求报文转发给后端服务器, 客户端是直接和后端服务器建立tcp连接; 但是七层模式下 HA会代替客户端和后端服务器发起单独的连接, 从客户的到负载均衡器和从负载均衡器到后端服务器是两个不同的tcp连接. 四层模式下是转发, 而七层模式下是代理服务器的作用
LVS的4层和HA与Nginx的4层的区别
LVS-NAT: 修改请求报文的目标ip地址, 根据目标的ip地址进行转发, 并且客户端的ip地址会传给后端服务器
LVS- DR: 修改请求报文的mac地址, 根据mac地址进行转发, 并且客户端的ip地址会传给后端的服务器. 因此, DR模式下, 后端服务器上会有很多公网地址的连接.
HA和Nginx的四层, 会修改请求报文的源ip和目标ip, 后端服务器拿不到客户端的ip地址
另外四层和七层不管是lvs,ha还是nginx, 四层都是起到转发, 客户端直接和后端服务器握手, 而七层是客户端和负载均衡器建立连接, 然后负载均衡器和后端服务器建立连接,是两个不同的tcp连接
4.4 IP透传
web服务器中需要记录客户端的真实ip地址, 用于做访问统计, 安全防护, 行为分析, 区域排行等场景.
但是取得ip地址通常都是用户所连接的公网出口ip地址.
不管四层还是7层负载均衡模式下, 请求报文的源ip都是会被HA负载均衡器修改的, 因此, 如果后端服务器想要获得客户端的公网ip地址, 那么需要设置ip透传
客户端通过域名访问网站时, 请求的域名会被解析成防火墙的公网ip地址, 防火墙的公网ip地址就是公司网络的入口, 一般是在IDC机房, 网站的域名一般都是解析到自己的公网ip地址, 通过DNAT映射到后端的负载均衡上的vip地址(通过keepalive生成), 此时客户端发的请求报文已经被防火墙替换过一次了, 把请求报文的目的地址, 从自己的公网ip地址, 替换成了公司内部负载均衡器的vip地址, 之后负载均衡器会再次修改请求报文的信息, 具体根据使用的负载均衡器以及工作模式不同而不同, 根据不同的调度将请求发给后端服务器.
4.4.1 四层ip透传
后端是nginx服务器
- 在haproxy设置
在后端服务器设置上添加proxy-protocol参数
无论是四层还是七层, 都需要在HA的的调度中配置send-proxy
listen web1
bind 10.0.0.19:80
mode tcp # 基于四层
balance hdr(User-Agent)
server web1 10.0.0.39:8080 send-proxy weight 1 check inter 3000 fall 3 rise 5 # send-proxy参数就是四层和7层开启ip地址透传
server web2 10.0.0.49:8080 send-proxy weight 1 check inter 3000 fall 3 rise 5
- 在后端nginx设置, 分别在10.0.0.39和10.0.0.49上开启两个nginx服务,然后修改配置文件和日志
# Ubuntu修改/etc/nginx/sites-enabled/default
server {
listen 8080 proxy_protocol;
# CentOS修改/etc/nginx/nginx.conf
#日志里只需添加"$proxy_protocol_addr"即可, 这是四层tcp取客户端源地址. 而$http_x_forwarded_for是取得七层http地址透传
#在http语句块配置, 访问日志格式
log_format main '"$proxy_protocol_addr" - $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
#修改日志调用格式
access_log /var/log/nginx/access.log main;
- 重启服务前查看访问日志, 可以看到nginx访问日志的原地址都是haproxy的地址
10.0.0.19 - - [09/Nov/2020:19:02:53 +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"
10.0.0.19 - - [09/Nov/2020:19:02:54 +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"
- 重启服务后查看日志
#10.0.0.1是客户端的地址, 10.0.0.19是HA的地址
"10.0.0.1" - 10.0.0.19 - - [25/Mar/2021:19:43:31 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57" # "10.0.0.1"是浏览器的ip地址, 而10.0.0.19是HA的地址
4.4.2 七层ip透传
后端是nginx服务器
- 在haproxy设置
#可以在defaults中配置
defaults
option forwardfor
#也可以在listen的后端服务器中配置
listen web1
bind 10.0.0.19:80
mode http
balance hdr(User-Agent)
#option forwardfor只能配置在listen的http模式下, listen中如果配置成了tcp, 那么option forwardfor将被忽略掉.
# 注意: 在listen中, 如果mode是http, 那么其内部配置的所有tcp的选项都会被忽略, 反之亦然
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 1 check inter 3000 fall 3 rise 5
- 在nginx中配置
#nginx日志格式中添加以下, 这也是日志的默认格式
"$http_x_forwarded_for"
# 7层透传无需加proxy_protocol, 必须要把配置文件中的proxy_protocol删除, 否则请求无法到达nginx
Listen 8080
#apache 配置:
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%
{User-Agent}i\"" combined
#tomcat 配置:
pattern='%{X-Forwarded-For}i %l %T %t "%r" %s %b "%{User�Agent}i"'/>
"10.0.0.227" - 10.0.0.19 - - [24/May/2021:00:08:49 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "10.0.0.227"
# 10.0.0.227就是客户端的ip, 实际工作中, 一般为客户端公网出口ip, 可以予以统计分析
一般生产环境中, 会用HA做四层, Nginx做七层, 因此, 不仅要在HA上做ip地址透传, Nginx上也要做透传, 才能把客户端ip和中间代理ip都传到后端服务器.
client(10.0.0.29) ---> HA(10.0.0.19) 四层 --> Nginx(10.0.0.81,10.0.0.82) 七层 --> 后端服务器组(10.0.0.237,10.0.0.227)
Nginx七层配置举例:
http{
...
upstream nginx {
server 10.0.0.237;
server 10.0.0.227;
}
...
}
...
location / {
proxy_pass http://nginx;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
后端服务器日志配置:
Nginx: "http_x_forwarded_for"
Apache: \"%{X-Forwarded-For}i\"
Tomcat: pattern='%{X-Forwarded-For}i
日志格式: 以Nginx为例
10.0.0.29为客户端地址, 实际中为公网出口
10.0.0.19为HA的地址
10.00.81为Nginx地址
"10.0.0.29, 10.0.0.19" - 10.0.0.82 - - [24/May/2021:00:24:06 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.81 - - [24/May/2021:00:24:06 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.82 - - [24/May/2021:00:24:08 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.81 - - [24/May/2021:00:24:08 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.82 - - [24/May/2021:00:24:38 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.81 - - [24/May/2021:00:24:40 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.82 - - [24/May/2021:00:24:46 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.81 - - [24/May/2021:00:24:48 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
"10.0.0.29, 10.0.0.19" - 10.0.0.82 - - [24/May/2021:00:24:54 +0800] "GET / HTTP/1.0" 200 19 "-" "curl/7.58.0" "10.0.0.29, 10.0.0.19"
补充: ip地址透传在安全防控时很有帮助, 通过在后端服务器统计源ip地址, 定义一个时间段内的访问次数阈值, 超过阈值就可以认为是恶意访问, 之后直接在防火墙把源地址封掉
5. HAproxy高级功能及配置
5.1 基于cookie的会话保持
当后端服务器都没有做session共享时, 可以在haproxy做cookie会话保持
这时一般会基于cookie而不是源地址hash, 因为基于源地址hash会把来自同一个公网ip的用户全部调度到同一个后端服务器上
原理:
用户第一次访问时, HA会给用户调度到一个服务器上, 当HA收到服务器的响应报文时, HA会给用户生成一个cookie然后添加到响应报文中. 这样用户就拿到HA的cookie了, 用户再次访问时, 可以携带这个cookie, 之后HA收到用户请求会检查cookie和后端服务器的对应关系然后一直转到同一个服务器上
cookie是HA自定义的一个值, 对于不同的后端服务器, 要有不同的cookie值, 也就是把cookie值和后端服务器做绑定. cookie值不要设置太长, 否则报文头部会很长, 只要能在不同的cookie间区分即可
注意: 如果想用HA做会话保持, 那么HA必须是工作在http七层模式下才行, 这样HA才能获取到请求报文中的字段, 比如cookie, User-Agent等
5.1.1 配置选项
cookie name [ rewrite | insert | prefix ][ indirect ] [ nocache ][ postonly ]
[ preserve ][ httponly ] [ secure ][ domain ]* [ maxidle ][ maxlife ]
name:cookie 的key名称,⽤于实现持久连接
insert:#如果客户端请求报⽂没有cookie就插⼊新的cookie到响应报⽂,如第⼀次访问HAProxy
indirect:#不会向客户端发送服务器已经处理过请求的cookie信息,⽐如后端服务器宕机后
HAProxy将客户端请求强制转发⾄real server则会涉及修改cookie,不建议配置
nocache:#当client和hapoxy之间有缓存时,haproxy不缓存客户端cookie,因为客户端浏览器会
缓存cookie并携带cookie访问haproxy
5.1.2 配置实例
indirect, 一般不建议配置, 即使后端服务器把cookie信息修改了, 也应该给客户端发送
listen web1
bind 10.0.0.11:80
mode http
cookie SERVER-COOKIE insert indirect nocache
server web1 10.0.0.238:80 cookie web1 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.228:80 cookie web2 weight 1 check inter 3000 fall 3 rise 5
在没有设置cokkie前, 请求报文和响应报文都是没有cookie的
重启HA, 使cookie配置生效
再次访问可以看到响应报文添加了cookie
客户端获取cookie后, 再次访问, 请求报文就会携带了cookie了
基于cookie的会话保持, 对于后端服务器的调度更平均, 每个服务器的负载更平均, 哪怕用户都是从同一个公网ip访问, 只要没有携带cookie, HA就会按照定义的调度算法进行调度, 并且分配cookie, 之后基于cookie来调度.
cookie vs session
cookie 和 session 都是为了解决http协议无法保存客户端连接状态的问题, 因为http协议是无状态的
cookie一般是服务器端生成, 保存在浏览器内存中的一段数据, 包括用户请求的路径, 域名等等信息. 用户第一次访问时, 服务器会生成cookie并把用户请求的信息保留下来, 然后发给客户端, 但是服务器本身并不会保存这个cookie只是会记录一个对应关系而已. 客户端下次在访问时, 客户端会携带cookie, 然后服务器会检查cookie和自己本地存的对应信息判断用户是不是第一次访问以及访问的内容是什么
session也是在服务器端生产, 是服务器为每个客户端连接专门开辟的一段地址空间, 用户第一次访问时, 服务器会给用户开辟session, 并且生成一个sessionid, 然后将sessionid以cookie的形式发给用户, 并且同时在自己的内存或者硬盘或者其他共享存储中记录sessionid和用户的对应关系, 之后用户访问的时候会携带这个cookie, cookie里面有sessionid, 服务器就可以根据sessionid判断用户身份了
HA中, cookie主要是当后端服务器没有做session共享时, 在负载均衡服务器上启用, 来区分用户, 一般如果想实现会话保持session用的比较多, 都是基于session共享来区分用户, 在后端服务器上配置session共享
5.2 HAproxy状态页
HA的状态页没nginx的功能多, 但是可以管理服务器, 比如强制给服务器上线下线, 但是需要开启才行, 否则也只能查看后端服务器的信息
5.2.1 状态页配置项
stats enable #基于默认的参数启⽤stats page
stats hide-version #隐藏版本, 如果不隐藏, 那么会显示当前HA的版本和发行时间
stats refresh #设定⾃动刷新时间间隔, 默认是不刷新的, 数据显示的就是登录时的数据
stats uri #⾃定义stats page uri,默认值:/haproxy?stats
stats realm #账户认证时的提示信息,示例:stats realm : HAProxy\ Statistics
stats auth : #认证时的账号和密码,可使⽤多次,配置多个账号和密码 默认:no authentication, 账号密码可以定义多个, 并且不需要创建, 在配置文件定义好即可
stats admin { if | unless } #启⽤stats page中的管理功能, 可以定义为if TRUE, 这样只要用户登录成功, 那么就可以进行管理,但是只能进行简单的管理比如服务器上线下线. 如果不开启, 那么用户登录后只能进行状态查看
5.2.2 启⽤状态页
listen stats
mode http #状态页要基于http
bind 0.0.0.0:9999 #可以设定独立的监听ip和端口号
stats enable
stats hide-version
stats uri /haproxy-status
stats realm "Authentication \ is \ required!"
stats auth hadmin:123456
log global
stats admin if TRUE #TRUE要大写
stats refresh 10s
5.2.3 登录状态⻚
pid = 2548(process #1, nbproc = 1, nbthread = 1) #pid为当前pid号,process为当前状态页当前使用的进程号,nbproc和nbthread为⼀共多少进程和每个进程多少个线程
uptime = 0d 0h00m08s #启动了多⻓时间
system limits: memmax = unlimited; ulimit-n = 4096 #系统资源限制:内存/最⼤打开⽂件数/
maxsock = 131124; maxconn = 65536; maxpipes = 0 #最⼤socket连接数/单进程最⼤连接数/最⼤管道数maxpipes
current conns = 1; current pipes = 0/0; conn rate = 1/sec bit rate = 190.610 kbps #当前连接数/当前管道数/当前连接速率
Running tasks: 1/9; 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)
- 如何统计网站并发情况:
- 可以根据HA状态页的current conns数值, 但是这个是单个ip上的, 如果用了keepalive就不太好统计; 而且HA有可能会负载多个业务的4层负载
- 一般可以看nginx的活动连接数, 然后把每台nginx活动连接数加起来, 即可得到并发情况, 这样可以统计单个业务的并发, 因为nginx一般不会代理多个业务
conn rate值如果高的话, 表示请求很多, bit rate也是一样
服务型下线方法, 一是通过socker文件, 把服务器标记为下线状态, 二是通过socket文件把服务器的权重标记为0; 无论用哪种方式, 如果HA上开启了基于cookie的会话保持, 那么对服务器进行下线时, 一定要取消这台服务器的cookie设定, 否则还会往上面调度
1. 状态页, 给服务器设置为DOWN, MAINT
2. socket文件, 给设置设置为DOWN,MAINT
3. 如果是动态算法, 也可以通过socket文件, 把要下线的服务器的权重设置为0
不过一般都是直接给服务器标记为MAINT, 不会去修改权重
注意: 无论是通过socket文件, 还是通过状态页把服务器下线, 一旦HA重启或者reload, 那么被下线的服务器, 就又会上线了, 因为一旦重启或者reload, 那么就会按照HA的配置文件定义来上线服务器, 因此, 服务器下线后, 不要马上重启HA, 而是等维护完毕, 再通过socket文件或者状态页手动把服务器上线
- 一旦服务器有队列值就说明后端服务器连接达到上线了, 后续的请求就要排队等待, 这时要考虑检查连接或者增加服务器了
5.2.4 backend server信息
5.3 报文修改
在http模式下,基于实际需求修改客户端的请求报⽂与响应报⽂,通过reqadd
和reqdel
在请求报⽂添加
删除字段,通过rspadd
与rspidel
在响应报⽂中添加与删除字段, 需要配置listen中
haproxy2.0以前和之后的版本配置指令有区别, 具体参考官方文档
5.3.1 配置选项
HA-2.0以前的版本
reqadd [{if | unless} ]
从请求报⽂中删除匹配正则表达式的⾸部
reqdel [{if | unless} ]
reqidel [{if | unless} ]
在响应报⽂尾部添加指定⾸部
rspadd [{if | unless} ]
示例:
rspadd X-Via:\ HAPorxy
从响应报⽂中删除匹配正则表达式的⾸部
rspidel [{if | unless} ]
rspidel [{if | unless} ]
示例:
rspidel server.* #从响应报⽂删除server信息
rspidel X-Powered-By:.* #从响应报⽂删除X-Powered-By信息
HA-2.0以后的版本
增加响应报文头部信息: http-after-response set-header X-Via HAProxy
删除响应报文头部信息: http-after-response del-header server
5.3.2 配置示例
修改前的请求报文和响应报文:
举例1: 告知客户端是通过HAproxy获得的资源
# 以下三种形式都可以, 需要配置在listen中
http-after-response set-header X-Via HAProxy
http-after-response set-header X-Via "HAProxy 2.2.5"
http-after-response set-header X-Via HAProxy\ 2.2.5
举例2: 删除后端服务器版本信息
http-after-response del-header server #想删除响应报文哪个字段就直接写key值即可, HA后端带的无论是apache还是Nginx都可以直接通过这个方式隐藏后端服务器的信息,
5.4 HAproxy日志配置
配置HAproxy记录日志到指定日志文件中, 包括用户访问日志, 和自身运行日志
需要配置在http模式下
两种方式, 一是通过进程名, 二是通过rsyslog来记录日志
5.4.1 在ubuntu下用apt安装haproxy的情况
- apt安装haproxy后会在/etc/rsyslog.d目录下生成49-haproxy.conf文件. 文件中定义了haproxy的日志会记录在, /var/log/haproxy.log. 这种是通过进程名来记录日志
[root@Ubuntu-1804-2:~]# vim /etc/rsyslog.d/49-haproxy.conf
# Create an additional socket in haproxy's chroot in order to allow logging via
# /dev/log to chroot'ed HAProxy processes
$AddUnixListenSocket /var/lib/haproxy/dev/log
# Send HAProxy messages to a dedicated logfile
:programname, startswith, "haproxy" {
/var/log/haproxy.log
stop
}
- 打开rsyslog的udp 514端口
rsyslog基于udp记录日志, 因此打开udp 514端口即可
[root@Ubuntu-1804-2:~]# vim /etc/rsyslog.conf
# provides UDP syslog reception
module(load="imudp")
input(type="imudp" port="514")
- 开启haproxy和rsyslog服务
[root@Ubuntu-1804-2:~]# systemctl enable --now haproxy
Synchronizing state of haproxy.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable haproxy
[root@Ubuntu-1804-2:~]# systemctl enable --now rsyslog
Synchronizing state of rsyslog.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable rsyslog
- 查看haproxy日志
#如果日志文件没有产生, 需要重启下haproxy.
#默认情况下, 日志只会记录haproxy的运行日志以及对于, 以及哪个客户端ip和端口号访问了haproxy以及转发请况, 详细的访问日志需要手动配置
[root@Ubuntu-1804-2:~]# ll /var/log/haproxy.log
-rw-r----- 1 syslog adm 914 Nov 23 14:34 /var/log/haproxy.log
[root@Ubuntu-1804-2:~]# vim /var/log/haproxy.log
2021-03-26T00:03:50+08:00 localhost haproxy[2972]: 10.0.0.1:62477 [26/Mar/2021:00:03:50.705] web1 web1/web2 0/0/1/1/2 200 229 - - ---- 3/2/0/0/0 0/0 "GET / HTTP/1.1"
5.4.2 ubuntu下编译安装的haproxy如何记录日志
编译安装的haproxy不会在/etc/rsyslog.d下创建49-haproxy.conf文件, 可以从apt安装那拷贝过来, 放到/etc/rsyslog.d目录下, 因为/etc/rsyslog.conf中定义了, rsyslog开启服务时, 也会加载/etc/rsyslog.d目录中的.conf文件.
或者, 可以在haproxy.conf配置文件中定义如何记录日志
默认的日志记录配置
global
log /dev/log local0
log /dev/log local1 notice
手动指定日志记录位置, 以及记录级别
global
log 127.0.0.1 local3 info # 这样HA的日志会记录到本地的local3, 级别是info以上, 之后还要在rsyslog配置中, 指定local3记录的位置
编辑rsyslog.conf配置文件
local3.* /var/log/haproxy.log # *表示所有级别, 一旦rsyslog收到日志是local3所有级别的日志, 都会记录到/var/log/haproxy.log
# provides UDP syslog reception
module(load="imudp") # 开启udp
input(type="imudp" port="514")
# provides TCP syslog reception
module(load="imtcp") # 开启tcp
input(type="imtcp" port="514")
编辑了配置文件后, 重启haproxy和rsyslog服务, 日志文件就会生成
root@HAproxy-1:~# systemctl restart haproxy
root@HAproxy-1:~# systemctl restart rsyslog
root@HAproxy-1:~# ll /var/log/haproxy.log
-rw-r--r-- 1 syslog syslog 380 Mar 25 23:58 /var/log/haproxy.log
# 查看日志
2021-03-25T23:59:26+08:00 localhost haproxy[2972]: 10.0.0.49:47816 [25/Mar/2021:23:59:26.591] web1 web1/web1 0/0/0/1/1 200 229 - - ---- 1/1/0/0/0 0/0 "GET / HTTP/1.1"
5.5 自定义haproxy日志格式
需要在haproxy.conf文件配置, 对应Centos或者Ubuntu的配置都一样
一般记录请求报文的host字段和user-agent, 很少记录响应报文的字段
配置选项;
capture cookie len #捕获请求和响应报⽂中的 cookie并记录⽇志
capture request header len #捕获请求报⽂中指定的⾸部内容和⻓度并记录⽇志
capture response header len #捕获响应报⽂中指定的内容和⻓度⾸部并记录⽇志
示例:
capture request header Host len 16 #len是字符数, 也就是记录某个字段的多少个字符
capture request header User-Agent len 512
capture request header Referer len 15
配置实例:
listen web1
bind 10.0.0.19:80
mode http
balance roundrobin
log global #记录日志一定要指明log global, 表明调用了global选项里的 log 127.0.0.0 local3 info的配置, 用rsyslog记录日志, 再由rsyslog把日志写到某个文件里, 虽然defaults中配置了log global, 但是要修改日志格式必须在listen中也配置log global
option httplog #⽇志格式选项, 如果要记录http日志, 一定要加option httplog, 可以配置在default和listenn里
capture request header Host len 15 # 记录客户端访问的ip或者主机名
capture request header User-Agent len 512
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
server web2 10.0.0.49:8080 weight 1 check inter 3000 fall 3 rise 5
修改前:
2021-03-26T00:13:40+08:00 localhost haproxy[2972]: 10.0.0.1:62671 [26/Mar/2021:00:13:40.779] web1 web1/web2 0/0/0/1/1 200 229 - - ---- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
修改后:
2021-03-26T00:25:52+08:00 localhost haproxy[3212]: 10.0.0.39:49986 [26/Mar/2021:00:25:52.529] web1 web1/web1 0/0/0/1/1 200 229 - - ---- 1/1/0/0/0 0/0 {haproxy.in.org|curl/7.58.0} "GET / HTTP/1.1"
# 新添加的字段信息, 会记录在"GET /HTTP/1.1前面"
5.6 压缩功能
对响应给客户端的报文进行压缩, 以节省网络带宽, 但是会占用部分cpu性能
一般都在nginx上配置压缩, 并且网站静态文件不要太大, 一般几十KB就差不多了, 尽量不要超过1M. 文本文件要压缩, 并且本身不能过大, 图片文件也不要太大, 否则占用带宽也影响传输速度
配置选项
#配置在listen中, 而且是在http模式下
compression algo ALGO #启用http协议中的压缩机制, 常用的算法有gzip deflate
identity #调试使用的压缩方式
gzip #常用的压缩方式, 与各浏览器兼容较好
deflate #有些浏览器不支持
raw-deflate #新出的压缩方式
compression type TYPE#要压缩的文件类型
配置示例
compression algo gzip deflate
compression type text/plain test/html test/css test/xml test/javascript application/javascript
准备压缩数据, 利用/var/log/syslog文件, 复制到/var/www/html主站点下, 改名文syslog.html, 多追加几次数据进去, 构建大文件, 并且修改权限, 否则会报403, 因此其他用户是没有权限查看syslog日志的
chmod +r /var/www/html/syslog.html
root@web-1:/var/www/html# ll
total 9108
drwxr-xr-x 2 root root 4096 Mar 26 00:35 ./
drwxr-xr-x 3 root root 4096 Mar 24 20:31 ../
-rw-r--r-- 1 root root 16 Mar 24 20:35 index.html
-rw-r--r-- 1 root root 612 Mar 25 19:14 index.nginx-debian.html
-rw-r--r-- 1 syslog adm 9307648 Mar 26 00:34 syslog.html # 准备的文件
利用curl命令访问:
# curl默认不压缩, 要加--compressed
curl --compressed http://10.0.0.19/syslog.html
压缩前:
# 可以看到数据是9307870
root@HAproxy-1:~# tail -n1 /var/log/haproxy.log
2021-03-26T00:43:31+08:00 localhost haproxy[3458]: 10.0.0.39:49990 [26/Mar/2021:00:43:27.554] web1 web1/web2 0/0/3/1/3732 200 9307870 - - ---- 2/2/0/0/0 0/0 {10.0.0.19|curl/7.58.0} "GET /syslog.html HTTP/1.1"
压缩后:
# 可以看到变成了1592140
2021-03-26T00:49:45+08:00 localhost haproxy[3515]: 10.0.0.49:47822 [26/Mar/2021:00:49:43.941] web1 web1/web2 0/0/1/4/1936 200 1592140 - - ---- 1/1/0/0/0 0/0 {10.0.0.19|curl/7.58.0} "GET /syslog.html HTTP/1.1"
5.7 web服务器状态监测
5.7.1 三种状态检测方式
三种状态监测方式
基于四层的传输端口做状态监测
基于指定uri做状态监测
基于指定uri的request请求头部内容做状态监测
option httpchk
option httpchk
option httpchk
option httpchk
# 这种就是基于4层传输端口做状态监测, HA会按照设定的检测间隔, 向后端服务器发起tcp连接, 来探测后端服务器存活情况
server web1 10.0.0.39:8080 weight 1 check inter 3000 fall 3 rise 5
5.7.2 不同检测方式分析
基于四层的探测有可能会存在问题, 比如服务进程还存在, 但是实际服务已经挂了, 尤其是java的内存溢出情况, 因此, 基于四层探测数据库服务还可以, web服务等七层服务不会用四层探测, 而是基于url做监测, 除了要监测四层端口, 也要监测url, 除了四层端口正常工作, url也必须正常响应才行
一般会在后端服务器比如apache, nginx, 设置一个监控的url, HA会定期按照指定的对后端服务器的探测频率, 访问每个后端服务器上的url去探测存, 如果是java服务或者微服务, 就会设置一个api, 来周期性监测是否访问结果为200. 如果显示超时或者5xx报错, haproxy会把对应的服务器踢出去
基于url做状态监测起始也有弊端, url有时候会返回一个文件, 文件也是有大小的, 会消耗带宽.
所以可以基于url的响应报文头部内容比如状态码做状态检测. 基于server中定义的inter时间, 周期性对后端服务器做监测, 只有haproxy能够成功访问该文件, 并且状态码是200, 才表示后端服务器还在线
# 检测的准确性和效率对比
四层端口 < 基于url < 基于url的响应报文头部信息
5.7.3 检测案例
示例: 基于后端服务器某个url进行探测
在后端服务器数据目录创建monitor文件夹, 专门用来存放用于做监测的文件
注意: 监控页面都是不能更改的, 并且后端服务器升级时, 要保留监控文件, 否则监控失败就会把后端服务器踢出去了
# 10.0.0.39
root@web-1:~# mkdir /var/www/html/monitor
root@web-1:~# cd /var/www/html/monitor/
root@web-1:/var/www/html/monitor# hostname -I > monitor.info
root@web-1:/var/www/html/monitor# ll
total 12
drwxr-xr-x 2 root root 4096 Mar 26 00:58 ./
drwxr-xr-x 3 root root 4096 Mar 26 00:57 ../
-rw-r--r-- 1 root root 11 Mar 26 00:58 monitor.info
#监控文件的权限一定要修改. 让其他人可读, 或者haproxy可读
# 10.0.0.49
root@web-2:~# cd /var/www/html/
root@web-2:/var/www/html# mkdir monitor
root@web-2:/var/www/html# cd monitor/
root@web-2:/var/www/html/monitor# hostname -I > monitor.info
root@web-2:/var/www/html/monitor# ll
total 12
drwxr-xr-x 2 root root 4096 Mar 26 01:00 ./
drwxr-xr-x 3 root root 4096 Mar 26 01:00 ../
-rw-r--r-- 1 root root 11 Mar 26 01:00 monitor.info
haproxy配置监控, 配置在listen里
option httpchk GET /monitor/monitor.html #这里的根是相对于后端服务器数据目录, HAproxy访问时是访问的是http://10.0.0.19/monitor/monitor.html
所有后端的服务器, 都要做相同的配置, 相同的文件存放路径
配置完后测试haproxy能访问该文件, 如果能访问就说明检测配置好了
目前两台服务器都是正常的
并且抓包可以看到HA会定期访问这个url
之后可以把其中一台后端服务器上的该文件移走, 测试haproxy可以监测到, 并且把该服务器踢出
在10.0.0.39上把监测文件移走
mv /var/www/html/monitor/monitor.html /opt
这时, 抓包, 首先会看到404报错, HA访问不到10.0.0.39上的monitor.html文件
当检查失败此时到达设置的次数后, 10.0.0.39会被踢出服务器组
测试haproxy不会在往228调度
#先在两天服务器上做一个nginx默认页面
echo 10.0.0.238 > /usr/share/nginx/html/index.html
echo 10.0.0.228 > /usr/share/nginx/html/index.html
验证结果
#可以看到已经不会往228调度了
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
现在恢复228的监测页面
root@web-1:/var/www/html/monitor# mv /opt/monitor.html .
抓包看到HA又可以访问10.0.0.39的监控页面了
达到预定要求后, 10.0.0.39就会被添加回调度群组
验证10.0.0.39继续被调度
web-1 10.0.0.39
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-1 10.0.0.39
root@web-1:/var/www/html/monitor# curl 10.0.0.19
web-2 10.0.0.49
但是基于url的探测, 后端服务器会传输整个文件给HA, 消耗内网带宽
下面修改监测方式为只监测响应报文的头部信息
# 配置10.0.0.19是告诉后端服务器表示是负载均衡器再请求头部信息, 防止由于负载均衡器不断的请求而被封
option httpchk HEAD /monitor/monitor.html HTTP/1.0\r\nHost:\ 10.0.0.19
抓包查看HA探测过程
总结:
对于日志记录,压缩以及访问权限控制, 正则匹配, 一般都是在Nginx开启, 让HA只负责四层的转发
对于java服务, 也可以不记录日志, 让Nginx来记录日志, 这样减轻java服务器的负担
如果需要HA来记录日志, 进行压缩或者进行访问控制, 那么HA需要工作在七层, 因为只有七层才会对请求和响应报文做拆分, 拿到数据后, 添加或者删除信息, 再重新封装