一、LVS

1.1 基础知识

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

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

image-20211025150101155

名称简写 详细名称 描述
DS Director Server 目标服务器, 即负载均衡器LVS
RS Real Server 真实应用服务器, 即后端服务器
CIP Client IP 客户端请求IP
VIP Virtual IP 虚拟IP, 直接面向用户的IP地址, 通常为公网IP
DIP Director Server IP 用于与后端RIP通讯的IP地址
RIP Real Server IP 后端真实服务器端IP地址

1.2 LVS常见模型

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

1.2.1 NAT模式

NAT模型原理: 通过修改请求报文目标IP地址, 然后根据算法挑出某台RS进行转发.

请求进入负载均衡器LVS时叫做DNAT, 后端返回数据报文出负载均衡器LVS时叫做SNAT

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

NAT模型特性

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

2、 RIP 和 DIP 必须为同一网段内。

3、 NAT 模型支持端口映射。

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

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

1.2.2 DR模式

DR模型原理: 通过修改请求报文目标MAC地址, 然后根据算法挑出某台RS进行转发

请求进入负载均衡器LVS时叫做MAC地址转换, 后端返回数据报文不经过负载均衡, 所有无需做转换

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、由于 DS 和 RS 在同一个网络中,所以是通过二层来传输。 POSTROUTING 链检 查 目标MAC 为 RIP的MAC 地址,那么此时数据包将通过 DIP 发送 RS 节点

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

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

DR 模型特性

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

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

3、 DS 和 RS 节点必须位于同一物理网络中

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

5、 RS 可以是常见的操作系统 Windows、Linux、MacOS 6、 RS 在 lo 接口上配置 VIP

DR模式配置

集群规划

服务 网络接口 IP地址 网关
DS eth1 172.16.1.3 172.16.1.1
eth1:1 172.16.1.100
RS1 eth1 172.16.1.5 172.16.1.1
lo:0 172.16.1.100
RS2 eth1 172.16.1.6 172.16.1.1
lo:0 172.16.1.100
VIP 172.16.1.100

RealServer添加VIP地址, 需要做arp抑制(简单来说就是路由器广播VMAC时所有RS不应答, 所有RS都不对外宣称自己的VIP),配置如下两个参数

  • arp_ignore: 忽略外部arp获取本机VIP地址的应答
    • 0: 默认值; 将本机所有接口的所有信息像每个连接的网络进行通告
    • 1: 只应答本地主机访问网络接口(eth0–>lo), 才给予应答
  • arp_annouce: 不主动对外宣告本机VIP地址
    • 0: 默认值; 将本机所有接口的所有信息像每个连接的网络进行通告
    • 1: “尽量避免”将接口信息向非直接连接网络进行通告
    • 2: “必须避免”将接口信息向非本地网络进行通告

RS服务器

sysctl.conf配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Arp config for lvs-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

或者
cat > /etc/sysctl.cof << 'EOF'
# Arp config for lvs-VIP
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
EOF
sysctl -p

网卡配置

1
2
3
4
5
6
7
8
9
cat > /etc/sysconfig/network-scripts/ifcfg-lo:0 << EOF
DEVICE=lo:0
IPADDR=172.16.1.100
NETMASK=255.255.0.0
ONBOOT=yes
NAME=loopback
EOF

ifdown lo:0 && ifup lo:0

DS服务器

sysctl.conf配置

1
2
# 启用FORWARD转发功能,实现路由功能
net.ipv4.ip_forward = 1

网卡配置

1
2
3
4
5
6
7
8
9
10
11
cat > /etc/sysconfig/network-scripts/ifcfg-eth1:1 << EOF
TYPE=Ethernet
BOOTPROTO=none
DEFROUTE=yes
NAME=eth1:1
DEVICE=eth1:1
ONBOOT=yes
IPADDR=172.16.1.100
PREFIX=24
EOF
ifdown eth1:1 && ifup eth1:1

ipvsadm添加规则

1
2
3
4
5
6
7
8
9
10
11
12
# 清除规则
ipvsadm -C

# 定义集群
ipvsadm -A -t 172.16.1.100:80 -s rr

# 添加 RS集群节点,采用DR模式
ipvsadm -a -t 172.16.1.100:80 -r 172.16.1.5:80 -g
ipvsadm -a -t 172.16.1.100:80 -r 172.16.1.6:80 -g

# 查看集群状态信息
ipvsadm -L -n

172.16.1.100为VIP地址

1.3 LVS持久化连接

DS节点修改集群配置

1
2
3
4
5
ipvsadm -E -t 172.16.1.100:80 -p 30

# 参数
-E 修改集群
-p 持久连接保持秒数

1.4 LVS集群命令

  • ipvsadm用法大概分为两类
    • 管理集群服务(定义负载均衡配置)
    • 管理后端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
ipvsadm - Linux Virtual Server administration
# 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之后

# 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 连接信息

二、Keepalived

2.1 Keepalived基础

Keepalived特性

1
2
3
4
5
keepalived软基是C语言编写的一个开源软件项目, 其本质是一个路由软件
keepalived基于ipvs功能进行二次整合实现负载均衡功能
keepalived基于VRRP协议进行二次整合实现高可用功能
keepalived借助于大量的功能脚本实现高质量的状态检查功能
keepalived框架可以单独使用, 也可以和其它软件进行整合使用

官方文档

https://www.keepalived.org/doc/

软件架构

image-20211125153929361

2.2 Keepalived + LVS 高可用

集群规划

服务 网络接口 IP地址
DS1 eth0 172.16.1.5
DS2 eth0 172.16.1.6
RS1 eth0 172.16.1.7
lo:0 172.16.1.100
RS2 eth0 172.16.1.8
lo:0 172.16.1.100
VIP 172.16.1.100

2.2.1 DS节点配置

keepalived配置

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
global_defs {
router_id lb01
}
vrrp_instance VI_1 {
state MASTER
priority 100
interface eth1
virtual_router_id 50
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
172.16.1.3 label eth1:0
}
}
# 配置集群地址访问的IP+Port
virtual_server 172.16.1.100 80 {
# 健康检查的时间,单位:秒
delay_loop 6
# 配置负载均衡的算法 wlc|nq|lblc|lblcr
lb_algo wlc
# 设置LVS的模式 NAT|TUN|DR
lb_kind DR
# 设置会话持久化的时间
perssisstence_timeout 5
# 设置协议
protocol TCP

# 负载均衡后端的真实服务节点RS-1
real_server 172.16.1.7 80 {
# 权重配比设置
weight -30
# 设置健康检查
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.8 80 {
# 权重配比设置
weight -30
# 设置健康检查
TCP_CHECK {
# 检测后端80端口
connect_port 80
# 超时时间
connect_timeout 3
# 重试次数2次
nb_get_retry 2
# 间隔时间3s
delay_beefore_retry 3
}
}
}

关于priority和weight

1
2
3
4
5
6
7
priority: 为默认权重值,一般master要大于back,权重值还和weight有关,当检测失败时候,priority会和weight相加或者相减,得到一个新的权重值
weight: 绝对值要大于两台priority的差值

vrrp_script中weight:
如果weight值为正: 切换机制为,当mater检测失败,master的priority值不变,back的权重值为priority+weight,master的权重小于back的权重,进行切换

如果weight值为负: 切换机制为,当master检测失败,master的权重值为priority-weight的值,back的权重值不变,master的权重值小于back,进行切换

主从参数:

参数 master节点 backup节点
router_id lb-01 lb-02
state MASTER BACKUP
priority 100 90

sysctl.conf配置

1
2
# 启用FORWARD转发功能,实现路由功能
net.ipv4.ip_forward = 1

2.2.2 RS节点配置

sysctl.conf配置

1
2
3
4
5
6
7
8
9
10
11
cat > /etc/sysctl.cof << 'EOF'
# Arp config for lvs-VIP
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
EOF
sysctl -p

网卡配置

1
2
3
4
5
6
7
8
9
cat > /etc/sysconfig/network-scripts/ifcfg-lo:0 << EOF
DEVICE=lo:0
IPADDR=172.16.1.100
NETMASK=255.255.0.0
ONBOOT=yes
NAME=loopback
EOF

ifdown lo:0 && ifup lo:0

2.3 Keepalived + NGINX 高可用

2.3.1 nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat >> /etc/nginx/nginx.conf << "EOF"
# 这里通过端口配置的是四层负载,也可以通过域名方式,配置七层负载
stream {
log_format main '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';
access_log /var/log/nginx/upstream-access.log main;

upstream www {
ip_hash;
server 192.168.2.101:8001;
server 192.168.2.102:8001;
}

server {
listen 8001;
proxy_pass www;
}
}
EOF

2.3.2 Keepalived配置

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
30
31
32
33
34
cat > /etc/keepalived/keepalived.conf << "EOF"
global_defs {
notification_email {
www.localhost.com
}
notification_email_from www.localhost.com
router_id master
}

vrrp_script check_http {
script "</dev/tcp/127.0.0.1/8001" #修改为自己需要监听的端口,理论上可以监听远程端口
interval 2 #检查脚本的频率,单位(秒)
weight -30 #端口检查失败,优先级减少30,weight的绝对值要大于两台priority的差值
}

vrrp_instance VI_1 {
state MASTER
interface ens33 # 修改为实际网卡名
virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的
priority 100 # 优先级,备服务器设置 90
advert_int 1 # 指定VRRP 心跳包通告间隔时间,默认1秒
authentication {
auth_type PASS
auth_pass 123456
}
# VIP
virtual_ipaddress {
192.168.2.100/24 label ens33:0
}
track_script {
check_http
}
}
EOF

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
30
31
32
33
34
35
cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
notification_email {
www.localhost.com
}
notification_email_from www.localhost.com
router_id backup
}

vrrp_script check_http {
script "</dev/tcp/127.0.0.1/8001" #修改为自己需要监听的端口,理论上可以监听远程端口
interval 2 #检查脚本的频率,单位(秒)
weight -30 #端口检查失败,优先级减少30,weight的绝对值要大于两台priority的差值
}

vrrp_instance VI_1 {
state BACKUP
interface ens33 # 修改为实际网卡名
virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的
priority 90 # 优先级,备服务器设置 90
advert_int 1 # 指定VRRP 心跳包通告间隔时间,默认1秒
authentication {
auth_type PASS
auth_pass 123456
}
# VIP
virtual_ipaddress {
192.168.2.100/24 label ens33:0
}
track_script {
check_http
}
}
EOF

两个重要的值: priority和weight

1
2
3
4
5
6
7
priority: 为默认权重值,一般master要大于back,权重值还和weight有关,当检测失败时候,priority会和weight相加或者相减,得到一个新的权重值
weight: 绝对值要大于两台priority的差值

vrrp_script中weight:
如果weight值为正: 切换机制为,当mater检测失败,master的priority值不变,back的权重值为priority+weight,master的权重小于back的权重,进行切换

如果weight值为负: 切换机制为,当master检测失败,master的权重值为priority-weight的值,back的权重值不变,master的权重值小于back,进行切换

2.3.3 防火墙配置

开启防火墙端口112, 协议vrrp

1
2
iptables -A INPUT -p 112 -j ACCEPT
iptables -A INPUT -p vrrp -jACCEPT //这里不开启vrrp协议的端口,会造成backup服务器检测不到master服务器的是否正常而自动启动backup的应用服务

2.4 Keepalived检测机制

状态检测

  • 通过脚本实现
  • 主要针对本机服务本身(比如Keepalived服务, NGINX负载均衡服务等)
  • 常用于Keepalived+NGINX

VRRP script配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 定义NGINX服务检测脚本
vrrp_script check_nginx {
script "/bin/bash /data/scripts/check_nginx.sh" # cheaper than pidof
interval 2 # check every 2 seconds
# weight -2 # default prio: -4 if KO
# fall 2 # require 2 failures for KO
# rise 2 # require 2 successes for OK
}

# 使用
vrrp_instance VI_1 {
...
track_script {
check_nginx
}
}
注意:
检测命令的<SCRIPT_NAME>是VRRP专用的, 一定要保证在VRRP场景下是唯一的
script属性要求尽量使用脚本格式, 不包含特殊字符和表达是的简单命令也可以, 如果命令里包含特殊字符, 就不能执行

模版文件: keepalived.conf.vrrp.localcheck

脚本示例

1
2
3
4
5
6
cat /data/scripts/check_nginx.sh
#!/bin/bash
nginx=`netstat -tnlp | grep 80 | grep nginx | wc -l`
if [ "$nginx" -eq 0 ];then
weight -30
fi

健康检测

  • 通过默认检测方式检测
  • 主要针对后端服务(如web服务)
  • 适用于Keepalived+LVS

模版文件: keepalived.conf.HTTP_GET.port keepalived.conf.status_code等

主机检测属性(常用)

1
2
3
4
5
6
weight			设定真是主机的权重, 默认是1
HTTP_GET 以HTTP方式来检查后端主机
TCP_CHECK 以TCP方式来检查后端主机
SMTP_CHECK 以SMTP方式来检查后端主机
DNS_CHECK 以DNS方式来检查后端主机
MISC_CHECK 以MISC方式来检查后端主机

2.5 Keepalived双主模式

做两个vrrp_instance, 交叉主备