1、简述keepalived工作原理
1.Keepalived 定义
Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案,可以利用其来避免单点故障。一个LVS服务会有2台服务器运行Keepalived,一台为主服务器(MASTER),一台为备份服务器(BACKUP),但是对外表现为一个虚拟IP,主服务器会发送特定的消息给备份服务器,当备份服务器收不到这个消息的时候,即主服务器宕机的时候, 备份服务器就会接管虚拟IP,继续提供服务,从而保证了高可用性。Keepalived是VRRP的完美实现,因此在介绍keepalived之前,先介绍一下VRRP的原理。
2.VRRP 协议简介
在现实的网络环境中,两台需要通信的主机大多数情况下并没有直接的物理连接。对于这样的情况,它们之间路由怎样选择?主机如何选定到达目的主机的下一跳路由,这个问题通常的解决方法有二种:
在主机上使用动态路由协议(RIP、OSPF等)
在主机上配置静态路由
很明显,在主机上配置动态路由是非常不切实际的,因为管理、维护成本以及是否支持等诸多问题。配置静态路由就变得十分流行,但路由器(或者说默认网关default gateway)却经常成为单点故障。VRRP的目的就是为了解决静态路由单点故障问题,VRRP通过一竞选(election)协议来动态的将路由任务交给LAN中虚拟路由器中的某台VRRP路由器。
3.VRRP 工作机制
在一个VRRP虚拟路由器中,有多台物理的VRRP路由器,但是这多台的物理的机器并不能同时工作,而是由一台称为MASTER的负责路由工作,其它的都是BACKUP,MASTER并非一成不变,VRRP让每个VRRP路由器参与竞选,最终获胜的就是MASTER。MASTER拥有一些特权,比如,拥有虚拟路由器的IP地址,我们的主机就是用这个IP地址作为静态路由的。拥有特权的MASTER要负责转发发送给网关地址的包和响应ARP请求。
VRRP通过竞选协议来实现虚拟路由器的功能,所有的协议报文都是通过IP多播(multicast)包(多播地址224.0.0.18)形式发送的。虚拟路由器由VRID(范围0-255)和一组IP地址组成,对外表现为一个周知的MAC地址。所以,在一个虚拟路由 器中,不管谁是MASTER,对外都是相同的MAC和IP(称之为VIP)。客户端主机并不需要因为MASTER的改变而修改自己的路由配置,对客户端来说,这种主从的切换是透明的。
在一个虚拟路由器中,只有作为MASTER的VRRP路由器会一直发送VRRP通告信息(VRRPAdvertisement message),BACKUP不会抢占MASTER,除非它的优先级(priority)更高。当MASTER不可用时(BACKUP收不到通告信息), 多台BACKUP中优先级最高的这台会被抢占为MASTER。这种抢占是非常快速的(<1s),以保证服务的连续性。由于安全性考虑,VRRP包使用了加密协议进行加密。
4.VRRP 工作流程
(1).初始化:
路由器启动时,如果路由器的优先级是255(最高优先级,路由器拥有路由器地址),要发送VRRP通告信息,并发送广播ARP信息通告路由器IP地址对应的MAC地址为路由虚拟MAC,设置通告信息定时器准备定时发送VRRP通告信息,转为MASTER状态;否则进入BACKUP状态,设置定时器检查定时检查是否收到MASTER的通告信息。
(2).Master
设置定时通告定时器;
用VRRP虚拟MAC地址响应路由器IP地址的ARP请求;
转发目的MAC是VRRP虚拟MAC的数据包;
如果是虚拟路由器IP的拥有者,将接受目的地址是虚拟路由器IP的数据包,否则丢弃;
当收到shutdown的事件时删除定时通告定时器,发送优先权级为0的通告包,转初始化状态;
如果定时通告定时器超时时,发送VRRP通告信息;
收到VRRP通告信息时,如果优先权为0,发送VRRP通告信息;否则判断数据的优先级是否高于本机,或相等而且实际IP地址大于本地实际IP,设置定时通告定时器,复位主机超时定时器,转BACKUP状态;否则的话,丢弃该通告包;
(3).Backup
设置主机超时定时器;
不能响应针对虚拟路由器IP的ARP请求信息;
丢弃所有目的MAC地址是虚拟路由器MAC地址的数据包;
不接受目的是虚拟路由器IP的所有数据包;
当收到shutdown的事件时删除主机超时定时器,转初始化状态;
主机超时定时器超时的时候,发送VRRP通告信息,广播ARP地址信息,转MASTER状态;
收到VRRP通告信息时,如果优先权为0,表示进入MASTER选举;否则判断数据的优先级是否高于本机,如果高的话承认MASTER有效,复位主机超时定时器;否则的话,丢弃该通告包;
5.ARP查询处理
当内部主机通过ARP查询虚拟路由器IP地址对应的MAC地址时,MASTER路由器回复的MAC地址为虚拟的VRRP的MAC地址,而不是实际网卡的 MAC地址,这样在路由器切换时让内网机器觉察不到;而在路由器重新启动时,不能主动发送本机网卡的实际MAC地址。如果虚拟路由器开启的ARP代理 (proxy_arp)功能,代理的ARP回应也回应VRRP虚拟MAC地址;
2、编译安装haproxy
环境准备
由于CentOS7 之前版本自带的lua版本比较低并不符合HAProxy要求的lua最低版本(5.3)的要求,因此需要编译安装较新版本的lua环境,然后才能编译安装HAProxy,过程如下:
#当前系统版本
[root@centos7 ~]#lua -v
Lua 5.1.4? Copyright (C) 1994-2008 Lua.org, PUC-Rio
#安装基础命令及编译依赖环境
[root@centos7 ~]#yum install gcc readline-devel -y
[root@centos7 ~]#tar xvf lua-5.4.4.tar.gz -C /usr/local/src
[root@centos7 ~]#cd /usr/local/src/lua-5.4.4/
[root@centos7 lua-5.4.4]#make linux test
#查看编译安装的版本
[root@centos7 lua-5.4.4]#src/lua -v
Lua 5.4.4? Copyright (C) 1994-2022 Lua.org, PUC-Rio
1编译安装HAProxy
#HAProxy 2.0以上版本编译参数:
[root@centos7 ~]#yum -y install gcc openssl-devel pcre-devel systemd-devel
[root@centos7 ~]#tar xvf haproxy-2.4.15.tar.gz -C /usr/local/src
[root@centos7 ~]#cd /usr/local/src/haproxy-2.4.15/
#查看安装方法
[root@centos7 haproxy-2.4.15]#less INSTALL
[root@centos7 haproxy-2.4.15]#less Makefile
#参考INSTALL文件进行编译安装
[root@centos7 haproxy-2.4.15]#make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_LUA=1 LUA_INC=/usr/local/src/lua-5.4.4/src/ LUA_LIB=/usr/local/src/lua-5.4.4/src/
[root@centos7 haproxy-2.4.15]#make install PREFIX=/apps/haproxy
[root@centos7 haproxy-2.4.15]#ln -s /apps/haproxy/sbin/haproxy /usr/sbin/
#查看生成的文件
[root@centos7 haproxy-2.4.15]#tree /apps/haproxy/
/apps/haproxy/
├── doc
│? └── haproxy
│? ? ? ├── 51Degrees-device-detection.txt
│? ? ? ├── architecture.txt
│? ? ? ├── close-options.txt
│? ? ? ├── configuration.txt
│? ? ? ├── cookie-options.txt
│? ? ? ├── DeviceAtlas-device-detection.txt
│? ? ? ├── intro.txt
│? ? ? ├── linux-syn-cookies.txt
│? ? ? ├── lua.txt
│? ? ? ├── management.txt
│? ? ? ├── netscaler-client-ip-insertion-protocol.txt
│? ? ? ├── network-namespaces.txt
│? ? ? ├── peers.txt
│? ? ? ├── peers-v2.0.txt
│? ? ? ├── proxy-protocol.txt
│? ? ? ├── regression-testing.txt
│? ? ? ├── seamless_reload.txt
│? ? ? ├── SOCKS4.protocol.txt
│? ? ? ├── SPOE.txt
│? ? ? └── WURFL-device-detection.txt
├── sbin
│? └── haproxy
└── share
? ? └── man
? ? ? ? └── man1
? ? ? ? ? ? └── haproxy.1
6 directories, 22 files
1.1验证HAProxy版本
#验证HAProxy版本:
[root@centos7 haproxy-2.4.15]#which haproxy
/usr/sbin/haproxy
[root@centos7 haproxy-2.4.15]#haproxy -v
HAProxy version 2.4.15-7782e23 2022/03/14 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-2.4.15.html
Running on: Linux 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
#大写-V选项显示版本和帮助用法
[root@centos7 haproxy-2.4.15]#haproxy -V
HAProxy version 2.4.15-7782e23 2022/03/14 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-2.4.15.html
Running on: Linux 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
Usage : haproxy [-f <cfgfile|cfgdir>]* [ -vdVD ] [ -n <maxconn> ] [ -N <maxpconn> ]
? ? ? ? [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]
? ? ? ? -v displays version ; -vv shows known build options.
? ? ? ? -d enters debug mode ; -db only disables background mode.
? ? ? ? -dM[<byte>] poisons memory with <byte> (defaults to 0x50)
? ? ? ? -V enters verbose mode (disables quiet mode)
? ? ? ? -D goes daemon ; -C changes to <dir> before loading files.
? ? ? ? -W master-worker mode.
? ? ? ? -Ws master-worker mode with systemd notify support.
? ? ? ? -q quiet mode : don't display messages
? ? ? ? -c check mode : only check config files and exit
? ? ? ? -n sets the maximum total # of connections (uses ulimit -n)
? ? ? ? -m limits the usable amount of memory (in MB)
? ? ? ? -N sets the default, per-proxy maximum # of connections (0)
? ? ? ? -L set local peer name (default to hostname)
? ? ? ? -p writes pids of all children to this file
? ? ? ? -de disables epoll() usage even when available
? ? ? ? -dp disables poll() usage even when available
? ? ? ? -dS disables splice usage (broken on old kernels)
? ? ? ? -dG disables getaddrinfo() usage
? ? ? ? -dR disables SO_REUSEPORT usage
? ? ? ? -dL dumps loaded object files after config checks
? ? ? ? -dr ignores server address resolution failures
? ? ? ? -dV disables SSL verify on servers side
? ? ? ? -dW fails if any warning is emitted
? ? ? ? -dD diagnostic mode : warn about suspicious configuration statements
? ? ? ? -sf/-st [pid ]* finishes/terminates old pids.
? ? ? ? -x <unix_socket> get listening sockets from a unix socket
? ? ? ? -S <bind>[,<bind options>...] new master CLI
1.2准备HAProxy启动文件
#创建service文件
[root@centos7 haproxy-2.4.15]#vim /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
[Service]
ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
[root@centos7 haproxy-2.4.15]#systemctl daemon-reload
1.3配置文件
#创建自定义的配置文件
[root@centos7 haproxy-2.4.15]#mkdir /etc/haproxy
[root@centos7 haproxy-2.4.15]#vim /etc/haproxy/haproxy.cfg
global
? ? maxconn 100000
? ? chroot /apps/haproxy
? ? stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
? ? #uid 99
? ? #gid 99
? ? user haproxy
? ? group haproxy
? ? daemon
? ? #nbproc 4
? ? #cpu-map 1 0
? ? #cpu-map 2 1
? ? #cpu-map 3 2
? ? #cpu-map 4 3
? ? pidfile /var/lib/haproxy/haproxy.pid
? ? log 127.0.0.1 local2 info
defaults
? ? option http-keep-alive
? ? option forwardfor
? ? maxconn 100000
? ? mode http
? ? timeout connect 300000ms
? ? timeout client 300000ms
? ? timeout server 300000ms
listen stats
? ? mode http
? ? bind 0.0.0.0:9999
? ? stats enable
? ? log global
? ? stats uri /haproxy-status
? ? stats auth haadmin:123456
listen web_port
? ? bind 10.0.0.7:80
? ? mode http
? ? log global
? ? server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
1.4启动 haproxy
#准备socket文件目录
[root@centos7 haproxy-2.4.15]#mkdir /var/lib/haproxy
#设置用户和目录权限
[root@centos7 haproxy-2.4.15]#useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy
[root@centos7 haproxy-2.4.15]#systemctl enable --now haproxy
1.5验证 haproxy 状态
[root@centos7 haproxy-2.4.15]#systemctl status haproxy
● haproxy.service - HAProxy Load Balancer
? Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
? Active: active (running) since Sun 2022-04-17 14:10:51 CST; 1min 56s ago
? Process: 3503 ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q (code=exited, status=0/SUCCESS)
Main PID: 3506 (haproxy)
? CGroup: /system.slice/haproxy.service
? ? ? ? ? ├─3506 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
? ? ? ? ? └─3509 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
Apr 17 14:10:51 centos7 systemd[1]: Starting HAProxy Load Balancer...
Apr 17 14:10:51 centos7 systemd[1]: Started HAProxy Load Balancer.
Apr 17 14:10:51 centos7 haproxy[3506]: [NOTICE]? (3506) : New worker #1 (3509) forked
Apr 17 14:10:51 centos7 haproxy[3506]: [WARNING]? (3509) : Server web_port/web1 is DOWN, reason: Layer4 connec...ueue.
Apr 17 14:10:51 centos7 haproxy[3506]: [NOTICE]? (3509) : haproxy version is 2.4.15-7782e23
Apr 17 14:10:51 centos7 haproxy[3506]: [NOTICE]? (3509) : path to executable is /usr/sbin/haproxy
Apr 17 14:10:51 centos7 haproxy[3506]: [ALERT]? ? (3509) : proxy 'web_port' has no server available!
Hint: Some lines were ellipsized, use -l to show in full.
[root@centos7 haproxy-2.4.15]#pstree -p |grep haproxy
? ? ? ? ? |-haproxy(3506)---haproxy(3509)---{haproxy}(3510)
1.6查看haproxy的状态页面
#浏览器访问:http://10.0.0.7:9999/haproxy-status
3、总结haproxy各调度算法的实现方式及其应用场景
HAProxy调度算法
HAProxy通过固定参数 balance 指明对后端服务器的调度算法,该参数可以配置在listen或backend选项中 。
HAProxy的调度算法分为静态和动态调度算法,但是有些算法可以根据参数在静态和动态算法中相互转换。
1 静态算法
静态算法:按照事先定义好的规则轮询进行调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时动态修改权重(只能为0和1,不支持其它值)或者修改后不生效,如果需要修改只能靠重启HAProxy生效。
1.1 static-rr
static-rr:基于权重的轮询调度,该算法与轮循算法相似,只是它是静态的,这意味着动态更改服务器的权重不会产生任何影响。不支持运行时利用socat进行权重的动态调整(只支持0和1,不支持其它值)及后端服务器慢启动,其后端主机数量没有限制,相当于LVS中的 wrr
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance static-rr
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
1.2 first
first:根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务(一旦服务器达到其 maxconn 值,使用下一个服务器)。该算法的目的是始终使用最小的服务器数量,以便可以关闭额外的服务器在非密集时间。因此会忽略服务器的权重设置,此方式使用较少
不支持用socat进行动态修改权重,可以设置0和1,可以设置其它值但无效,该算法忽略服务器权重。
#代码实现
maxconn <maxconn> #当前后端server的最大并发连接数
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance first
? ? server rs1 10.0.0.17:80 maxconn 1 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试,同时运行下面命令,观察结果
[root@client ~]#while true;do curl http://10.0.0.7/index.html ; sleep 0.1;done
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
2 动态算法
动态算法:基于后端服务器状态进行调度适当调整,新请求将优先调度至当前负载较低的服务器,且权重可以在haproxy运行时动态调整无需重启。
2.1 roundrobin
roundrobin:基于权重的轮询动态调度算法,支持权重的运行时调整,不同于lvs中的rr轮训模式,HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数),其每个后端backend中最多支持4095个real server,支持对real server权重动态调整,roundrobin为默认调度算法,此算法使用广泛
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance roundrobin
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
2.2 leastconn
leastconn:加权的最少连接算法,支持权重的运行时调整和慢启动,即:根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接),比较适合长连接的场景使用,比如:MySQL等场景。
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance leastconn
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
2.3 random
在1.9版本开始增加 random的负载平衡算法,其基于随机数作为一致性hash的key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用,支持weight的动态调整,weight较大的主机有更大概率获取新请求
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance random
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.27
[root@client ~]#curl 10.0.0.7
10.0.0.17
3 其他算法
其它算法既可作为静态算法,又可以通过选项成为动态算法
3.1 source
源地址hash,基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过hash-type选项进行更改
这个算法一般是在不插入Cookie的TCP模式下使用,也可给不支持会话cookie的客户提供最好的会话粘性,适用于session会话保持但不支持cookie和缓存的场景
源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash
3.1.1 map-base 取模法
map-based:取模法,对source地址进行hash计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变,hash-type 指定的默认值为此算法
所谓取模运算,就是计算两个数相除之后的余数,10%7=3, 7%4=3
map-based算法:基于权重取模,hash(source_ip)%所有后端服务器相加的总权重
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance source
? ? hash-type map-based
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
3.1.2 一致性 hash
一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n ,该hash算法是动态的,支持使用 socat等工具进行在线权重调整,支持慢启动
算法:
1、key1=hash(source_ip)%(2^32) [0---4294967295]
2、keyA=hash(后端服务器虚拟ip)%(2^32)
3、将key1和keyA都放在hash环上,将用户请求调度到离key1最近的keyA对应的后端服务器
hash环偏斜问题:
增加虚拟服务器IP数量,比如:一个后端服务器根据权重为1生成1000个虚拟IP,再hash。而后端服务器权
重为2则生成2000的虚拟IP,再进行hash运算,最终在hash环上生成3000个节点,从而解决hash环偏斜问题
一致性hash示意图:后端服务器在线与离线的调度方式
#代码实现
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance source
? ? hash-type consistent
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
[root@client ~]#curl 10.0.0.7
10.0.0.17
3.2 uri
基于对用户请求的URI的左半部分或整个uri做hash,再将hash结果对总权重进行取模后,根据最终结果将请求转发到后端指定服务器,这确保了只要没有服务器启动或关闭,相同的URI将始终定向到同一台服务器。适用于后端是缓存服务器场景,以最大限度地提高缓存命中率。默认是静态算法,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash。
注意:此算法基于应用层,所以只支持 mode http ,不支持 mode tcp
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分:/<path>;<params>
整个uri:/<path>;<params>?<query>#<frag>
#代码实现
[root@rs1 ~]#cd /var/www/html/
[root@rs1 html]#ls
index.html
[root@rs1 html]#for i in {1..10};do echo test$i on 10.0.0.17 > test$i.html;done
[root@rs1 html]#ls
index.html? test1.html? test3.html? test5.html? test7.html? test9.html
test10.html? test2.html? test4.html? test6.html? test8.html
[root@rs2 ~]#cd /var/www/html/
[root@rs2 html]#ls
index.html
[root@rs2 html]#for i in {1..10};do echo test$i on 10.0.0.27 > test$i.html;done
[root@rs2 html]#ls
index.html? test1.html? test3.html? test5.html? test7.html? test9.html
test10.html? test2.html? test4.html? test6.html? test8.html
#uri 一致性hash
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance uri
? ? hash-type consistent
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试,访问不同的uri,确认可以将用户同样的请求转发至相同的服务器
[root@client ~]#curl 10.0.0.7/test1.html
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test1.html
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test1.html
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test1.html
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test2.html
test2 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test3.html
test3 on 10.0.0.27
[root@client ~]#curl 10.0.0.7/test3.html
test3 on 10.0.0.27
[root@client ~]#curl 10.0.0.7/test3.html
test3 on 10.0.0.27
3.3 url_param
url_param对用户请求的url中的 params 部分中的一个参数key对应的value值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server,如果无没key,将按roundrobin算法
#假设:
url = http://www.baidu.com/foo/bar/index.php?key=value
#则:
host = "www.baidu.com"
url_param = "key=value"
#代码实现
#url_param一致性hash
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance url_param userid
? ? hash-type consistent
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7/test4.html?userid=chen
test4 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test4.html?userid=chen
test4 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test4.html?userid=chen
test4 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test1.html?userid=chen
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/index.html?userid=chen
10.0.0.17
[root@client ~]#curl 10.0.0.7/index.html?userid=zhang
10.0.0.27
[root@client ~]#curl 10.0.0.7/index.html?userid=zhang
10.0.0.27
[root@client ~]#curl 10.0.0.7/index.html?userid=zhang
10.0.0.27
3.4 hdr
针对用户每个http头部(header)请求中的指定信息做hash,此处由 name 指定的http首部将会被取出并做hash计算,然后由服务器总权重取模以后派发至某挑出的服务器,如果无有效值,则会使用默认的轮询调度。
#代码实现
#hdr 一致性hash
[root@haproxy ~]#cat /etc/haproxy/conf.d/test.cfg
listen ha1_web_80
? ? bind 10.0.0.7:80
? ? balance hdr(User-Agent)
? ? hash-type consistent
? ? server rs1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
? ? server rs2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
? ? server local 127.0.0.1:80 backup
[root@haproxy ~]#systemctl reload haproxy.service
#测试
[root@client ~]#curl 10.0.0.7/index.html
10.0.0.17
[root@client ~]#curl 10.0.0.7/test1.html
test1 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test2.html
test2 on 10.0.0.17
[root@client ~]#curl 10.0.0.7/test3.html
test3 on 10.0.0.17
[root@client ~]#curl -A "IE" 10.0.0.7/test4.html
test4 on 10.0.0.27
[root@client ~]#curl -A "Chrome18" 10.0.0.7/test4.html
test4 on 10.0.0.17
[root@client ~]#curl -A "Chrome28" 10.0.0.7/test4.html
test4 on 10.0.0.27
[root@client ~]#curl -A "firefox" 10.0.0.7/test4.html
test4 on 10.0.0.17
3.5 rdp-cookie
rdp-cookie对windows远程桌面的负载,使用cookie保持会话,默认是静态,也可以通过hash-type
指定map-based和consistent,来定义使用取模法还是一致性hash。
注意:要实现此算法,需要Windows系统支持远程桌面功能。
4 算法总结
#静态
static-rr--------->tcp/http
first------------->tcp/http
#动态
roundrobin-------->tcp/http
leastconn--------->tcp/http
random------------>tcp/http
#以下静态和动态取决于hash_type是否consistent
source------------>tcp/http
Uri--------------->http
url_param--------->http
hdr--------------->http
rdp-cookie-------->tcp
5 各种算法使用场景
first #使用较少
static-rr #做了session共享的 web 集群
roundrobin
random
leastconn #数据库
source #基于客户端公网 IP 的会话保持
Uri--------------->http #缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯
url_param--------->http #可以实现session保持
hdr #基于客户端请求报文头部做下一步处理
rdp-cookie #基于Windows主机,很少使用
4、使用haproxy的ACL实现基于文件后缀名的动静分离
1 ACL
访问控制列表(ACL,Access Control Lists)是一种基于包过滤的访问控制技术,它可以根据设定的条件对经过服务器传输的数据包进行过滤(条件匹配),即对接收到的报文进行匹配和过滤,基于请求报文头部中的源地址、源端口、目标地址、目标端口、请求方法、URL、文件后缀等信息内容进行匹配并执行进一步操作,比如允许其通过或丢弃。
1.1 ACL配置选项
acl <aclname> <criterion> [flags] [operator] [<value>]
acl ? 名称 匹配规范 匹配模式 ? 具体操作符 ? 操作对象类型
ACL-Name
#ACL名称,可以使用大字母A-Z、小写字母a-z、数字0-9、冒号:、点.、中横线和下划线,并且严格区分大
小写,比如:my_acl和My_Acl就是两个完全不同的acl
ACL-criterion
定义ACL匹配规范,即:判断条件
path_end : suffix match #请求的URL中资源的结尾,如 .gif .png .css .js .jpg .jpeg
ACL-flags
-i 不区分大小写
1.2 ACL基于文件后缀名实现动静分离
[root@haproxy ~]#vim /etc/haproxy/conf.d/test.cfg
frontend ha1_web_80
? ? bind 10.0.0.7:80
? ? balance roundrobin
###################### acl setting ###############################
? ? acl acl_static path_end -i? .jpg .jpeg .png .gif .css .js .html
? ? acl acl_php path_end -i? ? .php
###################### acl hosts #################################
? ? use_backend static_hosts? ? ? ? ? if acl_static
? ? use_backend php_hosts? ? ? ? ? ? if acl_php
###################### backend hosts #############################
backend static_hosts
? ? server rs2 10.0.0.27:80 check inter 3000 fall 2 rise 5
backend php_hosts
? ? server rs1 10.0.0.17:80 check inter 3000 fall 2 rise 5
[root@haproxy ~]#systemctl reload haproxy.service
#分别在后端两台主机准备相关文件
[root@rs1 html]#pwd
/var/www/html
[root@rs1 html]#echo 10.0.0.17 > test.php
[root@rs2 html]#pwd
/var/www/html
[root@rs2 html]#echo 10.0.0.27 > index.html
#测试
[root@client ~]#curl 10.0.0.7/index.html
10.0.0.27
[root@client ~]#curl 10.0.0.7/test.php
10.0.0.17