如果要让你的服务器支持fullnat,需要如下修改:

  • 内核打阿里开源的fullnat补丁
  • 使用ali开源的keepalived
  • 使用ali开源的ipvsadm

Fullnat所有的代码,阿里都在github上开源了,附送了一批使用手册,相对来说已经比较完善。

内核打fullnat补丁

我的环境使用的是centos6.5,其内核版本是2.6.32-431。按千户最早的想法,为了避免影响原来系统的稳定性,我们觉得重编ip_vs相关ko并替换原有内核模块,风险相对小一点;但很奇葩的是,centos6.5的yum源里,只有2.6.32-573版本的内核了。所以如果要支持fullnat,就需要升级内核,并打补丁,相对来说比较繁琐。 所幸,阿里开源时直接提供了一个打过补丁的内核包,其版本是2.6.32-220,我们可以直接用这个包来编译内核。

如果你也要编译,可以直接参考lvs官网的一篇文档。我这里使用了centos6.5发行版的config,下面写下操作的步骤。

wget http://kb.linuxvirtualserver.org/images/b/b7/Linux-2.6.32-220.23.1.el6.x86_64.lvs.src.tar.gz
tar xvf Linux-2.6.32-220.23.1.el6.x86_64.lvs.src.tar.gz
# ----不要被tar.gz骗了,实际这只是个tar包
cd linux-2.6.32-220.23.1.el6.x86_64.lvs
# 用发行版的config,覆盖.config
cp /boot/config-2.6.32-431.el6.x86_64 .config
sh -c 'yes "" | make oldconfig'
# 开始编译,内存有多少G,就-j多少
make -j16
# 安装module
make modules_install;
# 安装内核
make install;

安装完了以后,查看/boot/下的initrd-2.6.32、vmlinuz等文件,其时间都更新了;修改/boot/grub/menu.list,修改default为0,即2.6.32(默认default是1,即发行版的2.6.32-431)。

default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32)
        root (hd0,0)
        kernel /vmlinuz-2.6.32 ro ...
        initrd /initramfs-2.6.32.img
title CentOS (2.6.32-431.el6.x86_64)
        root (hd0,0)

到这里,内核就升级完毕了,重启后使用的就是支持fullnat的2.6.32-200了。

编译安装keepalived和ipvsadm

由于fullnat是新的packet-forwarding-method,所以ipsvadm是必须要用阿里开源的版本。但是在编译的时候,发现它居然还依赖keepalived囧。

编译、安装keepalived(下面是从LVS官网抄的)

 cd /root/keepalived
 ./configure --with-kernel-dir="/lib/modules/`uname -r`/build"
 make
 make install

编译、安装ipvsadm(下面也是从LVS官网抄的)

 cd /root/ipvsadm
 make
 make install

我没有编译quaage。

好了,按上面2步,内核升级OK、ipvsadm、keepalived安装OK,重启即可。

fullnat使用

相对centos发行版的ipvsadm,阿里开源的ipvsadm要支持fullnat,主要是加了如下2种命令:

添加Real Server

ipvsadm -a -t service-address -r server-address -b

其中-b是指--fullnat -b fullnat mode。我们向Virtual Service添加的所有RS,都需要加-b参数(之前是-m,masquerading)。

添加、查看Local Address

#add
ipvsadm -P -t service-address -z local-address
#get
ipvsadm -G -t service-address
#delete
ipvsadm -Q -t service-address -z local-address

为什么需要Local Address呢?参照前一篇文章里对Fullnat的说明,syn报文经过LVS,转发给Real Server时,除了要做DNAT修改目的地址为Real Server的地址,还需要做SNAT,修改源地址。那么源地址填谁呢?

通常来说,LVS自己可以根据目的地址和路由信息,从本机选择一个最匹配的源地址填写到报文中去。但很不幸,Fullnat有一个的缺陷,造成我们必须手工来指定一个Local Address作为源地址。

缺陷是什么呢?当我们配置virtual service时(-A -t x.x.x.1:port),命令下发后,会发现无法ssh连接x.x.x.1了!咨询阿里的同学,原来下发了以后,内核fullnat会对所有报文都做转发,不再上送(其实就是转发的时候没有查inpcb,看过这块代码的同学能明白,其实实现还是比较粗暴的)。所以我们在接口上配置2个地址,一个虚地址给fullnat用,一个给ssh用。

回到刚刚选择源地址的问题,如果让内核自己来选源地址,ipvsadm下发生效后我们就不能再ssh到内核选中的源地址了,这样显然是非常不合适的,因为我们不知道内核选的是哪个地址。所以Fullnat额外要求用户必须自己再配置一个Local Address。

最终,查看到的配置及连接信息如下:

[root@lvs ~]# ipvsadm -l -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.104.84:10000 wrr
  -> 192.168.103.98:9999          FullNat 1      17         0
  -> 192.168.103.99:9999          FullNat 1      15         0
[root@lvs ~]# ipvsadm -l -n -c
IPVS connection entries
pro expire state       source             virtual            destination
TCP 04:30  C0A86762    192.168.104.83:52861 192.168.104.84:10000 192.168.103.84:12354
TCP 04:30  C0A86762    192.168.104.83:52850 192.168.104.84:10000 192.168.103.84:12352

state那里是个bug,正常应该是ESTA/SYN_RECV之类,回头改掉。