集群高可用之lvs
集群:
随着互联网的发展,大量的客户端的请求,服务器的负载越来越大,单台服务器的负载有限,会导致服务器响应客户端请求的时间越长,甚至产生拒绝服务的情况。目前网站是24小时不间断提供网络服务,仅采用单点服务器对外提供要求,如果出现单点故障,会导致整个网络的中断。集群环境的核心是负载均衡和高可用。
常见的负载均衡器
根据工作在的协议层划分可划分为:
- 四层负载均衡:根据请求报文中的目标地址和端口进行调度
- 七层负载均衡:根据请求报文的内容进行调度,这种调度属于「代理」的方式
根据软硬件划分:
- 硬件负载均衡:
- F5 的 BIG-IP
- Citrix 的 NetScaler
- 这类硬件负载均衡器通常能同时提供四层和七层负载均衡,但同时也价格不菲
- 软件负载均衡:
- TCP 层:LVS,HaProxy,Nginx
- 基于 HTTP 协议:Haproxy,Nginx,ATS(Apache Traffic Server),squid,varnish
- 基于 MySQL 协议:mysql-proxy
LVS
LVS 是一个工作在四层的负载均衡器,它的实现和 iptables/netfilter 类似,工作在内核空间的 TCP/IP 协议栈上,LVS 工作在 INPUT Hook Funtion 上,并在 INPUT 设置附加规则,一旦客户端请求的是集群服务,LVS 会强行修改请求报文,将报文发往 POSTROUTING,转发至后端的主机。
和 iptables/netfilter 类似,LVS 也是两段式的:
ipvsadm
:工作在用户空间,负责定义和管理集群服务的规则ipvs
:工作在内核中,在 2.4.23 之前,必须向内核打补丁,并重新编译内核。在 2.4.23 和 2.6 之后的版本,ipvs
直接内置在内核中。
LVS 集群的设备地址命名
- VIP:Virtual IP,LVS 面向用户请求的 IP 地址
- RIP:Real server IP,后端服务器用于和 LVS 通信的 IP 地址
- DIP:Director IP,LVS 用户和后端服务器通信的 IP 地址
- CIP:Client IP,客户端 IP 地址
lvs有三种模式:
1)基于NAT的lvs负载均衡
LVS-NAT 模型类似于 DNAT,工作机制与 DNAT 一样,当客户端请求的是集群服务时,LVS 修改请求报文的目标地址为 RIP,转发至后端的 RealServer,并修改后端响应报文的源地址为 VIP,响应至客户端。
在 LVS-NAT 模型下,Director 进出请求报文都经过 Director,因此 Director 的压力是比较大的。
LVS-NAT 的特性:
- 集群节点跟 Director 必须在同一个 IP 网络中
- RIP 通常是私有地址,仅用于各集群节点间的通信
- Director 位于 client 和 Realserver 之间,负责处理进出的所有报文
- Realserver 必须将网关指向 DIP
- 支持端口映射
- 较大规模应用场景中,Director 易成为系统瓶颈(bottleneck)
2)基于TUN的lvs负载均衡
和 DR 模型类似,Realserver 都配有不可见的 VIP,Realserver 的 RIP 是公网地址,且可能和 DIP 不再同一网络中。当请求到达 Director 后,Director 不修改请求报文的源 IP 和目标 IP 地址,而是使用 IP 隧道技术,使用 DIP 作为源 IP,RIP 作为目标 IP 再次封装此请求报文,转发至 RIP 的 Realserver 上,Realserver 解析报文后仍然使用 VIP 作为源地址响应客户端。
LVS-TUN 的特性:
- 集群节点和可以跨越 Internet
- RIP,DIP,VIP 都是公网地址
- Director 仅负责处理入站请求,响应报文由 Realserver 直接发往客户端
- Realserver 使用自己的网关而不是 Director
- Realserver 只能使用支持隧道功能的操作系统
- 不支持端口映射
3)基于DR的lvs负载均衡
DR 值 Direct Routing,直接路由,DR 模型中,Director 和 Realserver 处在同一网络中,对于 Director,VIP 用于接受客户端请求,DIP 用于和 Realserver 通信。对于 Realserver,每个 Realserver 都配有和 Director 相同的 VIP(此 VIP 隐藏,关闭对 ARP 请求的响应),仅用户响应客户端的请求,RIP 用于和 Director 通信。
当客户端请求集群服务时,请求报文发送至 Director 的 VIP(Realserver的 VIP 不会响应 ARP 请求),Director 将客户端报文的源和目标 MAC 地址进行重新封装,将报文转发至 Realserver,Realserver 接收转发的报文。此时报文的源 IP 和目标 IP 都没有被修改,因此 Realserver 接受到的请求报文的目标 IP 地址为本机配置的 VIP,它将使用自己的 VIP 直接响应客户端。
LVS-DR 模型中,客户端的响应报文不会经过 Director,因此 Director 的并发能力有很大提升。
LVS-DR 模型的特性:
DR 值 Direct Routing,直接路由,DR 模型中,Director 和 Realserver 处在同一网络中,对于 Director,VIP 用于接受客户端请求,DIP 用于和 Realserver 通信。对于 Realserver,每个 Realserver 都配有和 Director 相同的 VIP(此 VIP 隐藏,关闭对 ARP 请求的响应),仅用户响应客户端的请求,RIP 用于和 Director 通信。
当客户端请求集群服务时,请求报文发送至 Director 的 VIP(Realserver的 VIP 不会响应 ARP 请求),Director 将客户端报文的源和目标 MAC 地址进行重新封装,将报文转发至 Realserver,Realserver 接收转发的报文。此时报文的源 IP 和目标 IP 都没有被修改,因此 Realserver 接受到的请求报文的目标 IP 地址为本机配置的 VIP,它将使用自己的 VIP 直接响应客户端。
LVS-DR 模型中,客户端的响应报文不会经过 Director,因此 Director 的并发能力有很大提升。
LVS-DR 模型的特性:
- 保证前端路由器将目标地址为 VIP 的报文通过 ARP 解析后送往 Director。
- 静态绑定:在前端路由将 VIP 对应的目标 MAC 地址静态配置为Director VIP 接口的 MAC 地址。
- arptables:在各 Realserver 上,通过 arptables 规则拒绝其响应对 VIP 的 ARP 广播请求
- 修改内核参数:在 Realserver 上修改内核参数,并结合地址的配置方式实现拒绝响应对 VIP 的 ARP 广播请求
- 各RIP 必须与 DIP 在同一个物理网络中
- RS 的 RIP 可以使用私有地址,也可以使用公网地址,以方便配置
- Director 仅负责处理入站请求,响应报文由 Realserver 直接发往客户端
- Realserver 不能将网关指向 DIP,而直接使用前端网关
- 不支持端口映射
LVS调度算法:
当 LVS 接受到一个客户端对集群服务的请求后,它需要进行决策将请求调度至某一台后端主机进行响应。LVS 的调度算法共有 10 种,按类别可以分为动态和静态两种类型。
轮询调度:
rr 简单在各主机间轮流调度
加权轮询调度:
wrr 根据各主机的权重进行轮询
最小连接调度:
lc 根据 overhead = active*256 + inactive
计算服务器的负载状态,每次选择 overhead 最小的服务器
加权最小连接调度:
wlc LVS 根据 overhead = (active*256+inactive)/weight
来计算服务器负载,每次选择 overhead 最小的服务器,它是 LVS 的默认调度算法
基于局部性的最少连接:
lblc 基于本地的最少连接,相当于 dh + wlc,正常请求下使用 dh 算法进行调度,如果服务器超载,则使用 wlc 算法调度至其他服务器
带复制的基于局部性的最少连接:
lblcr 与 LBLC 不同的是 LVS 将请求 IP 映射至一个服务池中,使用 dh 算法调度请求至对应的服务池中,使用 lc 算法选择服务池中的节点,当服务池中的所有节点超载,使用 lc 算法从所有后端 Realserver 中选择一个添加至服务吃中。
目标地址散列调度
dh 将请求的目标地址进行哈希,将相同 IP 的请求发送至同一主机,dh 机制的目的是,当 Realserver 为透明代理缓存服务器时,提高缓存的命中率。
原地址散列调度
sh:source hash,源地址哈希,对客户端地址进行哈希计算,保存在 Director 的哈希表中,在一段时间内,同一个客户端 IP 地址的请求会被调度至相同的 Realserver。sh 算法的目的是实现 session affinity(会话绑定),但是它也在一定程度上损害了负载均衡的效果。如果集群本身有 session sharing 机制或者没有 session 信息,那么不需要使用 sh 算法
ipvsadm
ipvsadm 用于配置 LVS 的调度规则,管理集群服务和 Realserver
管理集群服务
添加:-A -t|u|f service-address [-s scheduler]
-t: TCP协议的集群
-u: UDP协议的集群
service-address: IP:PORT
-f: FWM: 防火墙标记
service-address: Mark Number
修改:-E
删除:-D -t|u|f service-address
例如: # ipvsadm -A -t 10.10.0.1:80 -s rr
管理集群服务中的 RS
添加:-a -t|u|f service-address -r server-address [-g|i|m] [-w weight]
-t|u|f service-address:事先定义好的某集群服务
-r server-address: 某RS的地址,在NAT模型中,可使用IP:PORT实现端口映射;
[-g|i|m]: LVS类型
-g: DR
-i: TUN
-m: NAT
[-w weight]: 定义服务器权重
修改:-e
删除:-d -t|u|f service-address -r server-address
例如:
# ipvsadm -a -t 10.10.0.2:80 -r 192.168.10.8 -m
# ipvsadm -a -t 10.10.0.3:80 -r 192.168.10.9 -m
查看规则
-L|-l
-n:数字格式显式主机地址和端口
--stats:统计数据
--rate: 速率
--timeout: 显示tcp、tcpfin和udp的会话超时时长
-c: 显示当前的ipvs连接状况
删除所有的集群服务:
-C:清空 ipvs规则
保存规则:
-S
比如:ipvsadm -S > /path/to/somefile
载入保存的规则
-R
比如:ipvsadm -R < /path/from/somefile
这里介绍下DR工作模式的负载均衡
LVS负载均衡调度器设置,修改配置文件
ROUTER: 外网接口(eno16) 124.126.147.169 内网接口(eno33) 192.168.0.253 lvs.example.com eno16(VIP) 124.126.147.168 eno33 192.168.0.254 web1.example.com eno16 192.168.0.1 lo:0(VIP) 124.126.147.168 web2.example.com eno16 192.168.0.2 lo:0(VIP) 124.126.147.168 vim /etc/sysconfig/network-scripts/ifcfg-eno16 DEVICE=eno16 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=124.126.147.168 DNS1=202.106.0.20 NETMASK=255.255.255.0 vim /etc/sysconfig/network-scripts/ifcfg-eno33 DEVICE=eno33 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=192.168.0.254 DNS1=202.106.0.20 NETMASK=255.255.255.0 systemctl restart network 安装ipvsadm管理工具,创建虚拟服务,添加真实服务器组,为虚拟服务器设置适当的调度算法 yum -y install ipvsadm ipvsadm -A -t 124.126.147.168:80 -s wrr ipvsadm -a -t 124.126.147.168:80 -r 192.168.0.1:80 -g -w 1 ipvsadm -a -t 124.126.147.168:80 -r 192.168.0.2:80 -g -w 2 -A:添加虚拟服务 -t:TCP协议 -r:真实IP(RIP) -g:DR模式 ipvsadm -Sn > /etc/sysconfig/ipvsadm 保存调度规则 firewall-cmd --permanent --add-port=80/tcp firewall-cmd --reload
真实的web服务器设置如下(测试将web页面内容设置不同的内容,真实的生产环境服务器都提供相同的页面内容)
注意:调度器和真实服务器都设置了vip,所以这里要求所有的真实服务器禁止对VIP地址的ARP响应,通过配置arp_ignore和arp_announce来实现
vim /etc/sysconfig/network-scripts/ifcfg-eno16 DEVICE=eno16 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=192.168.0.1 NETMASK=255.255.255.0 GATEWAY=192.168.0.253 vim /etc/sysconfig/network-scripts/ifcfg-lo:0 DEVICE=lo:0 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=124.126.147.168 NETMASK=255.255.255.255 GATEWAY=192.168.0.253 vim /etc/sysctl.conf net.ipv4.conf.eno16.arp_ignore = 1 net.ipv4.conf.eno16.arp_announce = 2 net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce =2 sysctl -p yum -y install httpd systemctl restart httpd systemctl restart network echo "192.168.0.1" > /var/www/html/index.html firewall-cmd --permanent --add-port=80/tcp firewall-cmd --reload vim /etc/sysconfig/network-scripts/ifcfg-eno16 DEVICE=eno16 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=192.168.0.2 NETMASK=255.255.255.0 GATEWAY=192.168.0.253 vim /etc/sysconfig/network-scripts/ifcfg-lo:0 DEVICE=lo:0 BOOTPROTO=statci ONBOOT=yes TYPE=Ethernet IPADDR=124.126.147.168 NETMASK=255.255.255.255 GATEWAY=192.168.0.253 vim /etc/sysctl.conf net.ipv4.conf.eno16.arp_ignore = 1 net.ipv4.conf.eno16.arp_announce = 2 net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce =2 sysctl -p yum -y install httpd systemctl restart httpd systemctl restart network echo "192.168.0.2" > /var/www/html/index.html firewall-cmd --permanent --add-port=80/tcp firewall-cmd --reload
路由器采用Linux作为软件路由来实现本例:
vim /etc/sysconfig/network-scripts/ifcfg-eno16 DEVICE=eno16 BOOTPROTO=static ONBOOT=yes TYPE=Ethernet IPADDR=192.168.0.253 NETMASK=255.255.255.0 vim /etc/sysconfig/network-scripts/ifcfg-eno33 DEVICE=eno33 BOOTPROTO=static ONBOOT=yes TYPE=Ethernet IPADDR=124.126.147.168 NETMASK=255.255.255.255 vim /etc/sysctl.conf net.ipv4.ip_forward = 1 sysctl -p systemctl restart network
客户端使用浏览器访问http://124.126.147.168,最终可以访问到真实服务器所提供的页面内容,由于lvs采用WRR(加权轮询)算法,不同的请求将被分配到不同的后端服务器上,但服务器的优先级是不同的,本例中由于所有的页面都不相同,所以访问后得到不同的页面内容。
arp_ignore用来定义网卡在响应外部ARP请求时的响应级别
0:默认值,在任何网络接口收到ARP请求后,如果本机的任意接口有该ip,则予以响应
1:只回答目标IP地址是来访网络接口本地地址的ARP查询请求
2 -只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内
arp_announce:对网络接口上,本地IP地址的发出的,ARP回应,作出相应级别的限制: 确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口
0 - (默认) 在任意网络接口(eth0,eth1,lo)上的任何本地地址
1 -尽量避免不在该网络接口子网段的本地地址做出arp回应. 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一.如果改来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.
2 - 对查询目标使用最适当的本地地址.在此模式下将忽略这个IP数据包的源地址并尝试选择与能与该地址通信的本地地址.首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址. 如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.
关于对arp_announce 理解的一点补充:
其实就是路由器的问题,因为路由器一般是动态学习ARP包的(一般动态配置DHCP的话),当内网的机器要发送一个到外部的ip包,那么它就会请求 路由器的Mac地址,发送一个arp请求,这个arp请求里面包括了自己的ip地址和Mac地址,而linux默认是使用ip的源ip地址作为arp里面 的源ip地址,而不是使用发送设备上面的 ,这样在lvs这样的架构下,所有发送包都是同一个VIP地址,那么arp请求就会包括VIP地址和设备 Mac,而路由器收到这个arp请求就会更新自己的arp缓存,这样就会造成ip欺骗了,VIP被抢夺,所以就会有问题。
arp缓存为什么会更新了,什么时候会更新呢,为了减少arp请求的次数,当主机接收到询问自己的arp请求的时候,就会把源ip和源Mac放入自 己的arp表里面,方便接下来的通讯。如果收到不是询问自己的包(arp是广播的,所有人都收到),就会丢掉,这样不会造成arp表里面无用数据太多导致 有用的记录被删除。