我们已经知道 DPVS 是一个基于 DPDK 的高性能负载均衡器,它能够支持 DR、FNAT、NAT 等工作模式。另外支持还能够支持 SNAT 功能,实现原理和 FNAT 很相似,因此也具备高性能的特点。
在实际生产环境中,有些服务器需要通过 SNAT 功能来访问外网服务,可能的需求原因如下:
出于以上的某种原因(可能还有其他别的原因),服务器没有配置公网 IP 地址,默认是不能够出公网访问的。但实际有时会有访问外网的需求,比如,通过 yum 仓库安装软件包,或是调用外部第三方的某个接口来获取数据等等的需求场景,这时 SNAT 系统能够很方便的解决这些问题。据了解,很多公司都会提供 SNAT 服务,可能没有负载均衡服务那么重要的地位,但也属于公司内部底层基础架构的组成部分,大多时候也是必不可少的。
SNAT 最常见也是最经典的实现方式就是通过 Linux 提供的 iptables 功能,操作简单,大概操作方式如下:
# 开启 IPv4 的转发功能
$ echo 1 > /proc/sys/net/ipv4/ip_forward
$ iptables -t nat -A POSTROUTING -d 192.168.1.160/32 -p tcp --dport 9200 -j SNAT --to-source 192.168.1.7;
# 客户端设置默认路由,网关指向 SNAT 设备 IP 地址即可
$ ip route add default via 192.168.1.1 dev eth0
# 注:只描述大概操作步骤和方式。
在实际生产环境中,为了节省大部分服务器出公网时只设置了少量的公网 IP 地址。生产环境中的 SNAT 服务器一般会做成主备模式或 BGP 集群。
网关IP
和 WAN IP
管理,本质上也是 VIP 的形式存在,如果 A 服务器宕机,B 服务器 keepalived 服务感知到后就会在本机上生成相应的 WAN IP
和 网关IP
地址,继续提供 SNAT 服务。这两种方式用来实现 SNAT 功能也比较成熟和稳定,不过由于 iptables 自身的一些问题,如 nf_conntrack 表的限制,以及大量的 iptables 规则导致的查找性能下降,整体来看,iptables 实现方式存在明显的性能瓶颈。在生产环境中,经常会遇到某个业务流量跑高导致的 cpu 告警等问题。
DPVS 本身是一个基于 DPDK 的高性能负载均衡器,比较常用的工作模式是 FNAT,这种方式从运维和管理上比较友好一些,而且它能够充分的利用多核 CPU 的处理能力,同时也做了很多无锁的优化,它的整体性能也是非常强劲。SNAT 功能在实现原理和逻辑流程上,与 FNAT 非常相似,因此也具备高性能的特点。
可以对比 FNAT 与 SNAT 来进行理解:
(一)基本原理说明
先通过下图简单说明 snat 实现的大致原理:
(二)DPVS SNAT 的基础使用操作
这里只罗列在 DPVS 上如何配置 SNAT 服务。
假设设备上有两张网卡,一个外网,一个内网,测试 SNAT 双臂模式:
sapool
选项SNAT 设置如下:
WAN_IP=123.1.2.3 # WAN IP can access Internet.
WAN_PREF=24 # WAN side network prefix length.
GATEWAY=123.1.2.1 # WAN side gateway
LAN_IP=192.168.100.1
LAN_PREF=24
# add WAN-side IP with sapool
./dpip addr add $WAN_IP/$WAN_PREF dev dpdk1 sapool # must add sapool for WAN-side IP
# add LAN-side IP as well as LAN route (generated)
./dpip addr add $LAN_IP/$LAN_PREF dev dpdk0
# add default route for WAN interface
./dpip route add default via $GATEWAY dev dpdk1
MATCH0='proto=tcp,src-range=192.168.100.0-192.168.100.254,oif=dpdk1'
MATCH1='proto=icmp,src-range=192.168.100.0-192.168.100.254,oif=dpdk1'
./ipvsadm -A -s rr -H $MATCH0
./ipvsadm -a -H $MATCH0 -r $WAN_IP:0 -w 100 -J
./ipvsadm -A -s rr -H $MATCH1
./ipvsadm -a -H $MATCH1 -r $WAN_IP:0 -w 100 -J
通过上述配置,就配置了两个 service,分别是 MATCH1 和 MATCH2,这时如果通过 ipvsadm -ln 就能看到 svc 已经生效,这里就不贴展示的内容了。
同样上述命令行实现的配置效果,用 keepalived 的配置方式也同样能够实现:
virtual_server match SNAT1 {
protocol TCP
lb_algo rr
lb_kind SNAT
src-range 192.168.100.0-192.168.100.254
oif dpdk1
real_server 123.1.2.1 0 {
weight 4
}
}
virtual_server match SNAT2 {
protocol ICMP
lb_algo rr
lb_kind SNAT
src-range 192.168.100.1-192.168.100.254
oif dpdk1
iif dpdk0
real_server 123.1.2.1 0 {
weight 4
}
}
通过上述配置,我们可以简单总结到如下的内容:
(三)SNAT 使用时需要考虑的一些问题
SNAT 是爱奇艺团队开发的功能,如果我们要使用的话,需要仔细分析是否与自己的需求一致,能否满足我们的使用方式。
通过上述内容,可以大概了解 SNAT 的基本实现原理,下边通过代码层面来分析其实现细节。主要根据数据包的流向为思路,来分析 inbond 和 outbond 方向的数据包流程。
(一)outbond 方向数据包处理流程
outbond 方向是指数据包从内网服务器 -> DPVS -> 外网服务器(如 baidu.com )
假设内网服务器需要访问外网某个功能接口,请求数据包到达了 DPVS 服务器。数据包从网卡某个队列 queuex 进入后,被 cpux 接收并开始相关的逻辑处理。收包路径大概包括如下:
netif_loop
-> lcore_job_recv_fwd
-> lcore_process_packets
-> netif_deliver_mbuf
netif_deliver_mbuf
函数中根据 packet_type 分别调用相应的 func 函数,func 类型主要包括:ipv4、ipv6、arp 三种类型。这里主要以 IPv4 分析 SNAT 整个过程。ipv4_rcv
,经过 IP 层相关检查和校验处理后,调用 INET_HOOK 来执行相关的钩子函数,这里调用的是 INET_HOOK_PRE_ROUTING
位置钩子,包括:dp_vs_pre_routing
& dp_vs_in
函数,核心代码入口在 dp_vs_in
里边。在 dp_vs_in
中其实调用的是 __dp_vs_in
函数,主逻辑入口在这个函数中体现:
dp_vs_proto_lookup
,确认此数据包是哪种协议类型,如 TCP 或 UDP 等,每种协议的相关处理逻辑不同,后续的相关操作会根据对应协议注册的回调函数来执行。conn_sched
函数进行连接的初始化和调度,TCP 的连接调度函数是 tcp_conn_sched
。dp_vs_service_lookup
函数进行查找,不过这里查找到的 service 类型是 match 方式。如果找不到响应的 svc 就跳出 SNAT 主流程。dp_vs_schedule
进行主调度流程,并创建初始化新的连接 Entry。
svc->scheduler->schedule
函数,从 RS 列表中挑选出一个 RS 作为 destdp_vs_snat_schedule()
函数sa_fetch
根据源地址选择相对应的源 portdp_vs_conn_fill_param
将相关数据赋值到 param 变量中去。dp_vs_conn_new
建立初始化新的连接条目。
dp_vs_conn_alloc
dp_vs_conn_hash
DPVS_CONN_DIR_OUTBOUND
不同于其他模式。state_trans
函数,进行相关协议状态机的转换。xmit_outbound
将数据包发送出去。
neigh_output
发出数据包到此从 SNAT 数据包的接收、处理、转发等过程,完成 SNAT outbond 方向的整个流程。
(二)inbond 方向数据包处理流程
inbound 方向是指数据包从外网服务器(如 baidu.com) -> DPVS -> 内网服务器
内网服务器发送请求数据包后,外网服务器执行相关操作,发出响应数据包,此数据包到达了 DPVS 服务器。此时 DPDK 驱动程序会根据关键字段进行 FDIR 规则匹配,数据包从同样的网卡度列 queuex 进入后,被 cpux 接收并开始相关的逻辑处理,这样整个连接都是被同一个 cpux 来处理的。收包路径大概包括如下:
在 dp_vs_in
前的收包处理流程和 outbond 基本一致,这里就不再罗列。
dp_vs_proto_lookup
确认本次是 TCP 协议xmit_inbound
将数据包发送出去,具体细节与 outbond 很类似。不断的重复 outbond 和 inbond 流程,就能够通过多次数据包的交互完成连接过程中业务的传输,从而实现 SNAT 功能。
(三)SNAT 其他问题
总结先到此为止,在使用过程中遇到的问题还会再来补充更新。