01 LVS负载均衡介绍

1、LVS基本概述

1.1 什么是LVS

LVS的英文全称是 Linux Virtual Server ,即 Linux 虚拟服务器。其实它是⼀种 Cluster 集群技术,主要是用来实现负载均衡功能的,将用户请求均匀的调度到不同的后端服务器上处理。

注意: LVS 是基于四层 目标IP:目标PORT 实现的负载均衡,例如:请求目标IP为LVS,目标端口是80,LVS则将请求调度到后端的集群节点处理。

官网:http://www.linuxvirtualserver.org/

注意:LVS不支持HTTPS、Rewrite、Header首部改写、限速、限流等操作,因此在四层后面还可能会有七层负载均衡。

1.2 为何需要lVS

  • 1、解决七层端口数不够问题,实现百万连接;

  • 2、解决七层负载均衡高可用问题;

1.3 LVS组成部分

  • ipvs :⼯作在内核空间,实现集群服务的”调度“,借鉴了 iptables 的实现方式

  • ipvsadm :⼯作在用户空间,负责为 ipvs 内核框架编写规则。定义谁是集群服务,谁是后端服务器,数据包如何调度,调度到哪个节点。

1.4 LVS相关名词

接下来我们需要了解LVS中的名词,比如: DS、RS、CIP、VIP、DIP、RIP ,通过下面的图来做了解其含义。

名称

详细名称

描述

DS

Director Server

目标服务器,即负载均衡器LVS

RS

Real Server

真实应用服务,即后端服务器

CIP

Client IP

客户端请求IP

VIP

Virtual IP

直接面向用户的IP地址

DIP

Director Server IP

用于与后端RIP通信的IP地址

RIP

Real Server IP

后端真实服务器的IP地址

2、LVS应用场景

LVS主要应用场景:

3、LVS常⻅模型

LVS 负载均衡模型有 NAT、DR、TUN、FULL-NAT ,较为常⻅的模型有 NAT、DR ,使用最为广泛的模型是 DR

3.1 NAT模型

NAT :通过修改请求报文的目标 IP 地址,然后根据算法挑选出某台 RS 进行转发。(请求进⼊负载均衡器 LVS 时做 DNAT ,后端返回数据报文出负载均衡时做 SNAT

3.2 DR模型

DR :通过修改请求报文的目标 MAC 地址,然后根据算法挑选出某台RS进行转发。(请求进⼊负载均衡器 LVS 时做 MAC 地址转换,后端返回数据报文不经过负载均衡,所以无需做转换

4、LVS集群命令介绍

ipvsadm 的用法大概分类如下两类

  • 管理集群服务(定义负载均衡配置)

  • 管理后端RS(定义负载均衡后端节点的增删改查)

ipvsadm - Linux Virtual Server administration
ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [--pe persistence_engine] [-b sched-flags]
ipvsadm -D -t|u|f service-address
ipvsadm -C
ipvsadm -R
ipvsadm -S [-n]
ipvsadm -a|e -t|u|f service-address -r server-address [options]
ipvsadm -d -t|u|f service-address -r server-address
ipvsadm -L|l [options]
ipvsadm -Z [-t|u|f service-address]
ipvsadm --set tcp tcpfin udp
ipvsadm --start-daemon state [--mcast-interface interface] [--syncid sid]
ipvsadm --stop-daemon state

4.1 集群管理参数

# COMMANDS-Cluster
    -A ,--add-service 		# 添加⼀个集群服务
    -E ,--edit-service 	# 修改已添加的集群服务
    -D ,--delete-service # 删除虚拟服务
    -C ,--clear 		# 清空集群所有规则
    -R ,--restore  # 从文件中恢复集群
    -S ,--save 		# 将集群信息报文至文件中
    -L|-l,--list 	# 列出当前集群信息
    -Z ,--zero 		# 清空集群计数器
    -n 							# 数字格式显示 ip 和 port,注意-n只能写在-L之后

4.2 节点管理参数

# COMMANDS-RS
    -a,--add-server 		# 表示要添加 RS 节点
    -e,--edit-server 	# 表示要修改 RS 节点
    -d,--delete-server # 表示要删除 RS 节点
    -t,service-address # 指定操作哪个节点地址与端口,host[:port],tcp协议
    -u,service-address # 指定操作哪个节点地址与端口,host[:port],udp协议
    -r,--real-server 	# 指定 RS 节点地址与端口
    -w,--weight 				# 指定 RS 节点的权重
    -m,--masquerading	# 指定 LVS ⼯作模型 ( NAT模型 )
    -g,--gatewaying	  # 指定 LVS ⼯作模型 ( DR模型 )
    -i,--ipip 				  # 指定 LVS ⼯作模型 ( tun模型 )
    -s,--scheduler     # 指定 LVS 调度策略,默认为wlc
    -p,--persistent    # 持久连接超时时间
    -f,--fwmark-service # 防⽕墙标记
    -c,--connection 		# 显示 ipvs 连接信息

4.3 配置实例

1、定义负载均衡的IP地址为1.1.1.1,端口为80,然后后端节点为 1.1.1.2,1.1.1.3

[root@lvs01 ~]# ipvsadm -A -t 1.1.1.1:80
[root@lvs01 ~]# ipvsadm -a -t 1.1.1.1:80 -r 1.1.1.2:80
[root@lvs01 ~]# ipvsadm -a -t 1.1.1.1:80 -r 1.1.1.3:80

2、检查规则

[root@lvs01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 1.1.1.1:80 wlc
 -> 1.1.1.2:80 Route 1 0 0 
 -> 1.1.1.3:80 Route 1 0 0

3、删除后端真实服务器

[root@lvs01 ~]# ipvsadm -d -t 1.1.1.1:80 -r 1.1.1.3:80

4、删除LVS集群

[root@lvs01 ~]# ipvsadm -D -t 1.1.1.1:80

02 LVS负载均衡NAT模型

1、LVS NAT 模型介绍

通过修改请求报文的 目标IP 地址,而后根据调度算法挑选出⼀台 RS 节点进行转发。

(请求进⼊负载均衡器 LVS 时做 DNAT ,后端返回数据出负载均衡时做 SNAT )

1.1 NAT 基本逻辑

1.2 NAT 底层原理

  • 客户端: 10.0.0.100(外网)

  • 路由器: 10.0.0.20(外网) 、 172.16.1.20(内网)

  • DS 节点: 172.16.1.3(DIP) 、 172.16.1.10(VIP)

  • RS 节点: 172.16.1.5 、 172.16.1.6

1.3 NAT 访问原理

  • 1、当用户请求到达 Director Server ,此时请求的数据报文会先到内核空间的 PREROUTING 链。 此时报文的 源IP为CIP , 目标IP为VIP

  • 2、 PREROUTING 检查发现数据包的目标 IP 是本机,将数据包送至 INPUT 链。

  • 3、 IPVS 比对数据包请求的服务是否为集群服务,若是,通过调度算法挑选⼀台后端 RS 服务器,并修改数据包的 目标IP 为 RS的IP ,然后将数据包发至 POSTROUTING 链。 此时报文的 源IP为CIP , 目标IP为RIP

  • 4、 POSTROUTING 链通过选路,将数据包通过 Director Server 的DIP 发送给 RS

  • 5、 RS 发现目标为自己的 IP ,则交给应用程序处理,然后构建响应报文发回给 Director Server 。 此时报文的 源IP为RIP , 目标IP为CIP

  • 6、 Director Server 在响应客户端前,会将源 IP 地址修改为 VIP地址,然后响应给客户端。此时报文的 源IP为VIP , 目标IP为CIP

1.4 NAT 特性总结

  • 1、RS 必须使用私网地址,并需要将网关指向 DS ;

  • 2、RIP 和 DIP 必须为同⼀网段内;

  • 3、NAT 模型支持端口映射;

  • 4、RS 可以使用任意操作系统。例如 Linux、Windows 等;

  • 5、请求和响应报文都要经过 DS ,高负载场景中, DS 易称为瓶颈。

2、LVS NAT 模型场景实战

2.1 NAT 架构规划

2.2 NAT Route 配置

使用Linux模拟路由器功能。

1、配置 eth0外 网卡

[root@route ~]# cat /etc/NetworkManager/system-connections/eth0.nmconnection
[connection]
id=eth0
type=ethernet
interface-name=eth0

[ethernet]

[ipv4]
address1=10.0.0.20/24
gateway=10.0.0.2 # 网关需要指向能上公网的IP
dns=223.5.5.5;
method=manual

# 重启网卡
[root@route ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

2、配置 eth1内 网卡

[root@route ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1
[ethernet]
[ipv4]
address1=172.16.1.20/24
method=manual

# 重启网卡
[root@route ~]# nmcli connection reload && nmcli connection down eth1 && nmcli connection up eth1

vim新写的配置文件不被nmcli管理,使用nmcli connection migrate查看目前的所有文件

# 将新配置文件进行重加载

nmcli connection migrate /etc/NetworkManager/system-connections/eth1.nmconnection

# 再次重启

nmcli c reload

nmcli c up eth1

3、在 Route 节点启用 FORWARD 转发功能,实现路由功能。

[root@route ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && sysctl -p

2.3 NAT RS节点配置

  • 1、将所有RS的eth0网卡关闭;

  • 2、将所有RS节点的eth1的网关指向到LVS的VIP地址上;

  • 3、在所有RS节点上部署Nginx,后期通过LVS进行调度测试;

1、关闭所有RS节点的eth0网卡

[root@rs01 ~]# nmcli connection down eth0

2、配置所有RS节点的网关指向到LVS服务器的VIP地址上(所有的RS节点都需要操作)

[root@rs01 ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1
[ethernet]
[ipv4]
address1=172.16.1.5/24
gateway=172.16.1.10 # 指向到LVS的VIP地址
dns=223.5.5.5;
method=manual

[root@proxy-node01 ~]# nmcli connection reload && nmcli connection down eth1 && nmcli connection up eth1

# 检查路由
[root@rs01 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.1.10 0.0.0.0 UG 100 0 0 eth1

3、在RS1节点上配置Nginx,然后测试访问

[root@rs01 ~]# yum install nginx -y
[root@rs01 ~]# vi /etc/nginx/conf.d/lvs.ops.net.conf
server {
	listen 80;
	server_name lvs.ops.net;
	root /opt;
	limit_rate 100k;

	location / {
		index index.html;
	}
}

[root@rs01 ~]# dd if=/dev/zero of=/opt/bigdata bs=100M count=1
[root@rs01 ~]# echo "Web Page RS-Node1" > /opt/index.html
[root@rs01 ~]# systemctl start nginx

# 本机测试访问
[root@rs01 ~]# curl -HHost:lvs.ops.net http://172.16.1.5
Web Page RS-Node1

4、在RS2节点上配置Nginx,然后测试访问

[root@rs02 ~]# yum install nginx -y
[root@rs02 ~]# cat /etc/nginx/conf.d/lvs.ops.net.conf
server {
	listen 80;
	server_name lvs.ops.net;
	root /opt;
	limit_rate 100k;

	location / {
		index index.html;
	}
}

[root@rs01 ~]# dd if=/dev/zero of=/opt/bigdata bs=100M count=1
[root@rs02 ~]# echo "Web Page RS-Node2" > /opt/index.html
[root@rs02 ~]# systemctl start nginx

# 本机测试访问
[root@rs02 ~]# curl -HHost:lvs.ops.net http://172.16.1.6
Web Page RS-Node2

2.4 NAT LVS节点配置

  • 1、关闭eth0外网网卡

  • 2、配置eth1网卡,将网关指向到路由器节点的内网IP;

  • 3、添加VIP地址网卡配置,将VIP绑定到 eth1:1 子接口上;

  • 4、开启 LVS节点的内核转发功能,不然 RS 节点返回的数据包经过LVS时会被丢弃。

  • 5、配置 LVS NAT 负载均衡

1、关闭eth0网卡

[root@lvs01 ~]# nmcli connection down eth0

2、配置eth1网卡,将网关指向到路由器节点的内网IP

[root@lvs01 ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1

[ethernet]

[ipv4]
address1=172.16.1.3/24
gateway=172.16.1.20 # 指向到路由器的IP
dns=223.5.5.5;
method=manual

3、新增 VIP 地址的网卡配置,将 VIP 绑定到 eth1:1 网卡上。在Rockylinux9中不能使用配置文件方式指定 eth1:1 这样的设备,但可以通过以下命令的方式来临时添加VIP地址

[root@lvs01 ~]# ip addr add 172.16.1.10/32 dev eth1 label eth1:1

# 查看VIP是否配置成功
[root@lvs01 ~]# ip addr s eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:fa:4b:81 brd ff:ff:ff:ff:ff:ff
    altname enp19s0
    altname ens224
    inet 172.16.1.3/24 brd 172.16.1.255 scope global noprefixroute eth1
       valid_lft forever preferred_lft forever
    inet 172.16.1.10/32 scope global eth1:1
       valid_lft forever preferred_lft forever
    inet6 fe80::3f2:1401:b0e9:8659/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

4、开启 LVS 节点的内核转发功能,不然 RS 节点返回的数据包经过 DS 节点会被丢弃。

[root@lvs01 ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && sysctl -p

5、配置LVS负载均衡

# 安装LVS
[root@lvs01 ~]# yum -y install ipvsadm 

# 定义 LVS 集群
[root@lvs01 ~]# ipvsadm -A -t 172.16.1.10:80 -s rr

# 添加 RS1、RS2集群节点
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:80 -r 172.16.1.5:80 -m
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:80 -r 172.16.1.6:80 -m

6、检查负载均衡配置

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.1.10:80 rr
  -> 172.16.1.5:80                Masq    1      0          0         
  -> 172.16.1.6:80                Masq    1      0          0

2.5 客户端访问测试

  • 1、关闭客户端eth1网卡;

  • 2、配置客户端的eth0网关指向到路由器;

  • 3、直接访问LVS的VIP地址;

  • 注意:这属于伪实现,因为在实际的场景中,客户端是不可能直接指向到对端服务器的路由器上;

1、关闭客户端节点 eth1 网卡

[root@Client ~]# nmcli connection down eth1

2、配置客户端节点 eth0 网络

[root@Client ~]# cat /etc/NetworkManager/system-connections/eth0.nmconnection
[connection]
id=eth0
type=ethernet
interface-name=eth0

[ethernet]

[ipv4]
address1=10.0.0.100/24
gateway=10.0.0.20 # 真实场景不可能将客户端网关指向企业的路由器上
dns=223.5.5.5;
method=manual

3、重启客户端网络

[root@Client ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

4、使用 Client 测试访问效果

[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
Web Page RS-Node2

[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
Web Page RS-Node1

2.6 真实场景访问测试

  • 1、删除客户端指向路由节点的网关

  • 2、配置路由节点,添加DNAT配置

  • 3、如果客户端需要上网,也可以添加⼀条SNAT(共享上网功能)

  • 4、检查客户端究竟是与谁建⽴的TCP三次握手

1、删除 Client 节点的网关配置

[root@client ~]# sed -i '/gateway/d' /etc/NetworkManager/system-connections/eth0.nmconnection
[root@client ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

# 查看路由表
[root@Client ~]# ip route 
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.100 metric 100

2、配置路由节点的DNAT以及SNAT

# DNAT (端口映射,虚拟环境使用)
[root@route ~]# iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 80 -j DNAT --to 172.16.1.10:80

#SNAT (让内部主机通过路由可以上网)
[root@route ~]# iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j SNAT --to 10.0.0.20

[root@web1 ~]# ping baidu.com
PING baidu.com (39.156.70.37) 56(84) 比特的数据。
来自 172.16.1.3 (172.16.1.3) icmp_seq=2 Redirect Host(新的下一跳: 172.16.1.20 (172.16.1.20))
来自 172.16.1.3 (172.16.1.3) icmp_seq=4 Redirect Host(新的下一跳: 172.16.1.20 (172.16.1.20))
来自 172.16.1.3 (172.16.1.3) icmp_seq=7 Redirect Host(新的下一跳: 172.16.1.20 (172.16.1.20))
来自 172.16.1.3 (172.16.1.3) icmp_seq=13 Redirect Host(新的下一跳: 172.16.1.20 (172.16.1.20))

3、再次使用客户端进行测试

# 真实情况下,客户端节点也是无法连接企业内部网络
[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
curl: (7) Couldn't connect to server

[root@Client ~]# curl -HHost:lvs.ops.net http://10.0.0.20
Web Page RS-Node2

[root@Client ~]# curl -HHost:lvs.ops.net http://10.0.0.20
Web Page RS-Node1

4、检查客户端是否是与 RS 节点建⽴的 TCP 连接(需要准备大文件持续下载,以便更好观测)

[root@rs01 ~]# netstat -nt
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State 
tcp 0 0 172.16.1.73:80 10.0.0.100:57426 ESTABLISHED


# windows cmd 查看
C:\Users\Administrator>netstat -ant | findstr "5541"
  TCP    10.0.0.128:5541        10.0.0.20:80           ESTABLISHED     InHost

03 LVS负载均衡DR模型

1、LVS DR模型详解

它通过修改请求报文的目标 MAC 地址,然后根据算法挑选出合适的 RS 节点,进行转发。

(请求进⼊ DS Server 时做 MAC 地址替换,后端返回数据报文时无需经过 DS Server 节点,直接返回给客户端即可。)

1.1 DR基础图解

1.2 DR底层实现

  • 客户端: 10.0.0.100(外网)

  • 路由器: 10.0.0.20(外网) 、 172.16.1.20(内网)

  • DS 节点: 172.16.1.3(DIP) 、 172.16.1.10(VIP)

  • RS 节点: 172.16.1.5 、 172.16.1.6

问1:路由器如何找到 VIP 以及 MAC 地址呢?

答:路由器通过 ARP 广播获取VMAC,然后封装CIP、CMAC、VIP、VMAC,然后通过交换机转发至目标主机。

问2: RS 处理请求直接返回给 CIP ,不经过 LVS ,那么 RS 如何将数据包回传给 CIP?

由于 CIP 请求的是 VIP,而响应是通过 RIP 响应给CIP,所以数据报文⼀定会被丢弃。

那么就需要在所有的 RS 的 lo 接口上配置 VIP 地址。这样就可以通过 RS上的VIP 响应给 CIP 。

问3:所有RS节点都配置VIP,那么路由器在广播的时候,岂不是所有的VIP都会响应?

方式1:在路由器上绑定 VIP 与 VMAC 的对应关系。(但可能没有操作权限,且较为麻烦。)

方式2:在所有 RS 节点上配置 ARP 抑制,简单来说就是路由器广播获取 VMAC 时,所有的 RS 都不应答,其次所有的 RS 都不对外宣布自己的 VIP。

1.3 DR访问流程

  • 1、当用户请求到达 DS节点 ,此时请求的数据报文会先到内核空间的 PREROUTING 链。 此时报文的 源IP为CIP , 目标IP为VIP 。

  • 2、PREROUTING 检查发现数据包的 目标IP 是本机,将数据包送至 INPUT 链。

  • 3、IPVS 比对数据包请求的服务是否为集群服务,是则将请求报文中的 源MAC 修改为 DMAC ,将 目标MAC 修改 RMAC ,然后将数据包通过 POSTROUTING 链发出。 此时的 源IP 和 目的IP 均未修改,仅将 源MAC 修改为 DMAC , 目标MAC 修改为 RMAC

  • 4、RS 拆解数据报文发现请求的 IP 地址是本机,则会接收该数据报文,而后构建响应报文向外发出,此时的 源IP 是 VIP , 目标IP 是 CIP

  • 5、响应报文最终送达至客户端

1.4 DR特性总结

  • 1、请求报文必须由 DS 节点转发,但响应报文必须不经过 DS 节点

  • 2、 RS 不能将网关指向 DS 节点的 DIP

  • 3、 DS 和 RS 节点必须位于同⼀物理网络中,但不⼀定必须在同⼀个网段内

  • 4、 DR 模型不支持地址转换,也不支持端口映射

  • 5、 RS 可以是常⻅的操作系统 Windows、Linux、MacOS

  • 6、 RS 在 lo 接口上配置 VIP

1.5 DR ARP参数

arp_ignore (控制系统在收到外部的 arp 请求时,是否应答。)

  • 0 默认值,将本机所有接口的所有信息像每个连接的网络进行通告。

  • 1 只应答本地主机访问网络接口( eth0-->lo ),才给予响应。

arp_announce (控制系统是否对外宣布自己的地址。)

  • 0 默认值,把本机所有接口的所有信息向每个接口的网络进行通告。

  • 1 "尽量避免" 将接口信息向非直接连接网络进行通告。

  • 2 "必须避免" 将接口信息向非本网络进行通告。

2、DR 模型场景实践

2.1 DR 架构规划

2.2 DR Route配置

1、配置 eth0外 网卡

[root@route ~]# cat /etc/NetworkManager/system-connections/eth0.nmconnection 
[connection]
id=eth0
type=ethernet
interface-name=eth0

[ethernet]

[ipv4]
address1=10.0.0.20/24
gateway=10.0.0.2 	  # 网关需要指向能上公网的IP
dns=223.5.5.5;
method=manual

# 重启网卡
[root@route ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

2、配置 eth1内 网卡

[root@route ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1

[ethernet]

[ipv4]
address1=172.16.1.20/24
method=manual

# 重启网卡
[root@route ~]# nmcli connection reload && nmcli connection down eth1 && nmcli connection up eth1

3、在 Route 节点启用 FORWARD 转发功能,实现路由功能

[root@route ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && sysctl -p

2.3 DR RS节点配置

  • 1、将所有RS的eth0网卡关闭;

  • 2、将所有RS节点的eth1的网关指向能出公网的路由器;

  • 3、在所有的RS节点上配置VIP;

  • 4、在所有的RS节点上配置ARP抑制;

  • 5、在所有RS节点上部署Nginx,后期通过LVS进行调度测试;

1、关闭所有RS节点的eth0网卡

[root@rs01 ~]# nmcli connection down eth0
[root@rs01 ~]# nmcli connection down eth0

2、配置所有RS节点的网关指向到LVS服务器的VIP地址上(所有的RS节点都需要操作)

[root@rs01 ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection 
[connection]
id=eth1
type=ethernet
interface-name=eth1

[ethernet]

[ipv4]
address1=172.16.1.5/24
gateway=172.16.1.20  # 指向到能出公网的路由器地址
dns=223.5.5.5;
method=manual

[root@rs01 ~]# nmcli connection reload && nmcli connection down eth1 && nmcli connection up eth1

# 检查路由
[root@rs01 ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.1.20     0.0.0.0         UG    100    0        0 eth1
172.16.1.0      0.0.0.0         255.255.255.0   U     100    0        0 eth1

3、在所有的RS节点的 lo:0 接口上添加VIP地址,在Rockylinux9中不能使用配置文件方式指定 lo:0 这样的设备,但可以通过以下命令的方式来临时添加VIP地址

[root@rs01 ~]# ip addr add 172.16.1.10/32 dev lo label lo:0
[root@rs02 ~]# ip addr add 172.16.1.10/32 dev lo label lo:0

# 检查IP地址
[root@rs01 ~]# ip addr 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group 
default qlen 1000
 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 inet 127.0.0.1/8 scope host lo
 valid_lft forever preferred_lft forever
 inet 172.16.1.10/32 scope global lo:0
 valid_lft forever preferred_lft forever

4、在所有的RS节点上配置ARP抑制规则,不对外宣告本机 VIP 地址,也不响应其他节点发起 ARP请求 本机的 VIP;

echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/default/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/default/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce

5、在RS1节点上配置Nginx,然后测试访问

[root@rs01 ~]# yum install nginx -y
[root@rs01 ~]# cat /etc/nginx/conf.d/lvs.ops.net.conf
server {
listen 80;
server_name lvs.ops.net;
root /opt;
limit_rate 100k;
location / {
 index index.html;
}
}

[root@rs01 ~]# dd if=/dev/zero of=/opt/bigdata bs=100M count=1
[root@rs01 ~]# echo "Web Page RS-Node1" > /opt/index.html
[root@rs01 ~]# systemctl start nginx

# 本机测试访问
[root@rs01 ~]# curl -HHost:lvs.ops.net http://172.16.1.5
Web Page RS-Node1

6、在RS2节点上配置Nginx,然后测试访问

[root@rs02 ~]# yum install nginx -y
[root@rs02 ~]# cat /etc/nginx/conf.d/lvs.ops.net.conf
server {
listen 80;
server_name lvs.ops.net;
root /opt;
 limit_rate 100k;
location / {
index index.html;
}
}

[root@rs01 ~]# dd if=/dev/zero of=/opt/bigdata bs=100M count=1
[root@rs02 ~]# echo "Web Page RS-Node2" > /opt/index.html
[root@rs02 ~]# systemctl start nginx

# 本机测试访问
[root@rs02 ~]# curl -HHost:lvs.ops.net http://172.16.1.6
Web Page RS-Node2

2.4 DR LVS节点配置

  • 1、关闭eth0外网网卡

  • 2、配置eth1网卡,将网关指向能出公网的路由器;

  • 3、添加VIP地址网卡配置,将VIP绑定到 eth1:1 子接口上;

  • 4、配置 LVS NAT 负载均衡为DR模型

1、关闭eth0网卡

[root@lvs01 ~]# nmcli connection down eth0

2、配置eth1网卡,将网关指向到路由器节点的内网IP

[root@lvs01 ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1

[ethernet]

[ipv4]
address1=172.16.1.3/24
gateway=172.16.1.20 # 指向到路由器的IP
dns=223.5.5.5;
method=manual

3、新增 VIP 地址的网卡配置,将 VIP 绑定到 eth1:1 网卡上。在Rockylinux9中不能使用配置文件方式指定 eth1:1 这样的设备,但可以通过以下命令的方式来临时添加VIP地址

[root@lvs01 ~]# ip addr add 172.16.1.10/32 dev eth1 label eth1:1

# 查看VIP是否配置成功
[root@lvs01 ~]# ip addr 
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state U
P group default qlen 1000
 link/ether 52:54:00:f7:76:38 brd ff:ff:ff:ff:ff:ff
 altname enp0s4
 altname ens4
 inet 172.16.1.3/24 brd 172.16.1.255 scope global noprefixroute eth1
 valid_lft forever preferred_lft forever
 inet 172.16.1.10/32 scope global eth1:1

4、配置LVS负载均衡为DR模型

# 定义 LVS 集群
[root@lvs01 ~]# ipvsadm -A -t 172.16.1.10:80 -s rr

# 添加 RS1、RS2集群节点
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:80 -r 172.16.1.5:80 -g
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:80 -r 172.16.1.6:80 -g

5、检查负载均衡配置

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.5:80 Route 1 0 0
 -> 172.16.1.6:80 Route 1 0 0

2.5 客户端访问测试

  • 1、关闭客户端eth1网卡;

  • 2、配置客户端的eth0网关指向到路由器;

  • 3、直接访问LVS的VIP地址;

  • 注意:这属于伪实现,因为在实际的场景中,客户端是不可能直接指向到对端服务器的网关上;

1、关闭客户端节点 eth1 网卡

[root@Client ~]# nmcli connection down eth1

2、配置客户端节点 eth0 网络

[root@Client ~]# cat /etc/NetworkManager/system-connections/eth0.nmconnection
[connection]
id=eth0
type=ethernet
interface-name=eth0

[ethernet]

[ipv4]
address1=10.0.0.100/24
gateway=10.0.0.20 	# 真实场景不可能将客户端网关指向企业的路由器上
dns=223.5.5.5;
method=manual

3、重启客户端网络

[root@Client ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

4、使用 Client 测试访问效果

[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
Web Page RS-Node2

[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
Web Page RS-Node1

2.6 真实场景访问测试

  • 1、删除客户端指向路由节点的网关;

  • 2、配置路由节点,添加DNAT配置;

  • 3、如果客户端需要上网,也可以添加⼀条SNAT(共享上网功能);

  • 4、检查客户端究竟是与谁建⽴的TCP三次握手;

1、删除 Client 节点的网关配置

[root@client ~]# sed -i '/gateway/d' /etc/NetworkManager/system-connections/eth0.nmconnection

[root@client ~]# nmcli connection reload && nmcli connection down eth0 && nmcli connection up eth0

# 查看路由表
[root@Client ~]# route -n
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0

2、配置路由节点的DNAT以及SNAT

# DNAT (端口映射,虚拟环境使用)
[root@route ~]# iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 80 -j DNAT --to 172.16.1.10:80

#SNAT (让内部主机通过路由可以上网)
[root@route ~]# iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j SNAT --to 10.0.0.20

3、再次使用客户端进行测试

# 真实情况下,客户端节点也是无法连接企业内部网络
[root@Client ~]# curl -HHost:lvs.ops.net http://172.16.1.10
curl: (7) Couldn't connect to server

[root@Client ~]# curl -HHost:lvs.ops.net http://10.0.0.20
Web Page RS-Node2

[root@Client ~]# curl -HHost:lvs.ops.net http://10.0.0.20
Web Page RS-Node1

4、检查客户端是否是与 RS 节点建⽴的 TCP 连接(需要准备大文件持续下载,以便更好观测);

[root@rs01 ~]# netstat -nt
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State 
 
tcp 0 0 172.16.1.10:80 10.0.0.100:53240 ESTABLISHED

3、Shell脚本配置LVS DR实践

在网络规划没有问题的情况下,通过脚本来实现 LVS DR 模型。(注意修改脚本中的 IP 地址)

所有的节点,LVS、RS的网关都是指向到路由器节点 (172.16.1.20)

3.1 DS配置脚本

[root@lvs-01 ~]# cat lvs_dr.sh
#!/usr/bin/bash
VIP=172.16.1.10
RS1=172.16.1.5
RS2=172.16.1.6
HTTP_PORT=80
HTTPS_PORT=443
SCHEDULER=rr
DEV=eth1
DEV_LABLE=eth1:1
case $1 in
 start)
 # 配置VIP
 ip addr add ${VIP}/32 dev ${DEV} label ${DEV_LABLE}
 
 # 配置LVS规则
 ipvsadm -A -t ${VIP}:${HTTP_PORT} -s ${SCHEDULER}
 ipvsadm -a -t ${VIP}:${HTTP_PORT} -r ${RS1} -g
 ipvsadm -a -t ${VIP}:${HTTP_PORT} -r ${RS2} -g
 # 配置LVS规则HTTPS
 ipvsadm -A -t ${VIP}:${HTTPS_PORT} -s ${SCHEDULER}
 ipvsadm -a -t ${VIP}:${HTTPS_PORT} -r ${RS1} -g
 ipvsadm -a -t ${VIP}:${HTTPS_PORT} -r ${RS2} -g
 ;;
stop)
 # 删除VIP
 ip addr del ${VIP}/32 dev ${DEV} label ${DEV_LABLE}
 
 # 清除IPVS规则
 ipvsadm -C
 ;;
*)
 echo "Usage: sh $0 { start | stop }"
;;
esac

3.2 RS配置脚本

[root@web01 ~]# cat lvs_rs.sh
#!/usr/bin/bash
VIP=172.16.1.10
DEV=lo
DEV_LABLE=lo:0
case $1 in
 start)
 # 配置ARP抑制
 echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
 echo "1" >/proc/sys/net/ipv4/conf/default/arp_ignore
 echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
 
 echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
 echo "2" >/proc/sys/net/ipv4/conf/default/arp_announce
 echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
 # 配置VIP
 ip addr add ${VIP}/32 dev ${DEV} label ${DEV_LABLE}
 ;;
 
 stop)
 # 取消ARP抑制规则
 echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
 echo "0" >/proc/sys/net/ipv4/conf/default/arp_ignore
 echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
 
 echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
 echo "0" >/proc/sys/net/ipv4/conf/default/arp_announce
 echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
 
 # 删除VIP
 ip addr del ${VIP}/32 dev ${DEV} label ${DEV_LABLE}
 ;;
 *)
 echo "Usage: sh $0 { start | stop }"
esac

04 LVS负载均衡高可用

1、LVS 高可用介绍

1.1 LVS目前面临的问题

尽管LVS提供了强大的负载均衡功能,但它在某些方面还是存在局限性:

  • 1、健康检查:LVS本身不提供健康检查。如果⼀个RS宕机,LVS会继续将流量调度到这个故障的RS节点上。

  • 2、单点故障:如果LVS节点本身发生故障,那么整个集群就无法正常对外提供服务。进行路由的。

  • 3、配置不持久:LVS的配置是临时的,如果服务器出现重启,之前所有的配置都会丢失,需要重新配置。

1.2 LVS高可用解决方案

Keepalived提供了强大的功能,来弥补LVS的不足:

  • 1、健康检查: Keepalived引⼊了健康检查机制,能够监测RS的状态并自动从负载均衡池中添加或移除故障节点,确保流量只会被路由到正常节点上。

  • 2、故障转移: 配置多个LVS节点并使用VRRP协议,Keepalived就可以在主LVS节点故障时自动切换到备用的LVS节点是,以此来实现高可用。

  • 3、配置持久化: 在Keepalived的配置文件,可以直接定义LVS负载均衡的规则以及健康检查机制等,并且这些配置在服务重启后,依然有效。

总结: Keepalived 就是为 LVS 而诞生的。

2、LVS高可用配置实践

2.1 Master节点配置

1、安装 Keepalived

[root@lvs01 ~]# yum install keepalived -y

2、配置Keepalived

[root@lb01 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
 router_id lb01
}
vrrp_instance VI_1 {
 state MASTER
 priority 200
 interface eth1
 virtual_router_id 50
 advert_int 1
 authentication {
 auth_type PASS
 auth_pass 1111
 }
 virtual_ipaddress {
 172.16.1.10
 }
}
# 配置集群地址访问的IP+Port
virtual_server 172.16.1.10 80 {
 # 健康检查的时间,单位:秒
 delay_loop 6
 # 配置负载均衡的算法
 lb_algo rr
 # 设置LVS的模式 NAT|TUN|DR
 lb_kind DR
 # 设置会话持久化的时间
 # persistence_timeout 30
 # 设置协议
 protocol TCP
 # 负载均衡后端的真实服务节点RS-1
 real_server 172.16.1.5 80 {
 # 权重配比设置为1
 weight 1
 # 设置健康检查
 TCP_CHECK {
 # 检测后端80端口
 connect_port 80
 # 超时时间
 connect_timeout 3
 # 重试次数2次
 nb_get_retry 2
 # 间隔时间3s
 delay_beefore_retry 3
 }
 }
 # 负载均衡后端的真实服务节点RS-2
 real_server 172.16.1.6 80 {
 # 权重配比设置为1
 weight 1
 # 设置健康检查
 TCP_CHECK {
 # 检测后端80端口
 connect_port 80
 # 超时时间
 connect_timeout 3
 # 重试次数2次
 nb_get_retry 2
 # 间隔时间3s
 delay_beefore_retry 3
 }
 }
}

[root@lvs01 ~]# systemctl enable --now keepalived

2.2 Backup节点配置

1、安装 Keepalived

[root@lvs02 ~]# yum install keepalived -y

2、配置Keepalived

[root@lvs-02 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
 router_id lb02
}
vrrp_instance VI_1 {
 state BACKUP
 priority 150
 interface eth1
 virtual_router_id 50
 advert_int 1
 authentication {
 auth_type PASS
 auth_pass 1111
 }
 virtual_ipaddress {
 172.16.1.10
 }
}
# 配置集群地址访问的IP+Port
virtual_server 172.16.1.10 80 {
 # 健康检查的时间,单位:秒
 delay_loop 6
 # 配置负载均衡的算法
 lb_algo wlc
 # 设置LVS的模式 NAT|TUN|DR
 lb_kind DR
 # 设置会话持久化的时间
 # persistence_timeout 30
 # 设置协议
 protocol TCP
 # 负载均衡后端的真实服务节点RS-1
 real_server 172.16.1.5 80 {
 # 权重配比设置为1
 weight 1
 # 设置健康检查
 TCP_CHECK {
 # 检测后端80端口
 connect_port 80
 # 超时时间
 connect_timeout 3
 # 重试次数2次
nb_get_retry 2
 # 间隔时间3s
 delay_beefore_retry 3
 }
 }
 # 负载均衡后端的真实服务节点RS-2
 real_server 172.16.1.6 80 {
 # 权重配比设置为1
 weight 1
 # 设置健康检查
 TCP_CHECK {
 # 检测后端80端口
 connect_port 80
 # 超时时间
 connect_timeout 3
 # 重试次数2次
 nb_get_retry 2
 # 间隔时间3s
 delay_beefore_retry 3
 }
 }
}

[root@lvs02 ~]# systemctl enable --now keepalived

3、LVS高可用架构测试

3.1 当前架构环境说明

当启动两台 LVS 节点的 Keepalived 软件后,会发现两台节点都有 LVS的规则,但仅在 Master 节点上有 VIP 地址;

  • 1、当有新的请求进⼊时,所有的请求都会发送至 Master 节点;

  • 2、当 Master 节点故障, VIP 地址会漂移到 Backup 节点;

  • 3、最后由 Backup 节点继续对外提供服务,以此来实现 LVS 的高可用;

1、检查 LVS-Master 节点

[root@lvs01 ~]# ip addr | grep 172.16.1.10
 inet 172.16.1.10/32 scope global eth1
 
[root@lvs-01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.5:80 Route 1 0 0
 -> 172.16.1.6:80 Route 1 0 0

2、检查 LVS-BACKUP 节点

[root@lvs02 ~]# ip addr | grep 172.16.1.10

[root@lb02 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.5:80 Route 1 0 0
 -> 172.16.1.6:80 Route 1 0 0

3.2 模拟RS节点故障

模拟 Real Server 故障(关闭⼀台 Nginx ), Keeplaived 检测后会自动将节点移除

1、关闭RS1节点的Nginx

[root@rs01 ~]# systemctl stop nginx

2、检查LVS规则是否发生变化

[root@lvs01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.6:80 Route 1 0 0 		#仅剩下6节点了

3、查看Keepalived是否输出相应的信息

[root@lvs01 ~]# systemctl status keepalived.service
lvs01.ops.net Keepalived_healthcheckers[25970]: TCP_CHECK on service [172.16.1.5]:tcp:80 failed after 1 retries.
lvs01.ops.net Keepalived_healthcheckers[25970]: Removing service [172.16.1.5]:tcp:80 from VS [172.16.1.10]:tcp:80

# 如果恢复nginx,则会自动加⼊集群
lvs01.ops.net Keepalived_healthcheckers[25970]: TCP connection to [172.16.1.73]:tcp:80 success.
lvs01.ops.net Keepalived_healthcheckers[25970]: Adding service [172.16.1.73]:tcp:80 to VS [172.16.1.10]:tcp:80

3.3 模拟LVS节点故障

模拟 DS 故障,将 Master 节点 Keepalived 软件关闭,达到故障效果;

1、 Master 节点关闭Keepalived,会发现 VIP 地址自动漂移到备节点,同时Master上的 lvs 规则也会被清空;

[root@lvs01 ~]# systemctl stop keepalived.service
[root@lvs01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn

2、检查Backup是否有VIP、以及IPVS规则

[root@lvs02 ~]# ip addr |grep 172.16.1.10
 inet 172.16.1.10/32 scope global eth1

[root@lvs02 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 wlc
 -> 172.16.1.5:80 Route 1 0 0
 -> 172.16.1.6:80 Route 1 0 0

3、使用客户端测试,验证VIP地址漂移后是否能正常对外提供服务

客户端测试,无任何异常

[root@client ~]# curl -HHost:lvs.ops.com http://10.0.0.200
Web Page RS-Node2

[root@client ~]# curl -HHost:lvs.ops.com http://10.0.0.200
Web Page RS-Node1

05 LVS负载均衡调度算法

1、LVS调度算法详解

LVS 根据后端服务器的负载,或其他的计算的标准,判断挑选哪台 RS 来进行请求处理。调度算法主要分为”静态调度算法“、”动态调度算法“。

  • 静态调度算法: RR、WRR、SH、DH

  • 动态调度算法: LC、WLC、SED、NQ、LBLC、LBLCR

2、LVS静态调度算法

静态:仅根据算法本身进行调度,不考虑后端实际负载情况(起点公平)

2.1 RR

RR:round robin 轮询调度算法,将每⼀次用户的请求,轮流分配给 Real Server 节点。

[root@lb01 ~]# ipvsadm -E -t 172.16.1.10:80 -s rr

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.7:80 Route 1 0 0
 -> 172.16.1.8:80 Route 1 0 0

2.2 WRR

WRR:weighted round robin 加权轮询调度算法,根据服务器的硬件情况、以及处理能⼒,为每台服务器分配不同的权值,使其能够接受相应权值的请求。

[root@lb01 ~]# ipvsadm -E -t 172.16.1.10:80 -s wrr

[root@lb01 ~]# ipvsadm -e -t 172.16.1.10:80 -r 172.16.1.7:80 -g -w 5

[root@lb01 ~]# ipvsadm -e -t 172.16.1.10:80 -r 172.16.1.8:80 -g -w 1

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 wrr
 -> 172.16.1.7:80 Route 5 0 0
 -> 172.16.1.8:80 Route 1 0 0

2.3 SH

SH:Source Hashing 源地址 hash 调度算法,将请求的源 IP 地址进行 Hash 运算,得到⼀个具体的数值,同时对后端服务器进行编号,按照运算结果将请求分发到对应编号的服务器上。

  • 1、可以实现不同来源 IP 的请求进行负载分发;

  • 2、同时还能实现相同来源 IP 的请求始终被派发至某⼀台特定的节点;

[root@lb01 ~]# ipvsadm -E -t 172.16.1.10:80 -s sh

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 sh        # 配置了Weight无效
 -> 172.16.1.7:80 Route 5 0 3
 -> 172.16.1.8:80 Route 1 0 1

2.4 DH

DH:destination hash 目标地址 hash 将客户端的请求,始终发往同⼀个RS 。

应用场景: LVS-cache-源站 ,始终调度到指定的 cache ,加速用户体验。

[root@lb01 ~]# ipvsadm -E -t 172.16.1.10:80 -s dh

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 dh
 -> 172.16.1.7:80 Route 5 0 0
 -> 172.16.1.8:80 Route 1 0 0

3、LVS动态调度算法

动态:根据算法及 RS 节点负载状态进行调度,较小的 RS 将被调度(保证结果公平)

3.1 LC

LC:Least-Connection 最少连接数调度算法,哪台 RS 连接数少就将请求调度至哪台 RS

算法: Overhead = ( Active * 256 + Inactive仅连接 ) ⼀个活动连接相当于256个非活动连接

3.2 WLC

WLC:Weighted Least-Connection 加权最小连接数(默认调度算法),在服务器性能差异较大的情况下,采用“加权最少链接”调度算法优化负载均衡性能,权值较高的 RS 节点,将承受更多的连接;负载均衡可以自动问询 RS 节点服务器的负载状态,通过算法计算当前连接数最少的节点,而后将新的请求调度至该节点。

算法: Overhead =( Active * 256 + Inactive )/Weight

3.3 SED

SED:Shortest Expected Delay 最短期望延迟尽可能让权重高的优先接收请求,不考虑非活动状态,把当前处于活动状态的数目+1,通过算法计算当前连接数最少的节点,而后将新的请求调度至该节点。

算法:在WLC基础上改进, Overhead = (ACTIVE+1)*256/Weight

3.3 NQ

NQ:Never Queue 永不排队/最少队列调度

  • 原理: SED 算法由于某台服务器的权重较小,比较空闲,甚至接收不到请求,而权重大的服务器会很忙,而 NQ 算法是说不管权重多大都会被分配到请求。简单来说,就是无需队列,如果有台 Real Server 的连接数为0会直接分配过去,后续采用 SED 算法

  • 算法: Overhead = (ACTIVE+1)*256/Weight

3.4 LBLC

LBLC:Locality-Based Least-Connection 动态目标地址 hash 调度算法,解决 DH 调度算法负载不均衡。

应用场景: LVS-cache-源站 ,此前 DH 算法始终调度到后端 Cache1 节点,会造成 Cache1 负载过高, LBLC 会根据负载均衡动态调度到后端其他 Cache 节点。

3.5 LBLCR

LBLCR: Locality-Based Least-Connection with Replication 带复制功能的LBLC算法,解决LBLC负载不均衡的问题

应用场景: LVS-cache-源站 ,此前 LBLC 算法始终调度到后端 Cache1 节点,会造成 Cache1 负载过高,会根据负载均衡动态调度到后端其他 Cache 节点,同时也会将缓存数据同步⼀份至 Cache1、Cache2 节点。

06 LVS负载均衡场景实践

1、LVS实现MySQL负载均衡

1.1 场景说明

使用LVS的DR模型,来对MySQL进行的实例进行负载均衡调度。

  • 1、准备两台MySQL服务器

  • 2、关闭eth0网关

  • 3、配置对应的eth1网卡:将网关指向出口的路由器,也就是 172.16.1.20

  • 4、安装MySQL服务,然后配置MySQL服务允许远程用户能访问

  • 5、为两台RS节点配置VIP地址,并且配置ARP抑制,可以执行 lvs_rs.sh 脚本

  • 6、配置LVS规则,定义⼀个3306的集群,然后将请求调度到内部数据库节点

  • 7、配置路由器端口映射,将请求公网地址的3306,映射到VIP地址的3306

1.2 地址规划

系统环境

路由器

LVS(2台)

RS(2台)

RockyLinux9

eth0:10.0.0.20

eth1:172.16.1.20

eth0: 关闭

eth1: 172.16.1.3

gateway: 172.16.1.20

eth0: 关闭

eth1: 172.16.1.9

gateway:172.16.1.20

RockyLinux9

eth0: 关闭

eth1: 172.16.1.4

gateway:172.16.1.20

eth0: 关闭

eth1: 172.16.1.22

gateway:172.16.1.20

1.3 安装MySQL

1、安装MySQL

[root@web03 ~]# yum install mysql mysql-server -y

2、配置⼀个可以远程访问的账户

mysql> create user 'lvs'@'%' identified by 'ops.net';
mysql> grant all privileges on *.* to 'lvs'@'%';
mysql> flush privileges;

1.4 准备基础环境

1、关闭eth0网卡

[root@nfs ~]# nmcli connection down eth0

2、配置eth1网卡,将网关指向到路由器上

[root@nfs ~]# sed -i '/^address/a gateway=172.16.1.20' /etc/NetworkManager/system-connections/eth1.nmconnection

[root@nfs ~]# nmcli connection reload 

[root@nfs ~]# nmcli connection down eth1 && nmcli connection up eth1

[root@nfs ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.1.20 0.0.0.0 UG 100 0 0 eth1

3、配置VIP、以及ARP的抑制,可以通过 lvs_rs.sh 脚本来实现

[root@nfs ~]# sh lvs_rs.sh start
 
# 检查是否有VIP
[root@nfs ~]# ifconfig lo:0
lo:0: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
 inet 172.16.1.10 netmask 255.255.255.255
 loop txqueuelen 1000 (Local Loopback)

1.5 配置LVS调度规则

方式1:通过命令行定义负载均衡规则

[root@lvs01 ~]# ipvsadm -A -t 172.16.1.10:3306 -s rr
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:3306 -r 172.16.1.22:3306 -g
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:3306 -r 172.16.1.9:3306 -g

# 检查
[root@lvs01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn 
TCP 172.16.1.10:3306 rr
 -> 172.16.1.9:3306 Route 1 0 0 
 -> 172.16.1.22:3306 Route 1 0 0

方式2:通过 keepalived 配置文件来定义负载均衡规则

# 配置集群地址访问的IP+Port
virtual_server 172.16.1.10 3306 {
 delay_loop 6
 lb_algo rr
 lb_kind DR
 protocol TCP
 # 负载均衡后端的真实服务节点RS-1
 real_server 172.16.1.9 3306 {
 weight 1
 TCP_CHECK {
 connect_port 3306
 connect_timeout 3
 nb_get_retry 2
 delay_beefore_retry 3
 }
 }
 # 负载均衡后端的真实服务节点RS-2
 real_server 172.16.1.22 3306 {
 weight 1
 TCP_CHECK {
 connect_port 3306
 connect_timeout 3
 nb_get_retry 2
 delay_beefore_retry 3
 }
 }
}

1.6 路由器配置端口映射

1、将所有请求路由器公网的3306,调度到VIP的3306端口上

iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 3306 -j DNAT --to 172.16.1.10:3306

2、检查iptables的NAT规则

[root@route ~]# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination 
DNAT tcp -- 0.0.0.0/0 10.0.0.20 tcp dpt:3306 
to:172.16.1.10:3306

1.7 客户端测试

1、客户端需要安装mysql命令

[root@clinet ~]# dnf -y install mariadb

2、连接对应的MySQL测试

[root@clinet ~]# mysql -h 10.0.0.20 -ulvs -pops.net
mysql> select @@hostname;
+-----------------+
| @@hostname |
+-----------------+
| web03.ops.net |
+-----------------+
1 row in set (0.01 sec)
[root@clinet ~]# mysql -h 10.0.0.20 -ulvs -pops.net
mysql> select @@hostname;
+---------------+
| @@hostname |
+---------------+
| nfs.ops.net |
+---------------+
1 row in set (0.00 sec)

2、配置四七层负载均衡

1、准备两台LVS节点,实现主备模式;

2、准备两台七层负载均衡,实现集群模式;

3、准备两台web节点,提供真实的站点内容;

4、关闭eth0的网络接口,配置eth1的网关指向到 172.16.1.20

2.1 配置七层负载均衡

1、关闭eth0的网络接口

2、配置eth1的网关指向到 172.16.1.20

3、配置VIP、以及ARP的抑制,可以执行 lvs_rs.sh 脚本

4、定义⼀个域名 lvs.ops.net ,然后代理到后端集群 172.16.1.7、172.16.1.8

1、关闭eth0

2、配置eth1的网关

[root@proxy1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 
TYPE="Ethernet"
BOOTPROTO=none
DEFROUTE="yes"
NAME="eth0"
DEVICE="eth0"
ONBOOT="yes"
NETMASK=255.255.255.0
IPADDR=172.16.1.11
GATEWAY=172.16.1.20

3、配置VIP、ARP抑制,7层负载均衡主机配置

ip addr add 172.16.1.10/32 dev lo label lo:1

echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/default/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/default/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce

4、配置七层负载均衡(两台负载均衡都需要配置)

[root@proxy02 ~]# vim /etc/nginx/conf.d/proxy_lvs.ops.net.conf
upstream lvs {
 server 172.16.1.7:80;
 server 172.16.1.8:80;
}
server {
 listen 80;
 server_name lvs.ops.net;
 location / {
 proxy_pass http://lvs;
 proxy_set_header Host $http_host;
 }
}

2.2 配置应用服务器

1、应用服务器不需要配置网关指向,只要七层负载均衡能访问即可;

[root@web01 ~]# vim /etc/nginx/conf.d/lvs.ops.net.conf
server {
 listen 80;
 server_name lvs.ops.net;
 root /opt;
 location / {
 index index.html;
 }
}

2、重启Nginx

[root@web01 ~]# nginx -t
[root@web01 ~]# systemctl reload nginx

2.3 配置四层负载均衡

[root@lvs01 ~]# ipvsadm -A -t 172.16.1.100:80 -s rr
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.100:80 -r 172.16.1.5:80 -g
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.100:80 -r 172.16.1.6:80 -g
[root@lvs01 ~]# ipvsadm -L -n 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.100:80 rr
 -> 172.16.1.5:80 Route 1 0 0 
 -> 172.16.1.6:80 Route 1 0 0

2.4 路由器配置端口映射

[root@route ~]# iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 80 -j DNAT --to 172.16.1.100:80

2.5 配置LVS支持HTTPS

1、配置路由器的映射

[root@route ~]# iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 443 -j DNAT --to 172.16.1.10:443

2、配置LVS调度规则

[root@lvs01 ~]# ipvsadm -A -t 172.16.1.10:443 -s rr
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:443 -r 172.16.1.5:443
[root@lvs01 ~]# ipvsadm -a -t 172.16.1.10:443 -r 172.16.1.6:443
[root@lvs01 ~]# ipvsadm -L -n 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.10:80 rr
 -> 172.16.1.5:80 Route 1 1 1 
 -> 172.16.1.6:80 Route 1 1 1 
TCP 172.16.1.10:443 rr
 -> 172.16.1.5:443 Route 1 0 0
 -> 172.16.1.6:443 Route 1 0 0 

3、七层负载均衡主机

[root@proxy1 ~]# cat /etc/nginx/conf.d/proxy_lvs.ops.net.conf 
upstream lvs {
 server 172.16.1.5:80;
 server 172.16.1.6:80;
}
server {
    listen 443 ssl;
    server_name www.zhouhaoit.com;
    #ssl on;
    ssl_certificate ssl_key/www.zhouhaoit.com.pem;
    ssl_certificate_key ssl_key/www.zhouhaoit.com.key;
    location / {
    proxy_pass http://lvs;
    proxy_set_header Host $http_host;
  }
}

[root@proxy1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 
TYPE="Ethernet"
BOOTPROTO=none
DEFROUTE="yes"
NAME="eth0"
DEVICE="eth0"
ONBOOT="yes"
NETMASK=255.255.255.0
IPADDR=172.16.1.11
GATEWAY=172.16.1.20

4、web01和web02节点

[root@web01 ~]# cat /etc/nginx/conf.d/lvs.ops.net.conf 
server {
	listen 80;
	server_name www.zhouhaoit.com;
	root /opt;
	limit_rate 100k;

	location / {
		index index.html;
	}
}

[root@web01 ~]# cat /etc/NetworkManager/system-connections/eth1.nmconnection
[connection]
id=eth1
type=ethernet
interface-name=eth1
[ethernet]
[ipv4]
address1=172.16.1.5/24
#gateway=172.16.1.20
dns=223.5.5.5;
method=manual

2.6 Rocky9部署PHP7.4.33

部署参考:https://rpms.remirepo.net/wizard/

(如果第一条执行不了则)
cat > /etc/yum.repos.d/crb.repo << EOF
[crb]
name=Rocky Linux \$releasever - CRB
baseurl=https://dl.rockylinux.org/\$contentdir/\$releasever/CRB/\$basearch/os/
# 备用地址(若主地址不通,自动切换)
mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=\$basearch&repo=CRB-\$releasever
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
enabled=1
# 允许解析变量
metadata_expire=6h
EOFcat > /etc/yum.repos.d/crb.repo << EOF
[crb]
name=Rocky Linux \$releasever - CRB
baseurl=https://dl.rockylinux.org/\$contentdir/\$releasever/CRB/\$basearch/os/
# 备用地址(若主地址不通,自动切换)
mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=\$basearch&repo=CRB-\$releasever
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
enabled=1
# 允许解析变量
metadata_expire=6h
EOF



dnf config-manager --set-enabled crb
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm -y
dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm -y
dnf module install php:remi-7.4 -y
useradd www
sed -i '/^user/c user = www' /etc/php-fpm.d/www.conf
sed -i '/^group/c group = www' /etc/php-fpm.d/www.conf
systemctl enable --now php-fpm
mv /etc/nginx/default.d/php.conf /opt/
mv /etc/nginx/conf.d/php-fpm.conf /opt/

cat /etc/nginx/conf.d/lvs.ops.net.conf 
server {
	listen 80;
	server_name www.zhouhaoit.com;
	root /opt;
	limit_rate 100k;

	location ~ \.php$ {
		root /opt;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi_params;
	}
}

grep ^listen /etc/php-fpm.d/www.conf 
listen = 127.0.0.1:9000

cat /opt/info.php 
<?php
	phpinfo();
?>

dnf -y install php-mysqli

systemctl restart php-fpm nginx

cat /opt/mysqli.php
<?php
        $servername = "172.16.1.5";
        $username = "lvs";
        $password = "ops.net";

        // 创建连接
        $conn = mysqli_connect($servername,$username, $password);

        // 检测连接
        if (!$conn) {
                die("Connection failed: " .
        mysqli_connect_error());
        }
        echo "php连接MySQL数据库成功";
?>

dnf -y install php-mysqli

php /opt/mysqli.php 
php连接MySQL数据库成功

dnf -y install unzip

unzip wordpress-6.8.3.zip

chown -R nginx.nginx /opt/wordpress

cat /etc/nginx/conf.d/lvs.ops.net.conf 
server {
	listen 80;
	server_name www.zhouhaoit.com;
	root /opt/wordpress;
 	index index.php;

	location ~ \.php$ {
		index index.php;
		root /opt/wordpress;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi_params;
	}
}

systemctl restart php-fpm nginx

head -2 /opt/wordpress/wp-admin/setup-config.php
<?php
#添加下面一行
$_SERVER['HTTPS'] = 'on';

mysql> create database wordpress;

head -2 /opt/wordpress/wp-login.php 
<?php
#添加下面一行
$_SERVER['HTTPS'] = 'on';

head -2 /opt/wordpress/wp-admin/index.php
<?php
#添加下面一行
$_SERVER['HTTPS'] = 'on';

7层负载均衡

[root@proxy1 ~]# cat /etc/nginx/conf.d/proxy_lvs.ops.net.conf 
upstream lvs {
 server 172.16.1.5:80;
# server 172.16.1.6:80;
}
server {
    #listen 80;
    listen 443 ssl;
    server_name www.zhouhaoit.com;
    #ssl on;
    ssl_certificate ssl_key/www.zhouhaoit.com.pem;
    ssl_certificate_key ssl_key/www.zhouhaoit.com.key;
    location / {
    proxy_pass http://lvs;
    proxy_set_header Host $http_host;
    root /opt/wordpress;
  }

#    location ~* \.(jpg|jpeg|png|gif|ico)$ {
#        proxy_pass http://lvs;
#        proxy_set_header Host $host;
#        proxy_set_header X-Real-IP $remote_addr;
#        proxy_pass_header Content-Type;
#    }
}

#server {
#    listen 80;
#    server_name www.zhouhaoit.com;
#    return 302 https://$server_name$request_uri;
#}

[root@proxy1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 172.16.1.10/32 scope global lo:1
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:a2:01:02 brd ff:ff:ff:ff:ff:ff
    inet 172.16.1.11/24 brd 172.16.1.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fea2:102/64 scope link 
       valid_lft forever preferred_lft forever


echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/default/arp_ignore
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/default/arp_announce
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce

4层负载均衡

[root@lb01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.1.10:443 rr
  -> 172.16.1.11:443              Route   1      1          0

[root@lb01 ~]# sysctl -p
net.ipv4.ip_forward = 1

路由器主机

[root@route ~]# iptables -t nat -A PREROUTING -d 10.0.0.20 -p tcp --dport 443 -j DNAT --to 172.16.1.10:443

[root@route ~]# sysctl -p
net.ipv4.ip_forward = 1