kvs负载均衡
南风未起 2024-07-23 09:03:00 Linux
# LKV负载策略
# 1.keepalived
# 1.1.简介
keepalived是由C语言编写的路由软件,用于为Linux系统和基于Linux的基础结构提供`负载均衡`和`高可用`的简单而强大的功能。它依赖于提供四层负载均衡的Linux虚拟服务器(IPVS)内核模块lvs。keepalive实现了一组VIP功能,能够根据运行状况动态、自适应地维护负载均衡的服务器池。`VRRP`实现了高可用协议,是路由器故障转移的技术保证,也是keepalived实现服务高可用的基础。1
# 1.2.VRRP
VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)可以将多台物理路由器设备虚拟出一个虚拟路由器,每个虚拟路由器都有一个唯一标识,称为VRID,一个VRID绑定一组IP地址构成虚拟路由器。 虚拟路由器利用VIP对外提供服务,同一时间内部只会有一台物理机路由器对外提供服务,称为主路由器Master。其他物理路由器不持有VIP信息,也不提供对外网络功能,仅仅接收Master的VRRP状态通告信息,称为备份路由器Backup。主路由器失效时,处于Backup角色的备份路由器将重新进行选举,最终会有一台新的主路由器作为Master继续对外提供服务.1
2
# 1.3.运行原理
keepalived的主从路由器之间会进行心跳检测,主路由器挂掉后keepalived通过选举挑选出一台热备服务器作为Master,新的Master路由器将持有VIP对外提供服务。当挂到的Master服务器重启后,会重新持有VIP信息。 --- 选举策略 - keepalived启动时根据权重选举Master - Master路由器挂掉后根据Backup路由器的权重选出新的Master - 新的Backup加入且权重最大触发重新选举并作为Master1
2
3
4
5
6
# 1.4.状态检测
--- keepalived工作在tcp/ip的网络层、传输层和应用层 - 网络层,keepalived通过ICMP协议向服务器集群每个节点发送数据包,如果超时未回应则会剔除故障节点 - 传输层,keepalived利用tcp协议的端口连接和扫描技术判断集群节点是否正常,如果出现端口冲突会将对应节点剔除 - 应用层,keepalived根据配置的检测手段判断节点是否正常,如果不正常会剔除故障节点1
2
3
4
# 2.LVS
# 2.1.简介
LVS是一个开源的软件,可以实现linux平台下简单负载均衡,目前已被集成到Linux内核系统。ipvsadm是lvs的命名行管理工具,目前支持三种IP负载均衡技术(VS/NAT、VS/TUN和VS/DR)和八种调度算法(rr,wrr,lc,wlc,lblc,lblcr,dh,sh)。 --- LVS主要由三部分组成 - 负载均衡层,位于集群最前端,由一台或多态负载调度器组成,其上安装lvs核心模块ipvs实现流量分发。同时监控各个real server 监控状况 - 服务器群组,由一组实际运行应用服务的主机组成,每个real server之间通过高速LAN相连 - 数据共享存储,为所有real server提供共享存储空间和内容一致性的存储区域,一般由磁盘阵列设备构成1
2
3
4
5
6
# 2.2.IPVS
ipvs安装在负载均衡层,用于虚拟出VIP对外提供服务,流量会通过VIP到达负载均衡器,然后由负载调度器从real server列表选出一个服务节点响应。 --- 转发方式 - vs/nat 流量转发和响应时进行vip和real server ip的切换,高并发下会成为调度器的处理瓶颈 - vs/tun 基于ip隧道技术实现虚拟服务器,流量通过ip隧道转发并直接响应,对于vip和服务ip网段没有要求,吞吐量大 - vs/dr 基于直接路由实现虚拟服务器,通过改写流量mac地址转发并直接响应,要求调度器和real server网卡连在同一网段(最优) --- 负载均衡策略 - 轮询调度 1:1方式转发流程 - 加权轮询 根据权重及负载偏向性转发流量 - 最少链接 根据已建连接最少的服务器进行流量转发 - 加权最小链接 根据权重和已建链接数正比分发流量 - 基于局部最少链接 根据目标地址找到最近使用服务器,服务器可用且未处于半数工作负载时进行流量转发,否则基于最少链接转发 - 带复制的基于局部最少链接 根据目标地址找到维护的服务器组,利用最小链接选出目标;否则从集群找到最小链接服务器加入并进行 流量转发,当服务器组长期未修改时将最忙的服务器从组内剔除 - 目标地址散列 根据目标地址计算散列键从静态分配的散列表选出对应服务器 - 源地址散列 根据源地址计算散列键从静态分配的散列表找到对应服务器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.工作模式
# 3.1.NAT
# 3.1.1.简介
NAT(Network Address Translation 网络地址转换)是一种外网和内外地址映射的技术,内网可以是私有地址,外网可以使用NAT方法修改数据报头,让外网于内网可以互相通信。NAT模式下,网络数据包的进出都要经过LVS处理,LVS需作为RS的网关。当数据包到达LVS时,LVS做目标地址转换(DNAT),将目标IP改为RIP,。RS收到数据包后进行处理并返回响应,响应时经过LVS中转将源地址转换为VIP。这种模式实现的负载配置及管理简单,并且LVS调度和应用服务器可以处于不同网段,网络架构更加灵活。1
# 3.1.2.原理
--- 实现原理 1.当客户端请求到达DS,此时请求的数据报文会先到内核空间PREROUTING链,此时的报文源IP未CIP,目标IP为VIP 2.PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链 3.IPVS比对数据包请求的服务是否为集群服务,是的话会修改数据包的目标IP地址为后端服务器IP,然后将数据包发至PREROUTING链,此时报文源IP为CIP,目标为RIP 4.PREROUTING链通过选路,将数据包发给RS 5.RS比对发现目标为自己IP,开始构建响应报文发回给DS,此时报文的源IP为RIP,目标IP为CIP 6.DS在响应客户端前,此时将源IP地址修改为自己的VIP地址,然后响应给客户端,此时报文的源IP为VIP,目标IP为CIP --- 特性 1.RS应该使用私有地址,RS的网关必须指向DIP 2.DIP和RIP必须在同一个网段内 3.请求和响应报文都需要经过DS,高负载场景DS可能变成性能瓶颈 4.支持端口映射 5.RS可以使用任意操作系统1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.2.DR
# 3.2.1.简介
DR(Direct Routing 直接路由模式)下LVS调度器只接受客户端发来的请求并将请求转发给后端服务器,后端服务器处理请求后把内容直接响应给客户,而不用再次经过LVS调度器。LVS只需要将网络帧的MAC地址修改为某一台后端服务器RD的MAC,该包就会转发到相应的RS处理,此时数据包的源IP和目标IP都没变。。RS收到LVS转发的包时,链路层发现MAC是自己的,到上面的网络层发现IP也是自己的,于是包被合法接收,此时RS感知不到前面LVS的存在。 --- 特点 1.确保前端路由器将目标IP为VIP的请求报文发往Director 2.RIP可以使用私网地址或公网地址,RIP和DIP要求在同一个网络,RIP的网关不能指向DIP,确保报文不会经由Director 3.RS和DS要在一个物理网络(强制要求,因为通过修改数据包的mac实现转发) 4.此模式不支持端口映射1
2
3
4
5
6
7
# 3.2.2.原理
--- 实现原理 1.当用户请求到达DS,此时请求的数据报文会先到内核空间的PREROUTING链,此时报文的源IP为CIP,目标IP为VIP 2.PREROUTING检查发现数据包的目标IP为本机,将数据包送至INPUT链 3.IPVS比对数据包请求的服务是否为集群服务,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标的MAC地址修改为RIP的MAC地址,然后将数据包发至POSTROUTING链 4.由于DS和RS在同一个网络,所以通过二层来传输,POSTROUTING链检查目标MAC地址为RIP的MAC地址,此时数据包会发送至RS 5.RS发现请求报文的MAC地址是自己的MAC地址后,接收报文并处理,然后将响应报文通过lo接口传送给eth0网卡然后向外发出,此时的源IP为VIP,目标IP为CIP1
2
3
4
5
6
# 3.3.TUN
# 3.3.1.简介
TUN(virtual server via ip tunneling IP 隧道)调度器把请求的报文通过IP隧道转发到真实服务器。真实的服务器将响应处理后的数据直接返回给客户端,实现调度器仅处理请求入栈报文。这种转发方式不修改请求报文的IP首部(CIP->VIP),而是在源IP报文之外再封装一个IP首部(DIP->RIP),将报文发往挑选的目标RS,RS直接响应给客户端(VIP->CIP),由于一般网络服务应答数据比请求报文大很多,采用TUN模式后集群系统的最大吞吐量可以提高10倍。 --- 特点 1.DIP、RIP、VIP都要求公网地址 2.RS网关不允许指向DS 3.请求报文要经过Director,但响应报文直接发送给客户端 4.此模式不支持端口映射 5.RS操作系统需要支持隧道功能1
2
3
4
5
6
7
8
# 3.3.2.原理
--- 实现原理 1.用户请求到达DS,此时请求的数据报文会先到内核空间的PREROUTING链,此时报文的源IP为CIP,目标IP为VIP 2.PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链 3.IPVS比对数据包请求的服务是否为集群服务,将请求报文的首部再次封装一层IP报文,封装源IP为DIP,目标IP为RIP,然后发至PREROUTING链 4.PREROUTING链根据最新封装的IP报文,将数据包发至RS(外层封装多了一层IP首部,可以理解为通过隧道传输) 5.RS接收到报文后发现是自己的IP地址,就将报文接收下来,拆除掉最外层的IP后,会发现内部还有一层IP首部,而且目标是自己lo接口的VIP,此时RS处理请求并通过lo接口将响应数据报文送至eth0网卡向外传递,此时源IP为VIP,目标IP为CIP1
2
3
4
5
6
# 3.LKV的高可用负载
# 3.1.部署脚本
--- 以RS为192.168.1.1、192.168.1.2、192.168.1.3,VIP为203.0.113.10示例 #!/bin/bash # Display usage usage() { echo "Usage: $0 -v <VIP> -r <Real_Server_IPs_Comma_Separated> -p <Router_Priority> -s <MASTER/BACKUP> -vlan <VLAN_ID> -n <NODE_TYPE> -port <PORT> [-i <INTERFACE>]" exit 1 } # Split command-line arguments while getopts ":v:r:p:s:vlan:n:port:i:" opt; do case $opt in v) VIP=$OPTARG ;; r) RS_ADDRESSES=$OPTARG ;; p) PRIORITY=$OPTARG ;; s) STATE=$OPTARG ;; vlan) VLAN_ID=$OPTARG ;; n) NODE_TYPE=$OPTARG ;; # Node type port) PORT=$OPTARG ;; # Port i) INTERFACE=$OPTARG ;; # Network interface (optional) *) usage ;; esac done # Check if all arguments are provided if [ -z "$VIP" ] || [ -z "$RS_ADDRESSES" ] || [ -z "$PRIORITY" ] || [ -z "$STATE" ] || [ -z "$VLAN_ID" ] || [ -z "$NODE_TYPE" ] || [ -z "$PORT" ]; then usage fi # Run a command and check last status run_cmd() { "$@" local status=$? if [ $status -ne 0 ]; then echo "Error with $1" >&2 exit $status fi return $status } # Install necessary packages with environment install_dependencies() { echo "Installing keepalived and ipvsadm..." if command -v apt-get &> /dev/null; then run_cmd apt-get update run_cmd apt-get install -y keepalived ipvsadm elif command -v yum &> /dev/null; then run_cmd yum install -y keepalived ipvsadm else echo "Unsupported package manager, only apt, apt-get, and yum are supported." exit 1 fi } # Check if VLAN interface exists check_vlan_interface() { VLAN_INTERFACE="${INTERFACE:-eth0}.$VLAN_ID" if ip link show "$VLAN_INTERFACE" &> /dev/null; then echo "VLAN interface $VLAN_INTERFACE already exists." else echo "VLAN interface $VLAN_INTERFACE does not exist..." exit 1 fi } # Create Keepalived configuration file create_keepalived_config() { RS_ARRAY=(${RS_ADDRESSES//,/ }) cat <<EOF > /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { router_id LVS_$(hostname) # Must be unique within the cluster } vrrp_instance VI_1 { state $STATE # 设置非抢占式,避免当前Master流量未处理完被强行降级 nopreempt interface $VLAN_INTERFACE # Network interface virtual_router_id 51 priority $PRIORITY advert_int 1 authentication { auth_type PASS auth_pass 1111 } # 单播模式 # unicast_src_ip 99.15.132.177 # unicast_peer { # 99.15.131.176 # 99.15.131.177 # } virtual_ipaddress { $VIP # Virtual IP } } virtual_server $VIP $PORT { delay_loop 3 lb_algo rr lb_kind DR persistence_timeout 0 protocol TCP EOF for RS in "${RS_ARRAY[@]}"; do cat <<EOF >> /etc/keepalived/keepalived.conf real_server $RS $PORT { weight 1 TCP_CHECK { connect_timeout 3 retry 3 delay_before_retry 3 connect_port $PORT } } EOF done echo "}" >> /etc/keepalived/keepalived.conf } # Configure loopback address and ARP settings configure_loopback_and_arp() { echo "Configuring loopback address and ARP settings..." if ip addr show dev lo | grep -q "$VIP"; then echo "Loopback address $VIP is already configured." else run_cmd ip addr add $VIP/32 dev lo fi if [ "$(cat /proc/sys/net/ipv4/conf/lo/arp_ignore)" != "1" ]; then echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore fi if [ "$(cat /proc/sys/net/ipv4/conf/lo/arp_announce)" != "2" ]; then echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce fi if [ "$(cat /proc/sys/net/ipv4/conf/all/arp_ignore)" != "1" ]; then echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore fi if [ "$(cat /proc/sys/net/ipv4/conf/all/arp_announce)" != "2" ]; then echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce fi } # Add iptables rule for redirection add_iptables_rule() { echo "Adding iptables rule for redirection..." # Open VRRP port if iptables -C INPUT -p udp --dport 112 -j ACCEPT 2>/dev/null; then echo "Firewall rule for VRRP port already exists." else run_cmd iptables -A INPUT -p udp --dport 112 -j ACCEPT fi # Open port for proxy and service if iptables -C INPUT -p tcp --dport $PORT -j ACCEPT 2>/dev/null; then echo "Firewall rule for port $PORT already exists." else run_cmd iptables -A INPUT -p tcp --dport $PORT -j ACCEPT fi if iptables -t nat -C PREROUTING -d $VIP -p tcp --dport $PORT -j REDIRECT 2>/dev/null; then echo "Iptables rule for redirection already exists." else run_cmd iptables -t nat -A PREROUTING -d $VIP -p tcp --dport $PORT -j REDIRECT fi } # Start and enable Keepalived service start_keepalived_service() { echo "Starting and enabling Keepalived service..." run_cmd systemctl restart keepalived run_cmd systemctl enable keepalived } # Configure firewall rules configure_firewall() { echo "Configuring firewall rules..." if iptables -C INPUT -p udp --dport 112 -j ACCEPT 2>/dev/null; then echo "Firewall rule for VRRP already exists." else run_cmd iptables -A INPUT -p udp --dport 112 -j ACCEPT fi if iptables -C INPUT -p tcp --dport $PORT -j ACCEPT 2>/dev/null; then echo "Firewall rule for port $PORT already exists." else run_cmd iptables -A INPUT -p tcp --dport $PORT -j ACCEPT fi } # Check LVS load balancing and keepalived status check_lvs() { echo "Checking LVS load balancing..." LVS_STATUS=$(ipvsadm -L -n) if [[ -z "$LVS_STATUS" ]]; then echo "LVS is not running or no services are configured." exit 1 fi echo "$LVS_STATUS" echo "Checking Keepalived status..." local vrrp_status=$(ip -d link show type vrrp | grep -E 'state MASTER|state BACKUP') if [[ "$vrrp_status" != *"MASTER"* && "$vrrp_status" != *"BACKUP"* ]]; then echo "Error: Keepalived state is not MASTER or BACKUP" exit 1 fi echo "Keepalived is running properly with VRRP status: $vrrp_status" } # Main script execution if [[ "$NODE_TYPE" == "ds" ]]; then install_dependencies check_vlan_interface create_keepalived_config configure_loopback_and_arp add_iptables_rule start_keepalived_service configure_firewall echo "LVS + Keepalived configuration completed on DS node." check_lvs elif [[ "$NODE_TYPE" == "rs" ]]; then check_vlan_interface configure_loopback_and_arp echo "VLAN and loopback configuration completed on RS node." else echo "Unknown NODE_TYPE: $NODE_TYPE. Please specify either 'ds' or 'rs'." exit 1 fi --- 运行 ./setup_lvs_keepalived.sh -v 192.168.1.100 -r 192.168.1.10,192.168.1.11 -p 100 -s MASTER -vlan 100 -n ds -port 80 -i bond01
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239方案
1.DR模式下必须保证机器处在相同二层网络(同交换机/路由器网络相连),采用默认的组播模式实现主备心跳检测
2.如果无法保证DS机器处于相同二层网络,可采用单播工作模式,通过配置源端和对端公网IP实现主备心跳检测,降低设备限制,但DS->RS工作稳定性会受影响
3.如果
VIP和机器网络网卡的IP不在同一个网段,直接将VIP绑定在该网卡后VIP无法连接网络,此时需要在主网卡上建子网卡分配与VIP同网段IP,保证绑定VIP的网卡都处在该网段,可以正常使用网络
# 3.2.架构

搭建及状态监听动作采用kubernetes operator实现,基于上述脚本调整进行自动化部署
# 4.循环流量
# 4.1.背景
实际的生产场景下,往往存在硬件资源数量限制,此时需要设置DS节点复用RS节点,所以常见一个节点既作为DS节点又作为RS节点的架构,如下。 --- 复用架构 Client │ │VIP:7.7.7.18 ┌────────────────┼────────────────┐ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────┴───────┐ │ RS1/DS1 │ │ RS2/DS2 │ │ RS3 │ │7.7.7.11 │ │7.7.7.12 │ │7.7.7.13 │ └──────────────┘ └──────────────┘ └──────────────┘1
2
3
4
5
6
7
8
9
10
11
# 4.2.循环流量
# 4.2.1.场景描述
如果LVS主备复用DS和RS特性,并且backup的LVS rules已经启用(比如keepalived),此时会出现LVS转发混乱情况。这种情况出现的原因在于clent发SYNC包给master DS,50%机会master DS会把包转给backup DS(backup也是RS)。由于backup的LVS rules已经启用了(keepalived启动时),所以backup有50%机会把包传给master DS,master收到包后又有50%机会转发给backup,陷入死循环。1
# 4.2.2.解决思路
iptables的manage表可以对目标数据包加上mark标记,用于实现策略路由控制数据包的流向。因此,可以在规则中指定对端流量的mac地址来源,当流量不是来自对端mac地址的时候,说明来自客户端的请求,需要打上mark标记;如果属于来自对端mac地址的请求,说明是主节点转发的请求,不会打上标记。相应地,LVS也支持通过fwmark配置虚拟服务器,替代场景的VIP:PORT模式,实现对打了fwmark标记的数据包进行转发,二者结合起来即可避免转发流量的死循环。 --- 完整请求链 客户端请求到达LVS主机--->主机的iptables规则对流量打标记--->LVS捕获打标记的流量进行转发--->如果请求转发到备机,由于mac地址规则限制不会对流量打标记--->备机的LVS不会进行转发,只会作为RS节点处理流量1
2
3
4
# 4.2.3.解决示例
VIP=10.128.190.248 VPORT=80 MAC_Director1=34:73:79:20:ca:73 MAC_Director2=34:73:79:20:cb:83 # DS1配置`iptables`,除了DS2以外的包都设置`mark`为3 iptables -t mangle -I PREROUTING -d 10.128.190.248 -p tcp -m tcp --dport 80 -m mac ! --mac-source 34:73:79:20:cb:83 -j MARK --set-mark 0x3 # DS2配置`iptables`,除了DS1以外的包,都设置`mark`为4 iptables -t mangle -I PREROUTING -d 10.128.190.248 -p tcp -m tcp --dport 80 -m mac ! --mac-source 34:73:79:20:ca:73 -j MARK --set-mark 0x3 # 查看表`mangle`:(默认`iptables`查看的是`filter`表) iptables -t mangle -L -n firewall-cmd --info-ipset=lvs_mark_list # DS1配置`keepalived` virtual_server fwmark 3 { ... } # DS2配置`keepalived` virtual_server fwmark 4 { ... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24思路
1.client请求的流量经master后打上mark=3
2.LVS监控到mark=3的流量进行转发,50%几率发送到backup
3.backup收到包发现是master的mac地址请求的,不会打上mark=4的标记,此时的流量不会被LVS转发,会作为RS流量处理
防止系统重启时iptables规则失效