在LVS上实现SNAT网关
最近研究LVS,给其增加了SNAT网关的功能,见https://github.com/jlijian3/lvs-snat,下面跟大家分享一下学习心得。
LVS简介:
LVS是Linux Virtual Server的简写,即Linux虚拟服务器,功能是4层反向代理负载均衡。 1998年5月由章文嵩博士开源。 2004年12月25日做为netfilter模块加入官方内核2.6.10。 站点:http://www.linuxvirtualserver.org/ http://zh.linuxvirtualserver.org/
研究目的:
使用LVS做4层反向代理负载均衡 在网关做SNAT,为内网机器提供访问外网的功能
假设大家已经了解LVS的转发方式,这里仅叙述分析和解决问题的过程。
使用场景:
反向代理使用LVS的NAT转发模式
LVS机器有两张网卡,一张绑定外网IP,另一张绑定内网IP; 后端机器跟LVS内网IP处于同一个内网网段,每台后端机器都只有一个内网地址,他们的网关都指向LVS机器内网IP。
在LVS机器上做SNAT网关
内网机器访问外网时(比如访问www.baidu.com),数据包会经过LVS机器转发出去,如果想正常访问要做以下事情,即SNAT: 1)把数据包的源ip由内网地址改为LVS机器的外网地址,再转发出去; 2)收到外网服务器的应答时,需要把目的地址由LVS机器的外网地址改为内网机器地址,再转发到内网机器,如下图:
问题:
LVS不会帮我们做SNAT,它只做反向代理,只处理客户端请求和来自内网机器的应答数据。 一个解决方法是在LVS机器上启动iptables做SNAT,如:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to-source 121.14.161.100
但是如果启用iptables,即使内网机器不访问外网,也会严重影响到LVS反向代理的性能,因为每个应答数据包都有经过iptables(后来把单队列网卡升级为多队列网卡,发现iptables对LVS的性能影响很小,这里不展开分析)。
另一个解决方法是修改LVS源代码实现SNAT网关。
以学习研究LVS为目的,为LVS增加SNAT功能:
幸运的是小米的同学做了这件事情,https://github.com/xiaomi-sa/dsnat。 他在LVS的FULLNAT基础上,增加了SNAT网关功能,使内网机器可以访问外网。 可惜他没有考虑跟反向代理的兼容性,NAT/FULLNAT转发方式无法正常使用,FULLNAT的配置项被修改用于SNAT的配置。
接下来要做两件事情: 1)修复小米dsnat问题,使反向代理和SNAT可以同时使用;
2)直接在官方内核版本NAT转发模式上添加SNAT网功能,因为小米dsnat依赖于淘宝的FULLNAT补丁,我们并不需要FULLNAT。
1. 下载redhat 6.3内核
wget ftp://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/kernel-2.6.32-279.el6.src.rpm
2. 准备代码
rpm -ivh kernel-2.6.32-279.23.1.el6.src.rpm cd rpmbuild/SPECS rpmbuild -bp kernel.spec
3. 获取dsnat内核补丁和lvs tools代码
git clone git@github.com:xiaomi-sa/dsnat.git
4. 打补丁
cd ~/rpmbuild/BUILD/kernal-2.6.32-279.23.1.el6/linux-2.6.32-279.23.1.el6.x86_64 #把dsnat内核补丁拷贝到当前目录 cp ~/dsnat/dsnat-kernel-2.6.32-279.el6/dsnat-2.6.32-279.el6.xiaomi.noconfig.patch ./ patch -p1 < dsnat-2.6.32-279.el6.xiaomi.noconfig.patch
5. 编译安装
make -j16 make modules_install make install #重启 init 6 #grub选择新内核
6. LVS工具安装
#进入刚才clone的dsnat代码目录下 cd dsnat/dsnat-kernel-2.6.32-279.el6/dsnat_tools/keepalived make && make install cd ../ipvsadm/ make && make install
7. 打开ipvs模块调试开关
cd ~/rpmbuild/BUILD/kernal-2.6.32-279.23.1.el6/linux-2.6.32-279.23.1.el6.x86_64 make menuconfig 进入Networking support->Networking options->Netfilter->IP Virtual server support,勾选IP Virtual server debugging
8. 打开pr_debug开关
#LVS很多地方使用pr_debug打日志,需要编译选项加上-DDEBUG #修改net/netfilter/ipvs/Makefile,增加 ifeq ($(CONFIG_IP_VS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif
9. 重新编译安装ipvs模块
#进入内核代码目录,编译模块 make M=`pwd`/net/netfilter/ipvs modules #卸载模块 ipvsadm -C lsmod|grep ip_vs rmmod ip_vs_rr rmmod ip_vs #更新ko文件 cp net/netfilter/ipvs/*.ko /lib/modules/`uname -r`/kernel/net/netfilter/ipvs #重启lvs
10. 查看ipvs模块调试日志
#修改ipvs调试日志级别,默认为0 echo 12 > /proc/sys/net/ipv4/vs/debulg_level #查看日志 dmesg|grep IPVS|less #如果想直接在屏幕输出 echo 8 > /proc/sys/kernel/printk
11. 使用tcpdump分析问题
#查看各个网卡的数据 tcpdump -X -n -i eth0
12. 修复dsnat跟NAT的兼容性bug
ipvs代码路径:net/netfilter/ipvs 在ip_vs_core.c中找到forward钩子函数ip_vs_out,内网机器访问外网的请求数据、NAT应答数据都经过此钩子函数。 dsnat的作者没有区分这两种数据包,错误的修改了NAT的应答数据包的目的地址。 解决方法:因为dsnat是通过添加一个0.0.0.0:0的service来做SNAT的,在做dsnat之前,判断svc->addr.ip和svc->port是否为0,否则走NAT应答数据处理流程。 详情见https://github.com/jlijian3/lvs-snat
13. 修复dsnat跟FULLNAT的兼容性bug
dsnat作者没有考虑跟FULLNAT兼容,直接修改了FULLNAT的local address配置参数,用来添加SNAT的映射地址。 而我们希望两者同时使用。接下来要做的就是恢复FULLNAT的local address的添加和使用方式。 不仅要修改ipvs代码,还有修改ipvsadm和keepalived,开发和调试比想象中的麻烦。 详情见https://github.com/jlijian3/lvs-snat
14. 不使用FULLNAT补丁,在官方内核上实现SNAT
小米的dsnat基于淘宝开源的FULLNAT,但是我们不需要FULLNAT转发功能,接下来从头开始,不打dsnat补丁,直接在NAT转发基础上修改,改动量很少,ipvsadm和keepalived不需要修改,但是不支持源地址白名单过滤。 详情见https://github.com/jlijian3/lvs-snat
15. 其他问题
SNAT网关目前只支持tcp和udp,icmp的转发还不支持,在内网ping 外网ip是ping不同的,今后有时间再增加对icmp的支持。
16. 生成补丁
调试测试ok,准备两份代码,修改前和修改后的
diff -rupN linux-2.6.32-279.el6 linux-2.6.32-279.el6.snat > lvs-snat-2.6.32-279.el6.patch
17. 基于dsnat的SNAT网关ipvsadm配置示例
#添加一个0/0的virtul service ipvsadm -A -t 0.0.0.0:0 -s rr #添加一个内网源地址网段,做源地址匹配,符合该网段的才做SNAT ipvsadm -K --zone 192.168.0.0/16 #为192.168.0.0/24添加一个local address,即映射后的外网地址,可以加多个,轮询使用 #注意dsnat中使用-P来添加zone的laddr,这里改为-U ipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.100 ipvsadm -U --zone 192.168.0.0/24 -z 121.14.161.101 #删除zone的laddr由-Q改为-W ipvsadm -W --zone 192.168.0.0/24 -z 121.14.161.100 #-P/-Q恢复为原来的功能,即为FULLNAT的service添加删除laddr
18. 基于NAT的SNAT网关的ipvsadm配置示例
#直接用官方的ipvsadm即可,功能简单,没有源地址网段匹配,只要不是反向代理转发的数据,就做SNAT #添加0.0.0.0:0的虚拟服务,加上-p参数 #因为只有persistent service才能添加端口为0的服务,而我懒得修改ipvsadm代码了 ipvsadm -A -t 0.0.0.0:0 -s rr -p 10 #添加转换后的源地址,这里直接使用添加real server参数,端口为0,如下 ipvsadm -a -t 0.0.0.0:0 -r 121.14.161.100:0 -m #内网访问外网时,源地址就会被改为121.14.161.100
小结:
linux内核模块开发调试跟应用程序的开发还是很不一样的,调试起来要麻烦一些,不过只要有耐心,多查点资料,还是不难搞定的。 在学习研究开源软件时,如果能为其修复一些bug,增加一些功能,对其理解会更加透彻,对今后分析解决相关问题会很有帮助。
建议继续学习:
- LVS hash size解决4096个并发的问题 (阅读:5430)
- LVS & MySQL NDB Cluster (阅读:4053)
- 记一次LVS/Nginx环境下的访问控制 (阅读:3836)
- LVS & MySQL NDB Cluster (阅读:3719)
- 网关协议学习:CGI、FastCGI、WSGI (阅读:3657)
- 利用MySQL Cluster 7.0 + LVS 搭建高可用环境 (阅读:3381)
- linux双网卡双网关,不同IP段的设置 (阅读:3231)
- 怎么样让 LVS 和 realserver 工作在同一台机器上 (阅读:3153)
- Linux 上双网卡单网关设置方法 (阅读:2095)
- NAT网关安装笔记 (阅读:1893)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:李, 剑 来源: UC技术博客
- 标签: LVS SNAT 网关
- 发布时间:2013-10-08 12:38:25
- [56] IOS安全–浅谈关于IOS加固的几种方法
- [55] 图书馆的世界纪录
- [55] 如何拿下简短的域名
- [54] android 开发入门
- [53] Go Reflect 性能
- [53] Oracle MTS模式下 进程地址与会话信
- [50] 【社会化设计】自我(self)部分――欢迎区
- [49] 读书笔记-壹百度:百度十年千倍的29条法则
- [41] 程序员技术练级攻略
- [35] 视觉调整-设计师 vs. 逻辑