IPVS在内核中实现了传输层负载均衡,是一个L4的交换机。IPVS在一群真实服务器的前面,运行一个LB角色的主机,该主机面向客户端,提供了单一IP地址的虚拟服务。
在Director上,LVS钩子在netfilter框架中的位置,不管是来程、回程报文,均从左侧进入、右侧出去。对于DR/TUN模式,回程报文不经过Director:
只有fullNAT或者NAT才支持端口映射,也就是说VIP和RIP使用不同的端口。
fullNAT
不是标准内核的一部分。
来程报文: ClientIP: ClientPort - VIP:VPORT ⇨
IPVS Director
⇨
DirectorIP
:
DirectorPort
-
RIP
:
RPORT
回程报文:RIP: RPORT - DirectorIP: DirectorPort ⇨ IPVS Director ⇨
VIP
:
VPORT
-
ClientIP
:
ClientPort
和NAT模式相比,IPVS Director在进行DNAT的同时,还进行SNAT。这样获得以下优势:
客户端可以和Director、真实服务器在同一个局域网内
真实服务器
看不到客户端的真实IP
地址。只能由Director通过TCP Options携带真实IP,但是TCP Option只有40字节,可能客户端已经占用导致空间不足
来程报文: ClientIP: ClientPort - VIP:VPORT ⇨
IPVS Director
⇨ ClientIP: ClientPort -
RIP
:
RPORT
回程报文:RIP: RPORT - ClientIP: ClientPort ⇨ IPVS Director ⇨
VIP
:
VPORT
- ClientIP: ClientPort
基于NAT的虚拟服务器,当LB具有两个网络接口时:
一个接口分配面向外部的IP地址
另一个接口分配私有的、面向内部的IP地址
LB从外部接口接受流量,然后经过NAT,转发给内部网络中的真实服务器。
该模式的缺陷:
LB是性能瓶颈,它需要转发两个方向的流量
RS上必须进行适当的路由
,确保针对ClientIP的路由转发给IPVS Director处理
客户端必须和RS不在同一网段
。这个限制可以通过配置解决:
在Director上关闭ICMP重定向:
Tunneling
在此模式下,LB通过IP隧道将请求发送给真实服务器。
好处是可扩容,LB将请求转发给真实服务器后,
真实服务器直接将响应发送给客户端,不再需要经过LB
。由于大部分情况下都是请求包小、应答包大,这种模式下LB的性能压力较小。
该模式的缺陷:对网络结构依赖很大,
真实服务器必须支持IP隧道协议
。
Direct Routing
用户请求LB上的VIP,LB将请求直接转发(通过修改L2封包的方式,L3保持不变)给真实服务器。
好处是可扩容,而且没有Tunneling模式的封包解包开销。
真实服务器要直接应答客户端,则应答包的源IP必须是VIP。这就导致LB和所有真实服务器都需要共享VIP+MAC的组合。不经过合理配置,则真实服务器可能直接接收到请求,绕过LB。
该模式的缺陷:
RS和LVS的VIP,必须使用
相同端口
RS和LVS不能在同一机器上
,否则该RS访问VIP不经过LVS的负载均衡,而是直接访问自己
RS和LVS必须位于
同一VLAN
或局域网
负载均衡算法
简单的轮询算法,均等地对待每一台服务器,而不管服务器上实际的连接数和系统负载。
带权重的轮询算法,可以保证处理能力强的服务器处理更多的访问流量?调度器可以自动问询真实服务器的负载情况,并动态地调整其权值。
IPVS表存储了所有活动的连接,基于此信息可以将请求发送给具有
最少已建立连接
的RS。如果集群系统的
真实服务器具有相近的系统性能,采用"最小连接"调度算法可以较好地均衡负载
。
考虑权重的最少连接算法,具有较高权值的服务器将承受较大比例的活动连接负载。
通常用于缓存集群,将来自同一个目的地址的请求分配给同一台RS,如果RS满载则将这个请求分配给连接数最小的RS。
lblcr
根据请求的目标IP地址找出该目标IP地址对应的服务器组,按“最小连接”原则从该服务器组中选出一台服务器,若
服务器没有
超载, 将请求发送到该服务器
若服务器超载;则按“最小连接”原则从整个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器
当该服务器组有一段时间没有增减成员,将
最忙的服务器从服务器组中删除
源地址哈希调度以源地址为关键字查找一个静态hash表来获得需要的RS
目的地址哈希调度以目的地址为关键字查找一个静态hash表来获得需要的RS
如果存在空闲服务器,则请求转发给它;否则,按seq算法
最短期望延迟(Shortest Expected Delay)。期望延迟公式: (Ci + 1) / Ui ,其中Ci为服务器i的当前连接数,Ui为服务器i的固定服务速率(权重)
ipvsadm命令
管理Linux虚拟服务器的底层命令行工具,可以创建、修改、查看位于Linux内核中的虚拟服务器表
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
# 添加或修改虚拟服务
ipvsadm
-
A
|
E
virtual
-
service
[
-
s
scheduler
]
[
-
p
[
timeout
]
]
[
-
M
netmask
]
[
-
b
sched
-
flags
]
# virtual-service可以是:
# -t, --tcp-service service-address
# -u, --udp-service service-address
# 示例
ipvsadm
-
A
-
t
10.0.10.100
:
80
-
s
rr
-
p
600
# 删除虚拟服务
ipvsadm
-
D
virtual
-
service
# 清空虚拟服务表
ipvsadm
-
C
# 从标准输入还原虚拟服务表
ipvsadm
-
R
# 将虚拟服务表导出到标准输出
ipvsadm
-
S
[
-
n
]
# 为虚拟服务添加、编辑一个真实服务器
# 封包转发方式:
# -g --gatewaying 直接路由
# -i, --ipip 隧道(IPIP封装)
# -m, --masquerading NAT
ipvsadm
-
a
|
e
virtual
-
service
-
r
server
-
address
[
-
g
|
i
|
m
]
[
-
w
weight
]
[
-
x
upper
]
[
-
y
lower
]
# 示例
ipvsadm
-
a
-
t
10.0.10.100
:
80
-
r
10.1.0.10
:
80
-
m
# 为虚拟服务删除一个真实服务器
ipvsadm
-
d
virtual
-
service
-
r
server
-
address
# 示例
ipvsadm
-
d
-
t
10.0.10.100
:
80
-
r
10.1.0.10
:
80
# 列出虚拟服务
ipvsadm
-
L
|
l
[
virtual
-
service
]
[
options
]
# 清零包、字节、速率计数器
ipvsadm
-
Z
[
virtual
-
service
]
# 设置IPVS连接的超时值,三个值,分别用于TCP会话、接收到FIN后的TCP会话、UDP包
ipvsadm
--
set
tcp
tcpfin
udp
# 启动连接同步守护进程,state可以为master或backup。连接同步守护进程在内核中运行
# 主守护进程周期性的组播连接的变更,备份守护进程则接收组播消息并创建对应的连接。一旦主宕机,则从具有几乎全部的连接,不会出现已创建连接损坏的情况
ipvsadm
--
start
-
daemon
state
[
--
mcast
-
interface
interface
]
[
--
syncid
syncid
]
# 停止连接同步守护进程
ipvsadm
--
stop
-
daemon
state
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建TCP虚拟服务207.175.44.110:80,使用轮询调度
ipvsadm
-
A
-
t
207.175.44.110
:
80
-
s
rr
# 添加两台RS
ipvsadm
-
a
-
t
207.175.44.110
:
80
-
r
192.168.10.1
:
80
-
m
ipvsadm
-
a
-
t
207.175.44.110
:
80
-
r
192.168.10.2
:
80
-
m
# 打印连接列表,可以看到IPVS建立的连接的状态
ipvsadm
-
lc
# IPVS connection entries
# pro expire state source virtual destination
# TCP 01:19 FIN_WAIT zircon:33568 k8s:6443 neon:6443
# TCP 119:57 ESTABLISHED zircon:33570 k8s:6443 xenon:6443
# TCP 01:19 FIN_WAIT zircon:33569 k8s:6443 radon:6443
Keepalived提供负载均衡和高可用的框架。
负载均衡基于IP虚拟服务器(IPVS)内核模块,支持L4负载均衡。Keepalived提供了一组健康检查器,可以动态、自适应的管理上游服务器池。
高可用基于虚拟路由冗余协议(VRRP)。Keepalived实现了一系列钩子,和VRRP状态机进行底层/高层交互。
出于健壮性的考虑,Keepalived被拆分为三个进程:
一个最小化的父进程,负责监控子进程
两个子进程,分别负责VRRP、上游服务健康检查
通过包管理器安装:
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
# 提示此虚拟服务器是一个FWMARK
virtual_server
(
@
IP
PORT
)
|
(
fwmark
num
)
{
# 健康检查周期,秒
delay_loop
num
# 负载均衡算法(调度算法)
lb_algo
rr
|
wrr
|
lc
|
wlc
|
sh
|
dh
|
lblc
# IPVS模式
lb_kind
NAT
|
DR
|
TUN
(
nat
_
mask
@
IP
)
# 持久化连接,即会话亲和,让一段时间内同一客户端的连接(短)都发送到同一个RS
# 持久化连接的超时
persistence_timeout
num
# 持久化连接的粒度掩码
persistence
_
granularity
@
IP
# 用于HTTP/SSL_GET的虚拟主机名
virtualhost
string
# 协议类型
protocol
TCP
|
UDP
# 如果所有RS宕机,将下面的服务器加入池中
sorry_server
@
IP
PORT
# 定义一个真实服务器(RS)
real_server
@
IP
PORT
{
# 负载均衡权重
weight
num
# 通过TCP进行健康检查
TCP_CHECK
{
connect_port
num
connect_timeout
num
}
}
real_server
@
IP
PORT
{
weight
num
# 通过脚本进行健康检查
MISC_CHECK
{
misc_path
/
path_to_script
/
script
.
sh
(
or
misc
_
path
“
/
path_to_script
/
script
.
sh
<
arg_list
>
”
)
}
}
}
real_server
@
IP
PORT
{
weight
num
# 通过HTTP/HTTPS进行健康检查
HTTP_GET
|
SSL_GET
{
# 可以指定多个url块
url
{
# URL路径
path
alphanum
# 摘要信息
digest
alphanum
}
# 端口
connect_port
num
# 超时
connect_timeout
num
# 重试次数
retry
num
# 重试延迟
delay_before_retry
num
}
}
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
65
66
67
68
69
70
71
72
73
74
# 组
vrrp_sync_group
string
{
group
{
string
string
}
# 可以为脚本传递参数,整体用引号包围
notify_master
/
path_to_script
/
script_master
.
sh
notify_backup
/
path_to_script
/
script_backup
.
sh
notify_fault
/
path_to_script
/
script_fault
.
sh
}
# 默认情况下,Keepavelid只能对网络故障、Keepalived自身故障进行监控,并依此进行Master切换
# 使用vrrp_script则可以通过脚本进行自定义的(对本节点)健康检查
vrrp_script
chk
{
script
"/bin/bash -c 'curl -m1 -k -s https://127.0.0.1:6443/healthz -o/dev/null'"
# 每两秒执行一次
interval
2
# 如果检测成功(脚本结果为0),且weight大于0,则当前实例的优先级升高
# 如果检测失败(脚本结果为非0),且weight小于0,则当前实例的优先级降低
weight
-
10
# 连续3次为0才认为成功
fall
3
# 连续1次失败则认为失败
rise
1
}
# 实例
vrrp_instance
string
{
# 实例状态
# 如果所有实例都配置为BACKUP,则初始时高优先级的成为第一个Master
# 如果一个设置为MASTER其它设置为BACKUP,那么不设置nopreempt时每当Master恢复都会强占成为主
state
MASTER
|
BACKUP
# 高优先级的实例恢复正常后,不会去强占,成为Master
nopreempt
# 使用的网络接口
interface
string
# <span style="color: #404040;" data-mce-style="color: #404040;">VRRP通知的IP头上的IP地址</span>
mcast_src_ip
@
IP
# LVS sync_daemon在什么网络接口上运行
lvs_sync_daemon_interface
string
# 此实例所属的虚拟路由器ID
virtual_router_id
num
# 优先级,其运行时值可以随着vrrp_script的检测结果动态变化
# 如果接收到Peer的vrrp广播包,发现自己的优先级最高,则自动切换为Master
priority
num
# VRRP通知间隔
advert_int
num
# 当MASTER状态变化时进行邮件通知
smtp_alert
# VRRP身份验证配置
authentication
{
auth_type
PASS
|
AH
auth_pass
string
}
# 定义虚IP
virtual_ipaddress
{
@
IP
@
IP
@
IP
}
# 排除虚IP
virtual_ipaddress_excluded
{
@
IP
@
IP
@
IP
}
# 进入MASTER状态后执行的脚本
notify_master
/
path_to_script
/
script_master
.
sh
# 进入BACKUP状态后执行的脚本
notify_backup
/
path_to_script
/
script_backup
.
sh
# 进入FAULT状态后执行的脚本
notify_fault
/
path_to_script
/
script_fault
.
sh
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
global_defs
{
notification_email
{
admin
@
example1
.
com
}
smtp
_
server
127.0.0.1
[
<
PORT
>
]
smtp_helo_name
<
HOST_NAME
>
smtp_connect
_
timeout
30
router_id
my_hostname
# 机器标识
vrrp_mcast
_
group4
224.0.0.18
# VRRP组播地址
vrrp_mcast_group6
ff02
::
12
default_interface
eth0
# 静态地址的默认网络接口
# LVS连接同步守护进程的配置
# 监听接口 对应VRRP实例 lvs syncd的ID 最大包长度 使用的UDP端口 组播地址
lvs_sync_daemon
<
INTERFACE
>
<
VRRP_INSTANCE
>
[
id
<
SYNC_ID
>
]
[
maxlen
<
LEN
>
]
[
port
<
PORT
>
]
[
ttl
<
TTL
>
]
[
group
<
IP
ADDR
>
]
lvs_timeouts
tcp
7200
tcpfin
120
udp
300
# LVS超时配置
lvs_flush
# 启动时清空所有LVS配置
vrrp_garp_master
_
delay
5
# 转变为MASTER后多久发起ARP宣告
vrrp_garp_master
_
repeat
5
# 发起ARP宣告的次数
vrrp_garp_master
_
refresh
0
# ARP重新宣告的周期,默认0表示不重复宣告
vrrp_garp_master_refresh
_
repeat
1
# 重新宣告时发送ARP的次数
vrrp
_
version
2
# 使用的VRRP协议版本
}
使用DR模式时,必须保证:
RS本机必须有网络接口,配置为VIP。DR模式下LB节点仅仅修改以太网帧的目标MAC地址,IP包不做任何修改,因此RS必须作为IP包的Destination
RS不去响应针对VIP的ARP请求
RS不去发送关于VIP的ARP通知
要保证上面三点,需要调整RS内核的行为。具体实施方法有几种:
添加arptables规则
修改内核参数
关于内核参数/proc/sys/net/ipv4/conf/*/arp_ignore:
DR模式下,每个真实服务器节点都要在环回网卡上绑定VIP。如果客户端对于VIP的ARP请求广播到了各个真实服务器节点,当:
arp_ignore=0,则各个真实服务器节点都会响应该arp请求,此时客户端就无法正确获取LVS(LB)节点上正确的VIP所在网卡的MAC地址。客户端可能将以太网帧绕过LB直接发给RS
arp_ignore=1,只响应目的IP地址为接收网卡上的本地地址的arp请求
关于内核参数/proc/sys/net/ipv4/conf/*/arp_announce:
每台服务器或者交换机中都有一张arp表,该表用于存储对端通信节点IP地址和MAC地址的对应关系。当
收到一个未知IP地址的arp请求,就会在本机的arp表中新增对端的IP和MAC记录
当收到一个已知IP地址(arp表中已有记录的地址)的arp请求,则会根据arp请求中的源MAC刷新自己的arp表
如果arp_announce参数配置为0,则网卡在发送arp请求时,可能选择的源IP地址并不是该网卡自身的IP地址,这时候收到该arp请求的其他节点或者交换机上的arp表中记录的该网卡IP和MAC的对应关系就不正确,可能会引发一些未知的网络问题,存在安全隐患。
DR模式下要求arp_ignore参数配置为1,arp_announce参数配置为2:
# 修改所有RS的内核参数
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
# 在环回网卡上绑定虚拟IP
ifconfig
lo
:
k8s
10.0.10.1
netmask
255.255.255.255
broadcast
10.0.10.1
ifconfig
lo
:
etcd
10.0.10.1
netmask
255.255.255.255
broadcast
10.0.10.1
ifconfig
lo
:
ceph
10.0.10.1
netmask
255.255.255.255
broadcast
10.0.10.1
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
global_defs
{
router_id
oxygen
# 增加LVS超时
# 可以防止TCP连接静置一段时间后服务器返回RST
# 可通过ipvsadm -l --timeout确保生效
# 设置超时大于7200,因为TCP保活定时器默认2小时执行,保持行为一致
lvs_timeouts
tcp
7200
tcpfin
120
udp
300
lvs_sync_daemon
eth0
51
}
vrrp_instance
apiserver
{
state
MASTER
interface
eth0
virtual_router_id
51
priority
100
advert_int
1
authentication
{
auth_type
PASS
auth_pass
gmem
}
virtual_ipaddress
{
10.0.10.1
/
32
brd
10.0.10.1
dev
eth0
label
eth0
:
k8s
10.0.10.2
/
32
brd
10.0.10.2
dev
eth0
label
eth0
:
etcd
10.0.10.3
/
32
brd
10.0.10.3
dev
eth0
label
eth0
:
ceph
}
}
virtual
_
server
10.0.10.1
6443
{
delay
_
loop
5
lb_algo
wlc
lb_kind
DR
protocol
TCP
real
_
server
10.0.5.1
6443
{
weight
10
SSL_GET
{
url
{
path
/
healthz
status
_
code
200
}
connect
_
timeout
3
nb_get
_
retry
3
delay_before
_
retry
3
}
}
real
_
server
10.0.2.1
6443
{
weight
10
SSL_GET
{
url
{
path
/
healthz
status
_
code
200
}
connect
_
timeout
3
nb_get
_
retry
3
delay_before
_
retry
3
}
}
real
_
server
10.0.3.1
6443
{
weight
10
SSL_GET
{
url
{
path
/
healthz
status
_
code
200
}
connect
_
timeout
3
nb_get
_
retry
3
delay_before
_
retry
3
}
}
}
virtual
_
server
10.0.10.2
2379
{
delay
_
loop
5
lb_algo
wlc
lb_kind
DR
protocol
TCP
real
_
server
10.0.5.1
2379
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
real
_
server
10.0.2.1
2379
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
real
_
server
10.0.3.1
2379
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
}
virtual
_
server
10.0.10.3
6789
{
delay
_
loop
5
lb_algo
wlc
lb_kind
DR
protocol
TCP
# 要启用持久化连接,必须配置
persistence
_
timeout
300
real
_
server
10.0.5.1
6789
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
real
_
server
10.0.2.1
6789
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
real
_
server
10.0.3.1
6789
{
weight
10
TCP_CHECK
{
connect
_
timeout
3
}
}
}