最近一年,接触了大名鼎鼎的LVS,碰到一些问题,记录一下。由于工作中只用到 DR 模式,所以只写 DR 模式的一些问题。
LVS 只是内核的一个模块,只负责怎么把网络包负载均衡到 RS 上,这也符合 Unix/Linux 的设计哲学,一个模块只做一件事情,通过多个模块协作,完成整个功能。负载均衡系统除了负载功能,还需要考虑 RS 服务的健康检查,负载均衡系统本身的 HA 问题,而这些都是由其它模块完成的。工作中主要用 keepalived ,协作配合 LVS 来做负载均衡的。
DR 模式的限制
- VIP 的端口必须和RS服务的端口一致(DR模式只修改包的 mac 地址,不会修改IP及上层的内容)
- RS 必须对 arp 做相关设置1 2,lo 接口需要绑定VIP
- VIP 和 RIP 不需要在同一个网段,但是 Director 要有一个网口和 RS 是通过不分段的网络连接起来3
RS 的健康检查
LVS 是内核的一个模块,而健康检查一般是应用层的逻辑,比较复杂,毕竟要解析应用层的协议,写的不好,会影响内核的运行。所以健康检查需要借助其它模块,比如 keepalived。
DR 模式中,RS 需要监听 VIP,如果 RS 只监听 VIP,健康检查就比较麻烦。因为健康检查的网络包,目的地址是 VIP,而 Director 本身就绑定了 VIP,这个包怎么发出去呢?
TCP
一般 RS TCP server 监听0.0.0.0。keepalived 通过检查 RIP:PORT 来确定 RS 的健康状况。
UDP4
如果 RS 的 UDP server 监听0.0.0.0,则响应包的原地址变成了 RIP,而不是 VIP。当然也可以用 cmsg SOL_IP/IP_PKTINFO (ipi_spec_dst)
方式解决。生产中,RS 监听 VIP,健康检查简单粗暴,用 keepalived 的 MISC_CHECK, 直接写脚本 ping RIP
来判断 RS 上的 UDP 服务是否存活。
VIP 相关问题
VIP 在 RS 的配置5
VIP 一般配置在 RS 的 lo 中,当然也可以配置在其它接口中。如果配置在 lo 中,子网掩码必须是255.255.255.255
。原因是 lo 不仅仅响应 VIP,还响应 VIP 这个网段的所有 IP。
比如 eth0 绑定192.168.2.200/30,则 eth0 只会响应192.168.2.200这个IP的请求。但是 lo 绑定192.168.2.200/30,则会响应该段所有 IP(192.168.2.200~192.168.2.203) 的请求。
$ ifconfig lo:1 192.168.2.200/30
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet 192.168.2.200/30 scope global lo:1
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 qlen 1000
link/ether 08:00:27:12:96:98 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
inet6 fe80::a00:27ff:fe12:9698/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 08:00:27:17:e9:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.2.104/24 brd 192.168.2.255 scope global eth1
inet6 fe80::a00:27ff:fe17:e999/64 scope link
valid_lft forever preferred_lft forever
another box
$ ping 192.168.2.200 -c 1
PING 192.168.2.200 (192.168.2.200): 56 data bytes
64 bytes from 192.168.2.200: icmp_seq=0 ttl=64 time=0.311 ms
--- 192.168.2.200 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.311/0.311/0.311/0.000 ms
$ ping 192.168.2.201 -c 1
PING 192.168.2.201 (192.168.2.201): 56 data bytes
64 bytes from 192.168.2.201: icmp_seq=0 ttl=64 time=0.322 ms
--- 192.168.2.201 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.322/0.322/0.322/0.000 ms
$ ping 192.168.2.202 -c 1
PING 192.168.2.202 (192.168.2.202): 56 data bytes
64 bytes from 192.168.2.202: icmp_seq=0 ttl=64 time=0.256 ms
--- 192.168.2.202 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.256/0.256/0.256/0.000 ms
192.168.2.203
是广播地址,ping 不通
VIP 在 Director 的配置6
VIP 在Director里配置,子网掩码是否一定是 VIP 网段的掩码呢?根据大神说法,不是必须的。假设 DIP 配置在 eth0,并且/proc/sys/net/ipv4/conf/eth0/promote_secondaries
是关闭的,VIP 是 secondary address,netmask 可以是 DIP 的 netmask,或者32。是不是因为 LVS 只关注 mac 地址,不涉及路由,所以 VIP 的 netmask 可以随意?不晓得
keepalived 配置中,如果没有为 VIP 指定 netmask,默认为32
virtual_ipaddress {
192.168.1.100 # default netmask is 32
}
RS 是 Windows 2008 或以上版本7 8
Windows 2003 版本设置有点麻烦,现在还用 2003的,换更高版本吧。
对于2008或者2012的,添加 loopback 后,vip 的 netmask 还是 255.255.255.255。但是需要对 arp 做限制,Windows 是通过 weakhost7 来实现的
tcpdump抓包为什么会显示两条记录
- 第一条记录是客户端请求的包,四元组是(cip:cport,vip:vport)
- 第二条记录是 LVS 负载均衡的包,LVS 将包转发给 RS,整个包的 IP 内容并没有变化,还是(cip:cport,vip:vport)。所以抓包会显示两条同样的记录
localnode的问题9
TL;DR: 不要使用 localnode
localnode 就是 Director 和 RS 在同一台机器上。当年还没有虚拟机,所以为了提高机器的使用率,一台物理机器会跑很多的应用。所以可能会在一台机器跑 LVS 和 RS。
如果 LVS 主备都是 localnode,并且 backup 的 LVS rules 已经启用(比如 keepalived),那么就会出现下面的情况
- client 发 SYN 包给 master director
- 50% 机会 master director 把包转给 backup (因为 backup 也是 RS)
- 因为 backup 的 LVS rules 已经启用,所以50%机会 backup 把包转给 master
- master 收到包后,又把包转给 backup,然后陷入死循环。 (这里是不是50%机会?如果没有开 persistence 也会这样吗,不太清楚转发机制?)
如果用 telnet 测试,发现 telnet 收不到响应。而 LVS 上会发现 SYN 包不停的在转发,一会儿就把 LVS 搞挂了。
localnode 这个问题,有两种解决方案。
fliter by Mac address
假设主备 director, 可以这样处理:
- 经过 director1 的包,如果 mac address 不是 director2 的,用 iptables 给包打 mark=i
- 经过 director2 的包,如果 mac address 不是 director1 的,用 iptables 给包打 mark=j
- 同时配置 LVS,不用三元组(ip,port,protocol)来表示 virtual_server,而用 fwmark-service,keepalived 配置 lvs 使用 fwmark-service。
这样,如果是 director 转发过来的包,就不会进入 LVS 进行负载(防止两个 director 互相扔皮球,进入死循环),而是被 RS 服务处理。而客户端进来的包,就会进入 LVS 进行负载。
- Director1 配置 iptables ,除了 Director2 以外的包,都设置 mask 为3。
# iptables -t mangle -I PREROUTING -d $VIP -p tcp -m tcp --dport $VPORT -m mac \
! --mac-source $MAC_Director2 -j MARK --set-mark 0x3
- Director2 配置 iptables ,除了 Director1 以外的包,都设置 mask 为4。
# iptables -t mangle -I PREROUTING -d $VIP -p tcp -m tcp --dport $VPORT -m mac \
! --mac-source $MAC_Director1 -j MARK --set-mark 0x4
- keepalived 配置
virtual_server fwmark 3 { # node2 配置 fwmark 4
delay_loop 10
lb_algo rr
lb_kind DR
protocol TCP
real_server RIP1 8080 {
weight 1
MISC_CHECK {
# some check configuration
}
}
real_server RIP2 8080 {
weight 1
MISC_CHECK {
# some check configuration
}
}
}
包处理流程就变成这样
- client 请求,master director 收到包后,打了 mark 为 3。
- LVS 看到 mark=3 的请求,50% 自己的 RS 处理,50% 转发到 backup director。
- backup 收到包后,发现是 master 过来的,不设置 mark。所以这个包不会被 LVS 处理, 而被 backup 上面的服务给处理了
Redhat 7 之前用的是 Piranha 做 LVS 的 HA, 7版本后用 Keepalived。 Piranha 是不会启用 backup 的 rules的,所以不存在这个问题。
sysctl flag backup_only10
大神提到在 kernel 3.9+ 上,新增了一个 flag: backup_only=1,解决这个问题。生产中没有用这么新的 kernel,没有试过,不太清楚如果主备切换,flag 怎么做。