Tag: lvs

ubuntu 下的lvs tunnel real server 配置注意

来个速记。

以前就因为一次内网调整,使用了一次lvs 的tunnel 模式,后来都没有配置过了,今天因为要配置外网的lvs ,再次弄了一下,才发现要注意一下了。

DR 模式下的rs 配置一般如下:

/sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
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
tunnel 的话,可以改成这样:
/sbin/ifconfig tunl0 $VIP broadcast $VIP netmask 255.255.255.255 up
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
直接执行的话,可能会出现以下错误(反正那个死人坑爹的ubuntu 出现了!!!还是得找机会换了它。。。)
SIOCSIFADDR: No such device
tunl0: ERROR while getting interface flags: No such device
SIOCSIFNETMASK: No such device
竟然不能建立tun 设备!!
好吧,tun 设备作为module 了,没有编译进去内核@@
modprobe ipip 即可。

kernel 2.6.32 的服务器作为lvs 的rs 无法建立tcp 连接的问题

最近用xen 的虚拟机作为lvs 的rs 组了一下lvs ,发现上线以后,无法与client 建立tcp 连接,开始还以为是xen 的bridge 网络的问题,后来才把问题定位到系统内核。其实之前也出现过类似的问题的,但当时急着上线,没去研究了,这回太忙,连测试环境都搭出来了,也都没空去深究,今天同事帮我测试了一下,倒是完全明白了。

问题描述:

LVS 用dr 的方式组起来以后,访问vip 时,无法与real server 建立tcp 连接。tcpdump 看到只有client 过来的syn 包,rs 并不返回ack 包,根本无法完成tcp 握手。用curl 来模拟访问测试的话,会看到这样的返回 curl: (7) couldn’t connect to host 。

看看我的配置吧。

lvs director :

采用dr 的调度方式,通过内网把请求分配到real server

lvs 的 real server :

内核为 2.6.32-5-amd64 (2.6.32 这个内核很普遍了,debian 6 和 rhel 6 默认都这个了),不响应arp 配置

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

其它基本的配置就不说了,如果就这样子起服务后,就会出现我描述的问题了。

原因在于内核的这个参数 : reverse path filtering,这个是啥,我摘抄一下:

Reverse Path Filtering (RPF) is a technology that is used on InternetProtocol routers to try and prevent source address spoofing, which is often used for DenialOfService attacks. RPF works by checking the source IP of each packet received on an interface against the routing table. If the best route for the source IP address does not use the same interface that the packet was received on the packet is dropped. There are some situations where this feature will obviously not be the desired behaviour and will need to be disabled. In general if you are not multi-homed then enabling RPF on your router will not be a problem.

简单地说,就是如果从eth1 接收到包,就不会从eth0 返回,内核把这个包丢弃。

正好我们的LVS 就是这样的服务模式,lvs director 接收到client 对vip 的访问,经过包的重写通过内网把包分配给rs ,rs 直接使用外网返回这个client 的请求。正好就会被rp_filter 给干掉了。

但是,为什么我们以前并没有出现过类似的问题呢??看看rp_filter 的默认内核配置

net.ipv4.conf.eth1.rp_filter = 1

net.ipv4.conf.eth0.rp_filter = 1

net.ipv4.conf.lo.rp_filter = 0

net.ipv4.conf.default.rp_filter = 1

net.ipv4.conf.all.rp_filter = 0

rp_filter 的值的意义是:

814 rp_filter – INTEGER

815 0 – No source validation.

816 1 – Strict mode as defined in RFC3704 Strict Reverse Path

817 Each incoming packet is tested against the FIB and if the interface

818 is not the best reverse path the packet check will fail.

819 By default failed packets are discarded.

820 2 – Loose mode as defined in RFC3704 Loose Reverse Path

821 Each incoming packet’s source address is also tested against the FIB

822 and if the source address is not reachable via any interface

823 the packet check will fail.

0 就是对进来的包完全不作检查,这样有被dos 攻击的风险。

1 就是严格检查,只要不是这个interface 的包,就不返回。

2 就是不太严格,只要本机配置了这个ip ,还是可以返回的。

对于lvs 来说,用2 也是可以的。

只要把eth1 的 rp_filter 的值置为 0 ,lvs 的服务就能正常了。

从这里可以找到答案:http://www.spinics.net/lists/linux-net/msg17162.html

The first patch changed rp_filter from a boolean to an integer, and the

second patch changed the way the interface-specific value and the “all”

value are combined to produce a functional value from a logical AND to

an arithmetic MAX.

Before patches : functional value = interface AND all

After patches  : functional value = MAX(interface, all)

So now if net.ipv4.conf.all.rp_filter=1, source validation is enabled on

all interfaces as their functional value is at least 1. You may either

set net.ipv4.conf.all.rp_filter to 0 (to disable it) or 2 (to enable

loose mode globally), or set net.ipv4.conf.$interface.rp_filter to 2 (to

enable loose mode on $interface).

I guess that the patch suggested by Dave Miller is related to another

(apparently incomplete) change that occured in 2.6.32 :

在2.6.31 , 对于rp_filter 的最终值,有了不同的计算方法。

之前,是只要设置了all.rp_filter 为0 ,那么就是0 了。

之后,看具体的interface 和 all 的值的最大值来取最终值。

默认是 net.ipv4.conf.eth1.rp_filter = 1 和 net.ipv4.conf.all.rp_filter = 0 ,因此,组lvs 的时候,就会丢弃包了。

问题解决!!下次调一下内核参数吧。

PS:可以通过设置这个内核参数来查看一下log

echo 1 >/proc/sys/net/ipv4/conf/<interfacename>/log_martians

LVS hash size

LVS 有个 connection hash table ,默认的 size 为 4096,可以通过以下命令得到:

# ipvsadm -ln
IP Virtual Server version 1.0.12 (size=4096)

简单地说,这个hash table 用于记录每个进来的连接及路由去向的信息。面对庞大的连接时,这个4096是远远不够的,这时就会产生冲突,然后hash table  就不断置换table 中的数据,系统的负荷就这样上来了。所以,很多调优文章都说,要把这个值调大。至于如何调大呢,好像必须重编译内核了。。。

我的是 centos 5.4 final ,利用 srpm 去重编译吧,使用当前系统的 config ,这样编译后的内核只不过是改变了 hash table 的值,并没有改变其它的东西。

下一个 kernel 的 srpm :http://mirrors.163.com/centos/5.4/os/SRPMS/kernel-2.6.18-164.el5.src.rpm

重编内核时,把原来的 config(/boot/config-2.6.18-164.el5) copy 覆盖 SOURCES/kernel-2.6.18-x86_64.config .

把第一行的

#

改为

# x86_64

同时,修改 CONFIG_IP_VS_TAB_BITS=12 (2的12次方,4096)为 CONFIG_IP_VS_TAB_BITS=20 。

然后 rpmbuild -ba –target=`uname -m` kernel-2.6.spec ,新编译出来的kernel-2.6.18-164.el5.x86_64.rpm 就在 RPMS/x86_64/ 里了。

rpm -Uvh 吧。

reboot 后,再 ipvsadm -ln 就可以看到

IP Virtual Server version 1.2.1 (size=1048576)

成功!
lvs 调优的文章通常会说,如果你的并发连接数是 200,keep alive 是200秒,那么hash table size 就应该设置为不要低于 200×200=40000 太多,2的15次方32768 就差不多了。不过,谁知道我们的需求是否会变呢?我一般都懒得算,直接开到最大,20 就是最大的了。

还需要注意一点,就是每个连接是要占用内存的,印象中是每个连接占用136 bytes ,尽管说 lvs 可以抗百万级别的连接,也要注意有没有足够的内存哦,呵呵。

利用 piranha 快速搭建 lvs

最近为那个公司很重视的项目搭建 lvs ,在这里 log 一下吧。

利用red hat 提供的 piranha ,简单快捷。

我选择的是 centos 5.4 ,安装 piranha ,很简单

yum install piranha

此时,它还需要你安装 ipvsadm , php , php-cli , php-common , httpd ,piranha 是一个 web 管理的工具,没办法,虽然我不用web 界面,但这些在yum 下还是必须装上的。

装完以后,/etc/sysconfig/ha/lvs.cf 还是空的,我去抄一份过来。

serial_no = 1
primary = 60.12.227.1
service = lvs
backup_active = 1
backup = 60.12.227.2
heartbeat = 1
heartbeat_port = 10001
keepalive = 6
deadtime = 18
network = direct
debug_level = NONE
virtual vs[1] {
    active = 1
    address = 60.12.227.200 eth0:200
    vip_nmask = 255.255.255.0
    port = 80
    send = "GET / HTTP/1.0\r\n\r\n"
    expect = "HTTP"
    use_regex = 0
    load_monitor = none
    scheduler = wlc
    protocol = tcp
    timeout = 6
    reentry = 15
    quiesce_server = 0
    server rs[1] {
    address = 192.168.0.1
        active = 1
        weight = 2
    }
    server rs[2] {
    address = 192.168.0.2
        active = 1
        weight = 2
    }
}

到此,lvs 的 director 端,算是安装完成了。

有人会问了,为什么我没有修改 /proc/sys/net/ipv4/ip_forward 为1 。其实,这里我也还不是很懂,理论上,转发是必须设为1 的,但我发现,貌似不设,也可以正常工作,难道是 piranha 的关系??

然后就是配置 real server 端了。

我又抄一个shell 过来

#!/bin/sh
 
VIP=60.12.227.200
/sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
 
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

由于我的director 端配置的是 dr 模式,所以必须关闭arp 响应。

执行一下这个shell,real server 端也算是完成了。

启动 director 端:

# /etc/init.d/pulse start
Starting pulse:                                            [  OK  ]

查看服务状态:

# ipvsadm -ln
 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  60.12.227.200:80 wlc
-> 192.168.0.1:80             Route   2      326        172
-> 192.168.0.2:80             Route   2      326        192

搭建完成了,算是比较快速的了,呵呵。还有一点知识点的,下回再说吧。