Nginx中转Shadowsocks与负载均衡

Nginx中转Shadowsocks与负载均衡

由于TCP/IP网络模型的瘦腰结构,最终所有流量都会通过IP协议传送。因此防火墙的终极大杀器,就是封禁IP。一旦IP被封杀,此服务器便废了。

现在能正常上网的同学不要高兴太早。因为现在的防火墙采用的是黑名单方式,等到哪天逼急了采用白名单方式,那所以翻墙方法都会失效。因此闷声发大财,这是最好的。

另一个瓶颈在于DNS协议。并且现在的DNS污染已经上升到新的高度,即使设置自定义DNS也不能避免。因为除非使用VPN,否则系统的DNS请求全部通过53/UDP端口发送。 DNS请求是未加密的。因此各级网关可以对53/UDP的请求进行针对性的拦截。

运营商对53/UDP端口的拦截已经是公开的秘密了,只不过大多数时候用户是无法感知的。在ping Google.com时,发现竟然能ping通,并且延时只有几毫秒,不是GFW罢工了,也不是网络信号可以超光速了,只是你的DNS被劫持了而已 🙂

有两种解决方案:

  • 挂VPN或者使用类似软件,将DNS请求也经由代理发送。
  • 手工配置Hosts

国内转发的重要性

一个常规的数据包流向:

用户网络环境 ----> 国外VPS ----> 目标国外网站  

其中,第二段链路往往是比较流畅的(如果你不是又回过头来访问国内网站的话), 而第一段链路波动就比较大,尤其不同运营商会有不同表现。可能联通宽带可以很快访问,换移动宽带就很慢,甚至换了手机4G就完全访问不了。 这种速度、可靠性不稳定的访问体验是非常差的。

一个比较好的方案是使用一台国内机器作为中转。

用户网络环境 ----> 国内VPS ----> 国外VPS ----> 国外目标网站  

首先,完全不要担心多了一步中转是否会影响速度(延时),因为本来IP数据包就会经过多次路由转发,添加一台机器中转相当于手动优化了路由规则。 绝大多数情况下,经过中转会降低大大降低数据包从用户端到国外目标网站的传输时间。

像阿里云腾讯云这样的大厂,他们的机房都在主干网上,并且针对国内各个运营商的线路都做了优化。 因此可以保证在新的数据流动路径上,第一段链路在绝大多数情况下都是快速稳定的。

至于第二段链路,国内VPS到国外VPS。由于阿里云等厂商基本都是BGP机房,出国线路也比较快速和稳定。 此外,由于是两个机房之间的通信,并且都有公网IP,没有NAT问题。

这三段链路中,第二段和第三段链路的带宽都不是瓶颈,因为阿里云可以选择按流量计费然后带宽选择100Mb,国外流量更加便宜大碗,VPS基本上都是1Gb带宽。 所以整条链路带宽瓶颈就是用户的宽带带宽。

可以看到,经过国内中转,访问国外网站的瓶颈得到了消除,和访问国内网站无异,看YouTube比看Youku流畅完全是意料之中的事情(或许因为优酷实在是太烂了。。)

负载均衡

不过这样也有一个问题,你会发现整条链路是单链的,其中最脆弱的一环依然在国内到国外这一段,因为GFW只建在国境线上。 一旦IP暴露,被GFW封锁,则这台机器就废了,必须换一台。

对于自建的VPS,闷声发大财,往往没有这个问题。而对于和人共用飞机场的,则很容易被封。

一个比较好的解决方法是在国内的VPS上挂载负载均衡器,后端挂着多台国外VPS,一个IP被封杀则自动切换到下一个。

并且多线程下载时也有加速效果。

中转工具

常用的、也是网上最常搜到的中转方案,是使用iptables和HaProxy 两个工具都有致命缺点:

  • iptables只是简单转发,没有负载均衡功能,并且配置相对不友好。
  • HaProxy是专门的负载均衡器,可惜只支持TCP。虽说大多数时候够用,但很多时候我们确实需要UDP,譬如查询DNS或者玩游戏。

所以这篇文章主要介绍用Nginx来进行流量转发和负载均衡,它支持UDP这一点弥补了HaProxy的不足。 并且Nginx支持插件扩展,可以实现更丰富的负载均衡功能。

建议使用docker来部署Nginx,优点是快捷方便,且适合有洁癖的同学。 不过缺点是不方便自定义Nginx模块。

配置文件如下:

user  nginx;  worker_processes  1;    events {      worker_connections  1024;  }    stream {        upstream group1 {          hash $remote_addr consistent;          server xxx.xxx.xxx.xxx:xxx;     # ip:port          server xxx.xxx.xxx.xxx:xxx;     # ip:port      }        upstream group2 {          hash $remote_addr consistent;          server xxx.xxx.xxx.xxx:xxx;     # ip:port          server xxx.xxx.xxx.xxx:xxx;     # ip:port      }        server {          listen 443;          listen 443 udp;          proxy_pass group1;      }        server {          listen 444;          listen 444 udp;          proxy_pass group2;      }    }  

没必要做过多解释了,会Nginx的同学自然会配置。“拿来主义者”(非贬义)对Nginx配置参数也没兴趣,能直接用就行。

把自己的服务器地址填在对应位置,然后运行Nginx即可。

建议使用docker + docker-compose部署:

新建docker-compose.yml文件:

version: '2'  services:    nginx_lb:      image: nginx:alpine      ports:       - "443-444:443-444"       - "443-444:443-444/udp"      volumes:        - ./nginx.conf:/etc/nginx/nginx.conf  

执行docker-compose up -d即可。

以及别忘了在ECS控制台配置安全组规则,打开Nginx监听的端口(此处为443-444, udp+tcp端口)。

Nginx还有很多有趣的负载均衡策略,可以自己去配置。

不过这里我推荐使用ip_hash的方式,即根据客户端ip来选择转发机器。 这样的好处是同一个用户始终使用同一个代理,不会导致出口地址跳跃继而触发一些网站的风控。

顺便说一句,因为同时支持tcp+udp,因此也可以用来中转VPN流量。