USG上同时实现一线多拨负载均衡与策略路由

首先交代一下场景
作者的USG WAN1口是一条中国电信100M宽带,可以四拨,叠加到400M,WAN2是一条国外专线。
需求1:实现多拨,最大限度的薅羊毛。
需求2:实现策略路由,让国外流量走WAN2,国内流量走WAN1多拨的负载均衡。
需求3:如果WAN2不通了,让国外流量走WAN1,如果WAN2通了,自动恢复。

先聊点无关的

总的来说,在USG实现比较麻烦,因为Unifi会推送一些配置,自己写的配置要和Unifi产生的配置兼容。单独拿出以上任何一条需求,作者都可以通过一些tricks,很“优雅”的实现,比如作者的这个帖子说了如何再USG上如何实现策略路由,https://bbs.ui.com.cn/t/usg-wan/47364/1在这个贴子中,作者在USG上实现策略路由时,就大量使用了空字符串去覆盖Unifi产生的配置,然后重写json配置,在USG上实现的策略路由。
再比如这个帖子,实现了USG的高性能一线多拨,https://bbs.ui.com.cn/t/usg-edgemax-er-lite/47810/1 作者在实现USG的一线多拨时,用了一种trick,在配置文件中插入return,阻止USG执行Unifi推送的rule。
以上,这些tricks,作者认为都是比较“优雅”的实现方式。
当这三个需求合到一块时,作者试图使用一些“优雅”方法的,比如在cli中使用connmark来配置等等,但各种尝试,一旦connmark save,性能骤降,所以,作者放弃“优雅”,直接使用Linux Native Shell来实现,这也是我喜欢ubnt路由的原因,ubnt的系统就是一个debian,ubnt cli搞不定的需求,还有linux native shell搞不定的吗?而ubnt路由还有硬件加速,而这点又比x86 linux路由要强。

开始之前先看结果

先看国内测速:先跑了一遍策略路由,被筛选为国内流量,然后再经过多拨负载均衡,最后结果如图,作者是100M,一线四拨后,无论是带宽和延迟,都很满意,再次证明,USG虽然CPU不咋样,但有让所有的数据使用硬件加速,还是电信级转发效率,这也是cavium这种高端路由芯片的特色,把特定功能做到芯片里。

2018-05-13_15-06-37.png

再看国外测速:同样先跑了一遍策略路由,被筛选为国外流量,然后再通过WAN2出去,作者的国外线路的带宽是200M,已经跑满,延时稍大是因为国外链路。

2018-05-13_15-05-06.png

前提步骤:
1、让USG工作在双WAN模式,WAN2通过static 方式接入,过程过于简单,略过。
2、再实现USG上的WAN1口高性能的多拨,具体方法参照https://bbs.ui.com.cn/t/usg-edgemax-er-lite/47810/1 这个帖子,作者对于eth2的配置没有删掉,可以看到,WAN2(即eth2)的Load Balance等相关配置已经产生,但WAN2的weight被设置为0,WAN1口的四个PPPoE承担了100%的流量。注意:作者以前写过的一个USG策略路由方案,https://bbs.ui.com.cn/t/usg-wan/47364/1在本场景中已经完全不适用了。

首先,说明一下,有一些“优雅”的方法,通过CLI命令是可以实现作者的需求组合的,但作者尝试以后,发现性能下降太严重,所以作者没有继续尝试,如何有新的方法,欢迎讨论。
其次,本文比较难,建议有一些linux基础的童鞋尝试。

开始
先分析一下ubnt是如何通过iptables实现load balance,看图,这是问题的核心:2018-05-13_19-31-44.png
好了,如果你看懂了图中的文字。

接下来的事情就很简单了,简单一个脚本就就可以实现抢先给国外流量打上WAN2的标记。

root@Gateway:~# vi /config/scripts/load-chnroute.sh 
#!/bin/bash

CHNROUTE_RULES=/config/user-data/chnroute/chnroute.txt
CHNROUTE_RULES_CACHE=/config/user-data/chnroute/chnroute.ipset

if  -e $CHNROUTE_RULES_CACHE ]; then
  ipset -! restore < $CHNROUTE_RULES_CACHE
else
  sed -e "s/^/add chnroute &/g" $CHNROUTE_RULES | awk '{print $0} END{print "COMMIT"}' | ipset -R
  ipset save chnroute > $CHNROUTE_RULES_CACHE
fi

iptables -w -t mangle -N CHNROUTE
#这两条注释掉的语句是作者的特殊需求,跟文中实现没什么关系
#iptables -w -t mangle -A CHNROUTE ! -p tcp -j RETURN
#iptables -w -t mangle -A CHNROUTE -s 192.168.0.254/32 -j RETURN
iptables -w -t mangle -A CHNROUTE -m set --match-set chnroute dst -j RETURN
#在作者的USG上,WAN2口会被标记为0x64800000/0x7f800000,这个值并不会适用其他USG,懂的人能找到自己的值,不懂的人不建议尝试。
iptables -w -t mangle -A CHNROUTE -j MARK --set-xmark 0x64800000/0x7f800000
iptables -w -t mangle -A CHNROUTE -j RETURN
sleep 60s
if  -z "`iptables -w -t mangle -S | grep '0x0/0x7f800000 -j CHNROUTE'`" ]; then
  iptables -w -t mangle -I UBNT_WLB_wan_failover 3 -m state --state NEW -m mark --mark 0x0/0x7f800000 -j CHNROUTE
fi

到这,基本上就实现了
需求1:实现多拨,最大限度的薅羊毛。
需求2:实现策略路由,让国外流量走WAN2,国内流量走WAN1多拨的负载均衡。

接下来实现需求3,需求3:如果WAN2不通了,让国外流量走WAN1,如果WAN2通了,自动恢复。
首先,需要一种检测WAN2通和不通的办法,作者的国外链路只能走TCP协议,不能走ICMP协议,也就说,不能通过ping bbs.ubnt.com.cn来检测WAN2的状态。
所以,需要自己写一个脚本,来判断,WAN2是不是通的,脚本如下,不解释:

root@Gateway:~# vi /config/scripts/check-eth2.sh 
#!/bin/bash
curl --connect-timeout 10 -x socks5h://192.168.0.254:23456 http://bbs.ubnt.com.cn >/dev/null 2>&1
exit $?

然后再load balance中使用这个脚本检测WAN2


root@Gateway# show load-balance group wan_failover interface eth2            
 route-test {
   count {
   failure 10
   }
   initial-delay 20
   interval 30
   type {
   script /config/scripts/check-eth2.sh
   }
 }

接下来,ubnt会定期检查WAN2(其实是所有interface)的状态,当WAN2的状态发生变化后,会调用下面这个脚本发送一条alert(通过配置load balance中transition-script /config/scripts/wan-event-report.sh实现,这是unifi推送过来的配置,已经配好了),在这个脚本中,只要插入一小段shell命令,当WAN2不通,删掉原理分中自己的那条iptable,当WAN2通了时,又重新插入回去就可以了。

root@Gateway:~# vi /config/scripts/wan-event-report.sh 
#!/bin/bash

# File: wan-event-report.sh
# Desc: Reports WAN transistion events to the controller. This script should be configured under the
#       "load-balance group <GROUP_NAME> transition-script" config node. If the controller reporting
#       script is not found, logs to syslog

REPORT_SCRIPT=/usr/bin/mca-custom-alert.sh
NAME=wan-event
GROUP=$1
IFACE=$2
STATE=$3
KEY="Group $GROUP"
VALUE="Interface $IFACE to state $STATE"

#===================开始=========================
#如果WAN2不通了,删掉自己加的那条iptable,删前先检查一下有无
if  "$IFACE" = "eth2" -a "$STATE" = "inactive"  ]; then
  if  -n "`iptables -w -t mangle -S | grep '0x0/0x7f800000 -j CHNROUTE'`" ]; then
   iptables -w -t mangle -D UBNT_WLB_wan_failover -m state --state NEW -m mark --mark 0x0/0x7f800000 -j CHNROUTE
  fi
fi
#如果WAN2通了,加上自己的那条iptable,加前先检查一下有无
if  "$IFACE" = "eth2" -a "$STATE" = "active"  ]; then
  if  -z "`iptables -w -t mangle -S | grep '0x0/0x7f800000 -j CHNROUTE'`" ]; then
   iptables -w -t mangle -I UBNT_WLB_wan_failover 3 -m state --state NEW -m mark --mark 0x0/0x7f800000 -j CHNROUTE
  fi
fi
#===================结束========================
if  -f $REPORT_SCRIPT ]; then
   $REPORT_SCRIPT -k "$KEY" -v "$VALUE"
   logger -t $NAME -- "$KEY - $VALUE"
else
   logger -t $NAME -- $REPORT_SCRIPT -k \"$KEY\" -v \"$VALUE\"
fi

结束

1赞

:D手动存档。暂时不玩了。

大侠,小白表示懵逼了,救我,大侠:’(

很好的贴子,值得学习

benjaminlei,您好,参照您一线多拔负载均衡与策略路由的配置文章。进行了设置修改,但始终无法成功。通过抓包分析,也确实把外网流量分到了网关X86软路由上,但客户端上却不能访问,是不是缺少回程路由吗?

cnclg 发表于 2018-9-10 17:39
benjaminlei,您好,参照您一线多拔负载均衡与策略路由的配置文章。进行了设置修改,但始终无法成功。通过抓 …

如果在路由上wan2开启masquerade,x86不需要路由,因为x86收到的地址是路由器wan2的地址,x86自然知道从哪个接口发回去,但如果路由wan2没开masquerade,x86收到的是内网主机地址,自然就不知道怎么发回去了,这个时候才需要指定路由。

上文“地址”是“源地址”

benjaminlei,您好。
我也配置了,eth3 接口的masquade。
其它的如图,Inkedrouting_LI.jpgeth3.pngInkediptables -t mangle -S _LI.png
我的ER-4,单独通过socks代理,上外网没有问题,x86代理应该正常。

benjaminlei,附件是我的配件文件,烦请方便时能帮忙看一下,什么地方配置有问题,非常感谢!config.zip (2.81 KB)