Linux集群之美
上QQ阅读APP看书,第一时间看更新

1.3.2 LVS的持久连接机制

LVS是L4层负载均衡器,它自身提供了保持连接机制。

LVS是利用配置文件里的persistence(单位为秒)设置来设定会话保持时间的,这个选项对于电子商务网站来说尤其有用:当用户远程用账号登录网站时,有了这个会话保持功能,就能把用户的请求转发给同一个应用服务器了。在这里,我们来做一个假设,假定现在有一个LVS环境使用LVS/DR转发模式,真实的Web服务器有2个,LVS负载均衡器不启用会话保持功能。当用户第一次访问的时候,他的访问请求被负载均衡器转给某个真实服务器,此时他会看到一个登录页面,第一次访问完毕;接着他在登录框里填写用户名和密码,然后提交;这时候问题可能就会出现了——登录不成功。因为没有会话保持,负载均衡器可能会把第2次的请求转发到其他的服务器上,这样浏览器又会提醒客户需要再次输入用户名及密码。所以这里需要通过LVS启用持久连接机制来实现会话保持功能。

下面做一个简单的实验来验证一下,实验的IP分配如表1-1所示。

表1-1 LVS会话实验的服务器IP分配表

系统为CentOS 7.6 x86_64,内核版本为3.10.0-957.21.3.el7.x86_64,双网卡,这里准备将VIP地址绑定在eth1网卡上面。

由于这里是最小化安装,所以先安装编译工具等,另外为了不影响实验结果,建议关闭iptables防火墙和SELinux,它们会直接影响实验结果。在后端的两台Web服务器上直接安装了HTTPD服务,并分别设定了它们不同的首页地址,以示区分。

要注意的是,IPVS是LVS的关键,因为LVS的IP负载均衡技术就是通过IPVS模块来实现的,IPVS是LVS集群系统的核心软件,而IPVS具体是由ipvsadm来实现的。我们首先用如下命令查看当前内核是否支持:


lsmod | grep ip_vs

结果发现是不支持的,其解决方法呢?

这时需要在LVS-MASTER机器上安装ipvsadm软件,这里采用yum安装的方式,命令如下:


yum -y install ipvsadm

安装成功以后我们可以输入ipvsadm命令验证,应该有如下显示:


IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
    -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

查看ipvsadm版本,命令如下:


ipvsadm –verision

结果显示如下:


ipvsadm v1.27 2008/5/15 (compiled with popt and IPVS v1.2.1)

我们可以看看是否有ip_vs模块,输入如下命令验证:


lsmod | grep ip_vs

显示结果如下:


ip_vs_wrr              12697  1 
ip_vs                 145497  3 ip_vs_wrr
nf_conntrack          137239  1 ip_vs
libcrc32c              12644  3 xfs,ip_vs,nf_conntrack

现在,编写并运行init.sh脚本,绑定VIP地址到LVS-MASTER上,并设定LVS工作模式等,脚本内容如下:


#!/bin/bash
VIP=192.168.100.188
RIP1=192.168.100.23
RIP2=192.168.100.24
. /etc/rc.d/init.d/functions

logger $0 called with $1
case "$1" in
start)
echo " Start LVS of DirectorServer"
        #这里将VIP地址绑定在eth1网卡上
        /sbin/ifconfig eth1:0 $VIP broadcast $VIP netmask 255.255.255.255 up
        /sbin/route add -host $VIP dev eth1:0
        echo "1" >/proc/sys/net/ipv4/ip_forward
        #清空原先的规则表 
        /sbin/ipvsadm -C
        #重设规则表
        /sbin/ipvsadm -A -t $VIP:80 -s wrr -p 150 
        #如果没有-p参数的话,访问VIP地址时会发现在后端的两台Web上有轮询切换
        /sbin/ipvsadm -a -t $VIP:80 -r $RIP1:80 -g
        /sbin/ipvsadm -a -t $VIP:80 -r $RIP2:80 -g
        #开始运行ipvsadm
        /sbin/ipvsadm
        ;;
stop)
        echo "close LVS Directorserver"
        echo "0" >/proc/sys/net/ipv4/ip_forward
        /sbin/ipvsadm -C
        /sbin/ifconfig eth1:0 down
        ;;
*)
    echo "Usage: $0 {start|stop}"
    exit 1
esac

给予脚本initial.sh执行权限,并执行它,命令如下:


./initial.sh start

脚本显示结果如下:


IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
    -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.100.188:80 wrr persistent 150
    -> 192.168.100.23:80            Route   1      0          0         
    -> 192.168.100.24:80            Route   1      0          0

ActiveConn表示活动连接数,也就是TCP连接状态的ESTABLISHED;InActConn表示其他非活动连接数,即所有的其他状态和TCP连接数。

最后,在后端的两台Web服务器上执行realserver.sh脚本,此脚本的作用为绑定VIP地址并设定ARP抑制,脚本realserver.sh的代码如下:


#!/bin/bash
VIP=192.168.100.188
. /etc/rc.d/init.d/functions
case "$1" in
start)
    ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP
    /sbin/route add -host $VIP dev lo:0  
    echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore  
    echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
    echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
    sysctl -p >/dev/null 2>&1
    echo "RealServer Start OK"
    ;;
stop) 
    ifconfig lo:0 down  
    route del $LVS_VIP >/dev/null 2>&1  
    echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore  
    echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce  
    echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore  
    echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce  
    echo "RealServer Stoped"  
    ;;  
*)  
    echo "Usage: $0 {start|stop}"  
    exit 1  
esac  
exit 0

分别在两台Web机器上执行脚本,命令如下:


./realserver.sh start

然后我们就可以通过访问VIP地址来访问后端真正提供HTTP的服务器了。

LVS持久性连接的特性如下:把同一个客户的请求信息记录到LVS的Hash表里,保存时间使用persistence_timeout(Keepalived配置文件)控制,单位为秒。persistence_granularity参数(ipvsadm里的-M参数)是配合persistence_timeout的,在某些情况特别有用,它的值是子网掩码,表示持久连接的粒度,默认是255.255.255.255,也就是单独的客户端IP,如果改成255.255.255.0就表示一个网段的都会被分配到同一台后端Web机器上。

一个连接创建后空闲时的超时时间分为如下3类:

·TCP的空闲超时时间。

·LVS收到客户端tcpfin的超时时间。

·UDP的超时时间。

可以用如下命令查看这些值:


ipvsadm -L --timeout

命令显示结果如下所示:


Timeout (tcp tcpfin udp): 900 120 300

我们用ipvsadm验证下,命令如下:


ipvsadm  -Lcn

结果如下:


IPVS connection entries
pro expire state       source             virtual            destination
TCP 01:51  FIN_WAIT    10.0.0.7:54914     10.0.0.18:80       10.0.0.14:80
TCP 00:35  FIN_WAIT    10.0.0.7:54866     10.0.0.18:80       10.0.0.14:80
TCP 01:51  NONE        10.0.0.7:0         10.0.0.18:80       10.0.0.14:80
TCP 01:52  FIN_WAIT    10.0.0.7:54915     10.0.0.18:80       10.0.0.14:80

如果用户配置了持久化时间persistence_timeout,在客户端的请求到达LB后,IPVS会在记录表里添加一条state为NONE的连接记录。该连接记录的源IP为客户端IP,端口为0,超时时间为上面所说的持久化时间persistence_timeout,会逐步减小。当NONE的超时时间减到0时,如果IPVS记录中还存在ESTABLISHED或FIN_WAIT状态的连接,则persistence_timeout的值会刷新为初始值。

在该NONE状态的连接记录存在的期间,同一客户端IP的消息都会被调度到同一个RS节点。(NONE状态的连接不是表示一个具体的连接,而是代表一个客户端IP的连接模板,源端口用0表示。在IPVS上会记录具体的连接状态,并显示具体的源端口)。

ESTABLISHED前面的超时时间就是tcp|tcpfin|udp中TCP的值。该值表示一条TCP连接记录的空闲释放时间。如果客户端和服务端建立了连接,则IPVS中会出现一条ESTABLISHED的记录。每当客户端和服务端的连接中有信息交互时,该超时时间都会刷新为初始值。如果连接处于空闲状态,即一直没有信息交互,则等到该值超时后,ESTABLISHED的记录会直接消失(在这种情况下IPVS记录不会进入FIN_WAIT),实际上TCP连接还是存在的,并没有中断,但是由于持久化时间到了,后续同一客户端(IP+Port)过来的请求会重新调度。所以长连接业务场景需要注意根据业务需要设置好这个TCP空闲连接的超时时间。

FIN_WAIT前面的超时时间就是tcp|tcpfin|udp中tcpfin的值。在IPVS记录的每一条连接中,如果客户端发起了FIN断连,则IPVS中记录的连接状态会从ESTABLISHED变为FIN_WAIT。该值超时后,FIN_WAIT状态的记录直接消失。

还有一个细节要注意,如果用户没有配置持久化时间persistence_timeout,那么在ipvsadm查询的记录里是不会生成NONE记录模板的,因为此时不需要持久化。但是ipvsadm记录中还是会生成ESTABLISHED记录的,后续同一客户端(IP+PORT)的请求都会调度给同一个服务器,直到该连接达到了TCP空闲连接超时时间后,ESTABLISHED记录消失,IPVS才会重新调度该客户端的请求。这个机制是必需的,不能算作持久化(持久化针对的是同一客户端IP,可以是不同端口)。因为TCP在传输的过程中可能会出现报文分片,如果IPVS把来自同一客户端(IP+PORT)的不同分片调度给了不同的服务器,那么服务器收到报文分片后无法重新组合报文。

参考文档如下:

http://www.linuxvirtualserver.org/docs/persistence.html

https://blog.csdn.net/yujin2010good/article/details/88732377