lvs

LB cluster

一个创业公司刚刚起步的时候一般都是单机在跑,但是随着公司规模的不断扩大,当前的服务器已经不能够满足实际需要的时候,就需要扩展(Scale),扩展的方式有两种:
Up,向上扩展,又称垂直扩展,即提升硬件水平
Out,向外扩展,横向扩展,提升服务器数量,即采用集群的方式提供服务

然而,受制于技术水平,向上扩展很快会受限,大部分正常的选择是向外扩展,
当服务其数量达到一定规模,形成一个集群时,就需要做一个负载均衡集群(LB cluster)(Load balance cluster),此时就需要一个director负责调度分发请求,让各个web服务器根据分配的weighted进行接收处理请求。

如果有对个Web服务器,就涉及到会话保持的问题,即http是无状态的,而应用程序有事却需要状态追踪,那么如何进行状态追踪呢?
session 会话保持的三种方法

  • session sticky 会话粘滞,同一用户始终调度到同一服务器,会损害负载均衡的效果,如果某服务器突然宕机,还会导致session丢失
  • session replication 会话共享,即将session传输到所有的服务器上,每个服务器中都保存所有的session,但是会大大web服务器的负载。
  • session server 搭建session服务器,将所有的session都放在专门的服务器上,所有的web服务器都要到session服务器上读取session,session可以采用redis、memcached等高性能存储解决方案。此时要考虑session server的高可用问题。

LB cluster集群的实现

LB cluster需要使用调度器实现调度功能
七层调度:工作在应用层,由于七层调度每调度一次都要使用一个套接字进行主机到主机之间通信,而主机的端口又是有限制的,所以七层调度调度能力低,支持并发量小于4w
四层调度:内核级调度,调度能力高

负载的分类
硬负载:F5的big ip,A10的A10
软负载:

  • 四层:LVS,Nginx(stream),HAProxy(mod tcp)…
  • 七层:Nginx,HAProxy,Envoy…

LVS

LVS,是四层负载均衡器,全称Linux Virtual Server
LVS有两部分组成
ipvsadm:用户空间的命令行工具,规则管理器,用于管理集群服务及相关的RealServer
ipvs:工作于内核空间的netfilter的INPUT钩子之上的框架,ipvs工作于INPUT链上,如果数据报文符合ipvsadm定义的规则,则ipvs会强行将请求转换到FORWARD链上,甚至直接转换到postrouting链上。
ipvs是直接被整合到内核中的一个模块,使用只需装载一下即可(modeprobe ip_vs).

LVS的算法

ipvs的调度算法(10种),根据其调度室是否考虑各RS当前负载状态,可分为静态方法和动态方法两种。
静态算法:仅根据算法本身和请求报文特征进行调度,注重起点公平

  • rr:round-robin,轮询,依次将请求调度到各RS,

  • wrr:weighted-round-robin,加权轮询,对每个RS分配权重,权重越大的承载能力越高,将请求按照权重的比例调度到各RS,wrr算法是短链接无状态的最好算法(http)。

  • sh:source ip hashing 源地址哈希,即对请求的源地址进行哈希计算,将计算结果对权重之和取模之后,将请求调度给排在取模之后的数值对应的RS上(RS从0开始计数),来自同一个客户端的请求每次都调度给同一个服务器,对需要会话追踪的连接,例如fpm集群,电商站点,应用程序服务器等情况下比较实用,因为sh能实现同一客户端始终绑定到同一real server上。

    sh算法的缺点
    如果RS宕机会造成session丢失
    增加RS服务器负载
    如果一个公网ip背后有多个私网主机进行访问,那么这些主机会被当成同一个client,这会对负载均衡的效果产生严重的负面影响。

  • dh:destination ip hashing 目标地址哈希,用在内网访问外网的情况,例如内网很多台主机,为了提高访问速度,对内网用户做集群,通过调度分别分发道不同的代理服务器上向外发送请求报文,此时对内网发出的请求报文做dh,就能保证对同一目标服务器的访问始终都通过同一代理服务器发出,此时代理服务器上的缓存使用率就更高。

动态算法:额外考虑后端个RS的当前的负载状态,注重结果公平

  • lc:least connection,最少连接算法,将请求调度给负载最低的RS,如果各RS的负载相同,则调度给最上面的服务器,计算RS负载的算法为:overhead=activeconns*256+inactiveconns;
  • wlc:weighted lc:加权最少连接算法,将请求调度给负载最低的RS,算法为:overhead=(activeconns+*256+inactiveconns)/weighted;wlc算法是lvs的默认算法。长连接的mysql连接的最好算法。
  • sed:shortest expect delay最短期望延迟,如果后端RS的负载相同时,将请求调度给权重最大的RS。算法为:overhead=((activeconns+1)*256+inactiveconns)/weighted
  • nq:never queue永不排队,将请求先按权重从大小依次分一个给各RS,然后将后面的请求再按照权重分给各RS。
  • lblc:locality-based LC,动态的DH算法;缺点:影响负载均衡的效果。
  • lblcr:LBLC with Replication,带复制功能的LBLC

LVS Type

LVS集群的四种类型

  • lvs-nat:修改请求报文的目标ip(和端口),多目标ip的DNAT;通过将请求报文中的目标地址和目标端口修改为某挑出的RS的rip和port实现转发

    rip和dip必须在同一个ip网络,且应该使用私网地址,RS的网关要指向dip
    请求报文和响应报文都必须经由director转发,director易于称为系统瓶颈
    支持端口映射,可修改请求报文的目标port
    vs必须是linux系统,rs可以使任意系统

  • lvs-dr:director routing,直接路由。通过为请求报文重新封装一个MAC首部进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的rip所在接口的MAC地址,源ip/port,以及目标ip/port均保持不变
    director和各RS都得配置实用VIP

    确保前端路由器将目标ip为vip的请求报文发往director,做法有以下几种:1.在前端网关做静态绑定;2.在RS上使用arptables;3.在RS上修改内核参数以限制arp通告及应答级别:

    arp_announce
    0:默认值,表示可使用本地任意接口上配置的任意地址进行响应;
    1: 仅在请求的目标IP配置在本地主机的接收到请求报文接口上时,才给予响应;

    arp_ignore
    0:默认值,把本机上的所有接口的所有信息向每个接口上的网络进行通告;
    1:尽量避免向非直接连接网络进行通告;
    2:必须避免向非本网络通告;

    RS的rip可以使用私网地址,也可以是公网地址;rip与dip在同一ip网络,rip的网关不能指向dip,以确保响应报文不会经由director

    RS跟director要在同一个物理网络

    请求报文要经由director,但是响应报文不能经由director,而是有RS直接发往client

    不支持端口映射

  • lvs-tun:转发方式:不修改请求报文的ip首部(源ip为cip,目标ip为vip),而是在源ip报文之外再封装一个ip首部(源ip是dip,目标ip是rip),将报文发往挑选出的目标RS,RS直接响应给客户端(源ip是vip,目标ip是cip);采用ip隧道的方式

    DIP,VIP,RIP都应该是公网地址
    RS的网关不能也不可能指向DIP
    请求报文要经由director,但是响应报文不能经由director
    不支持端口映射
    RS的OS得支持隧道功能

  • lvs-fullnat:通过同时修改请求报文的源ip地址和目标ip地址进行转发,此类型默认不支持
    CIP <–> DIP
    VIP <–> RIP

    VIP是公网地址,RIP和DIP是私网地址,且通常不在同一IP网段,因此,RIP的网关一般不会指向DIP
    RS收到的请求报文源地址是DIP,因此,只能响应给DIP;但是director还要将其发往client
    请求和响应报文都经由director
    支持端口映射

总结
lvs-nat, lvs-fullnat:请求和响应报文都经由Director;

  • lvs-nat:RIP的网关要指向DIP;
  • lvs-fullnat:RIP和DIP未必在同一IP网络,但要能通信;

lvs-dr, lvs-tun:请求报文要经由Director,但响应报文由RS直接发往Client;

  • lvs-dr:通过封装新的MAC首部实现,通过MAC网络转发;
  • lvs-tun:通过在原IP报文之外封装新的IP首部实现转发,支持远距离通信;

实验:LVS-nat模式验证

在主机上建立两个容器运行web服务,在宿主机上做ipvs规则,把宿主机当做director,测试nat模式的负载均衡效果。

1
2
3
4
5
6
7
8
[root@docker ~]#docker run --name web1 -d -v /data/volumes/web1:/usr/shsre/nginx/html/ nginx:1.15-alpine 
[root@docker ~]#docker run --name web2 -d -v /data/volumes/web2:/usr/share/nginx/html/ nginx:1.15-alpine
[root@docker ~]#echo web1 > /data/volums/web1/index.html
[root@docker ~]#echo web2 > data/volumes/web2/index.html
[root@docker ~]#ipvsadm -A -t 192.168.34.102:80(vip) -s mrr
[root@docker ~]#ipvsadm -a -t 192.168.34.102:80 -r 172.17.0.3(rip1) -m
[root@docker ~]#ipvsadm -a -t 192.168.34.102:80 -r 172.17.0.4(rip2) -m
[root@docker ~]#for i in `seq 10`;do curl 192.168.34.102;sleep .5 ;done #在客户端做访问测试,此时可以看到nat模式的工作效果

这里存在一个问题,就是docker会自动将FORWARD链的默认策略修改为DROP,这就可能导致调度存在问题,可以通过修改docker的unit文件,在每次docker启动后都自动把FORWARD链的默认策略改为ACCEPT。

1
2
3
$ vi /var/lib/systemcd/system/docker.service
[Service]
ExecStartPost=/usr/sbin/iptables -P FORWARD -j ACCEPT

实验:lvs-dr模式验证

本实验采用三台服务器演示分别为ansible(192.168.34.103)做director,apache(192.168.34.108)做RS1,docker(192.168.34.102)做RS2
在RS1和RS2上分别部署web服务
采用dr模式,需要在每台服务器都配置vip,这会造成IP地址冲突的现象,所以要在RS修改内核参数,隐藏vip,而只将director的vip公布出来。

配置RS1,RS2的web服务,这里为了体现负载均衡效果,分别采用各自主机名做站点内容。

1
2
3
4
5
6
[root@docker ~]#echo docker > /var/www/html/index.html
[root@docker ~]#curl 192.168.34.102
docker
[root@apache ~]#echo apache > /var/www/html/index.html
[root@apache ~]#curl 192.168.34.108
apache

配置director的vip

1
[root@ansible ~]#ifconfig ens33:0 192.168.34.123 netmask 255.255.255.255 broadcast 192.168.34.123 #现在director上配置vip,配置32位的子网掩码,并将广播只发给自己,防止干扰局域网通信。

采用脚本的方式修改RS的内核参数并配置RS的vip,并将该脚本在各RS上分别执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@docker scripts]#cat setlpara.sh 
#!/bin/bash
#
vip=192.168.34.123
mask=255.255.255.255
interface="lo:0"

case $1 in
start)
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce

ifconfig $interface $vip netmask $mask broadcast $vip up
route add -host $vip dev lo:0
;;
stop)
ifconfig $interface down
echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_announce
;;
*)
echo "Usage $(basename $0) start|stop"
exit 1
;;
esac

在director上做ipvs规则

1
2
3
[root@ansible yum.repos.d]#ipvsadm -A -t 192.168.34.123:80 -s wrr
[root@ansible yum.repos.d]#ipvsadm -a -t 192.168.34.123:80 -r 192.168.34.102 -g -w 2
[root@ansible yum.repos.d]#ipvsadm -a -t 192.168.34.123:80 -r 192.168.34.108 -g -w 1

之后在客户端测试,

1
2
3
4
5
6
7
8
9
10
11
[root@node03 ~]#for i in `seq 10` ;do curl 192.168.34.123;sleep .5;done
docker
docker
apache
docker
docker
apache
docker
docker
apache
docker

至此,dr模式的测试成功。

LVS多端口绑定

LVS可以利用防火墙标记实现多端口绑定的功能,将多个应用程序绑定到一起进行调度。
firewallMark为服务实现分类器,从而实现将多个服务归类为一个服务。
在各RS上通过虚拟主机创建,模板如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@docker scripts]#cd /etc/httpd/conf.d/
[root@docker conf.d]#vi vhost.conf
Listen 8080

<VirtualHost *:80>
ServerName docker.80.com
DocumentRoot "/data/vhost1"
<Directory "/data/vhost1">
Options None
AllowOverride None
Require all granted
</Directory>
</Virtualhost>
<VirtualHost *:8080>
ServerName docker.8080.com
DocumentRoot "/data/vhost2"
<Directory "/data/vhost2">
Options None
AllowOverride None
Require all granted
</Directory>
</Virtualhost>
[root@docker conf.d]#echo docker.80 > /data/vhost1/index.html
[root@docker conf.d]#echo docker.8080 > /data/vhost2/index.html

在director上做规则

1
2
3
4
[root@ansible ~]#iptables -t mangle -A PREROUTING -d 192.168.34.123 -p tcp -m multiport --dports 80,8080 -j MARK --set-mark 7 #对目标地址是192.168.34.123的80和8080端口的tcp请求做防火墙标记,标记号为7
[root@ansible yum.repos.d]#ipvsadm -A -f 7 -s wrr
[root@ansible yum.repos.d]#ipvsadm -a -f 7 -r 192.168.34.102 -g -w 1
[root@ansible yum.repos.d]#ipvsadm -a -f 7 -r 192.168.34.108 -g -w 2

此时在客户端请求,80和8080端口就可以被统一调度了。

lvs持久连接

持久连接能实现不论是用何种调度算法,在一段时间内,都能够实现将来自同一客户端的请求始终发往同一RS
Usage:

  • ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]]

port Affinity:
每端口持久PPC:每个端口对应定义为一个集群服务,每集群服务单独调度;
每防火墙标记持久PFWM:基于防火墙标记集群服务,可实现将多个端口上的应用统一调度,即所谓的port Affinity;
每客户端持久PCC:基于0端口定义集群服务,即将客户端对所有应用的请求统统调度至后端主机,必须定义为持久模式。

LVS保存及重载规则

保存规则
建议保存至/etc/sysconfig/ipvsadm

  • ipvsadm-save > /PATH/TO/IPVSADM_FILE
  • ipvsadm -S > /PATH/TO/IPVSADM_FILE

重载规则

  • ipvsadm-restore < /PATH/FROM/IPVSADM_FILE
  • ipvsadm -R < /PATH/FROM/IPVSADM_FILE