架设支持负载均衡和HA的shadowsocks/pac代理

背景

在前文关于PAC自动代理和ios翻墙中我们讨论了如下几个问题:

  • 如何搭建shadowsocks服务器
  • 如何将socks代理转换为http代理
  • 什么是pac代理
  • 如何提供pac代理及编写pac文件
  • 如何防御端口扫描
  • 如何配置全平台客户端

本篇文章建立在前文的基础上,建议对上述知识点不了解的读者先阅读前文再来阅读本篇。

在本文中我们将讨论如下问题:

  • 使用supervisord管理进程和服务
  • 使用haproxy对shadowsocks server进行负载均衡
  • 使用dns对国内中转服务器进行负载均衡

当我们存在多台shadowsocks服务器时,我们可以考虑进行负载均衡和failover(故障倒换)。在shadowsocks的windows客户端上已经集成了这些功能,但是对于其他版本的客户端默认并没有集成这些功能,本文考虑使用haproxy来实现tcp的负载均衡功能。原理如下:

假设在pac中指定PROXY为192.168.1.1:2222,此2222即为haproxy监听的端口,在此端口上收到http代理数据包时,按照配置将其转发到后端的服务器列表中。由于haproxy工作在OSI模型的第四层,对上层应用和协议透明,故不仅可以用来转发socks/http代理流量,实际可以转发任何tcp、udp、http流量。

以下步骤,假设已拥有三台shadowsocks服务器以及一台国内中转服务器,下文的配置均发生在国内中转服务器上,假设其ip为192.168.1.1。

安装shadowsocks和polipo

具体细节请参考前文关于PAC自动代理和ios翻墙

shadowsocks-libev-2.4.7polipo-2014017 我已经上传到了我的云存储,可从shadowsocks-libev-2polipo下载。

安装shadowsocks-libev

1
2
3
4
5
6
7
$ sudo apt-get install build-essential autoconf libtool libssl-dev
$ wget http://static.chenhd.com/get/shadowsocks-libev-2.4.7.tar
$ tar -xvf shadowsocks-libev-2.4.7.tar
$ cd shadowsocks-libev-2.4.7
$ ./configure
$ make
$ sudo make install

安装Polipo

1
2
3
4
5
$ wget http://static.chenhd.com/get/polipo-20140107.tar.gz
$ tar -xzvf polipo-20140107.tar.gz
$ cd polipo-20140107
$ make
$ sudo cp polipo /usr/local/bin

Polipo配置

对于polipo如何将socks5代理转换为http代理,请参考前文关于PAC自动代理和ios翻墙。在开始配置前,我们需要将Polipo的开机自动启动关闭。

1
2
3
4
5
# centos/rhel
sudo chkconfig polipo off
#ubuntu/Debian
sudo update-rc.d polipo disable

supervisord

Supervisord是用Python实现的一款非常实用的进程管理工具,类似于monit,可以很方便的将非daemon程序在后台启动,并监视该进程的状态,一旦该进程意外退出则重新启动。此外还支持web界面和命令行方式的管理。

安装

1
sudo apt-get install supervisor

默认的配置文件是/etc/supervisor/supervisord.conf,对于简单使用,我们可以修改如下部分:

1
2
3
4
5
6
7
[include]
files = /etc/supervisor/conf.d/*.conf
[inet_http_server]
port = 127.0.0.1:9001
username = root
password = admin

第一个部分include模板中,表示引用位于/etc/supervisor/conf.d/目录下所有后缀名是conf的文件到该配置文件中。
第二个部分inet_http_server是配置web管理服务的设定,包括监听端口、HTTP basic认证的用户名和密码。

配置

假设我们有三台shadowsocks服务器A、B、C,根据前文所述,我们需要针对A、B、C分别启动三个ss-local进程和三个polipo进程。这里以A服务器为例,B和C类似。

1
2
3
cd /etc/supervisor/conf.d/
touch ss-A.conf
touch polipo-A.conf

其中ss-A.conf的内容如下:

1
2
3
4
5
[program:shadowsocks-A]
command=/usr/local/bin/ss-local -s my-shadowsocks-A.tyr.gift -p 8080 -l 7001 -k myshadowsocks -m aes-256-cfb -n 51200 -b 0.0.0.0 -A -a ssuser
autorestart=true
stdout_logfile=/var/log/supervisor/shadowsocks-A.log
stderr_logfile=/var/log/supervisor/shadowsocks-A.err

第一行是supervisord记录使用的进程标识,需要每一个进程都不相同。
第二行command为需要启动和监控的进程,也即命令,这里是shadowsocks客户端进程。
第三行autorestart是指当监控到进程退出时,自动重启进程。
最后两行配置了标准正常输出和错误输出的日志文件。

polipo-A.conf的内容如下:

1
2
3
4
5
[program:polipo-A]
command= polipo -c /etc/polipo/config_A pidFile=/var/run/polipo/polipo_A.pid logFile=/var/log/polipo/polipo_A.log forbiddenFile=/etc/polipo/forbidden
autorestart=true
stdout_logfile=/var/log/supervisor/polipo-A.log
stderr_logfile=/var/log/supervisor/polipo-A.err

各部分相应修改,在第二行command中,启动polipo时手动指定配置文件config_A,后面针对B、C时需要手动指定配置文件config_B和config_C。

针对shadowsocks-A服务器的配置到此完成,B和C服务器类似,只需在该目录下新建ss-B.conf/polipo-B.confss-C.conf/polipo-C.conf即可。再分别相应修改文件中的各部分。对于polipo,各服务器的配置文件也要相应修改。

配置完成后,执行sudo supervisorctl update命令就可以启动A、B、C的ss和polipo进程了。supervisorctl是supervisor提供的命令行界面,使用命令sudo supervisorctl进入后,可以输入help查看支持的命令,经常使用的命令有updaterestartreload等,这里不再详细介绍。

启动后,建议先分别测试一下A、B、C服务器的http代理是否已经可以正常工作,再进行下面部分的操作。

Haproxy

对于Haproxy前文haproxy+keepalive构建高可用负载均衡有介绍过,这里不再重复介绍。读者需要了解的是,haproxy和LVS一样,都是一种四层负载均衡方案。haproxy比LVS简单、方便部署一些,故本文选用haproxy。关于LVS本站也曾介绍过,可参考LVS集群架构及部署,confd+zookeeper实现LVS节点发现

安装

下载haproxy

1
2
3
4
5
wget http://static.chenhd.com/get/haproxy-1.6.5.tar
tar -xvf haproxy-1.6.5.tar
cd haproxy-1.6.5
make TARGET=linux26
make install

配置

提供haproxy的配置文件haproxy.cfg内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
global
log 127.0.0.1 local0 #[日志输出配置,所有日志都记录在本机,通过local0输出]
log 127.0.0.1 local1 notice #定义haproxy 日志级别[error warringinfo debug]
daemon #以后台形式运行harpoxy
nbproc 2 #设置进程数量
pidfile /var/run/haproxy.pid
ulimit-n 51200 #ulimit 的数量限制
maxconn 8192 #默认最大连接数,需考虑ulimit-n限制
#chroot /usr/local/haproxy
defaults
log global
mode tcp #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
retries 3 #两次连接失败就认为是服务器不可用,也可以通过后面设置
option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
option redispatch
maxconn 8192 #默认的最大连接数
timeout connect 5000ms #连接超时
timeout client 30000ms #客户端超时
timeout server 30000ms #服务器超时
balance roundrobin #设置默认负载均衡方式,轮询方式
#balance source #设置默认负载均衡方式,类似于nginx的ip_hash
#balnace leastconn #设置默认负载均衡方式,最小连接数
listen admin_stats
bind 0.0.0.0:1111 #设置Frontend和Backend的组合体,监控组的名称,按需要自定义名称
mode http #http的7层模式
option httplog #采用http日志格式
maxconn 10 #默认的最大连接数
stats refresh 30s #统计页面自动刷新时间
stats uri /haproxy #统计页面url
stats realm Haproxy #统计页面密码框上提示文本
stats auth admin:root #设置监控页面的用户和密码:admin,可以设置多个用户名
stats hide-version #隐藏统计页面上HAProxy的版本信息
stats admin if TRUE #设置手工启动/禁用,后端服务器(haproxy-1.4.9以后版本)
frontend ss-in
bind *:2222
default_backend ss-out
backend ss-out
mode tcp
balance roundrobin
option tcplog
#option allbackups
server polipo-A 127.0.0.1:7001 weight 10 maxconn 8192 check inter 1500 rise 3 fall 3
server polipo-B 127.0.0.1:7002 weight 10 maxconn 8192 check inter 1500 rise 3 fall 3
server polipo-C 127.0.0.1:7003 weight 10 check backup inter 1500 rise 3 fall 3

上述配置文件指明,haproxy监听2222端口的TCP连接,并将其按照roundrobin策略(即轮询)分发给后端的polipo-A和polipo-B服务器的7001和7002端口,权重都为10,且做了存活检查,间隔为1500ms,如果存活检查3次都失败,则判断该服务器宕机,从后端服务器转发列表中剔除。当宕机之后如果存活检查3次都成功才认为该服务器恢复正常,并加入后端服务器转发列表中。同时指定polipo-C服务器作为backup服务器,正常情况下不走流量,只在polipo-A和polipo-B都宕机时才切换为active状态。

同时配置了统计模块,监听端口为1111,uri为haproxy

配置rsyslog日志

/etc/rsyslog.conf末尾加入以下内容:

1
2
3
4
5
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
local3.* /var/log/haproxy.log
local0.* /var/log/haproxy.log

重启rsyslog服务

1
$ sudo service rsyslog restart

这样我们可以访问/var/log/haproxy.log来获得日志信息。记录如图:
haproxy-log

运行

1
sudo haproxy -f /etc/haproxy.cfg

将haproxy加入开机启动:

1
2
3
vim /etc/rc.local
#在exit 0 之前加入
haproxy -f /etc/haproxy.cfg

至此haproxy就配置完成了。在pac中指定PROXY为该服务器的2222端口,即完成了中转服务器上shadowsocks的负载均衡。
同时,我们可以访问这台服务器的1111端口,来获得统计信息,在浏览器中打开http://192.168.1.1:1111/haproxy,我们既可以获得如图所示的统计信息:

haproxy

这张图十分详细,我们可以看到当前的会话数量、历史最高会话数量、各后端服务器入站、出站的数据包数量和字节数、服务器状态、错误数量等等记录。

总结

整个过程是: PAC-> http代理经过haproxy -> 分发给后端服务(polipo) -> 转发给shadowsocks客户端(ss-local)。
通过Haproxy为我们提供了http代理层面的负载均衡,haproxy支持很多种负载均衡的调度策略,这里仅以最简单的roundrobin作为示例。同时haproxy对于tcp提供了简单的健康检查功能,如果检查到后端服务宕机(判断端口是否可以访问)则将该服务器的流量转移到其他active服务器上。当所有active服务器都宕机时,激活backup服务器使其进入active状态。

dns调度

上述过程讨论了如何对国外的多个shadowsocks服务器进行负载均衡,接下来讨论如果有多个国内中转服务器时,如何进行负载均衡和failover。这部分本文使用DNS进行调度。

现在的大部分DNS服务器都可以按照线路来解析,即如果进行了相应的配置,那么可以使得电信的用户解析 ss.tyr.gift和移动用户解析ss.tyr.gift获得不同的解析结果,这使得我们可以在电信、移动、联通的线路上分别部署国内中转服务器再按照用户的ISP通过DNS进行调度。

本站使用CloudXNS进行解析,示例如下:

dns

注:LINK类型是CloudXNS的私有类型,详情请参考官网介绍。

这里我按照线路对ss.tyr.gift配置了不同的解析结果,这里的ss.tyr.gift即pac文件中PROXY字段指定的域名。

CloudXNS还支持按照地区实现更细粒度的解析,如:

dns

这样,我们还可以按照华东区、华北区、华南区等方式进行调度,这也是CDN工作的原理。

同时,对于同一条dns记录,CloudXNS支持备用IP,如图所示:

dns

这是对一条记录配置了两个ip,其中左边的为主用IP,默认情况下解析结果即为该IP,还是还可以指定一个备用IP。当CloudXNS检测到主用IP宕机时会自动将解析结果改为备用IP,仅当主用IP恢复时,会将解析结果重新修改为主用IP。

为了使用这个智能切换的功能,我们需要对主用IP和备用IP配置监控,即上图中的蓝色小眼睛。监控支持HTTP监控和自定义的TCP端口监控,此外还支持报警等功能。此外,配置了智能监控后,细粒度的线路解析失效将会将解析切换到上一层线路的解析中。

总结

完整总结一下本站所使用的方案:

  1. pac文件中指定PROXY 的域名为ss.tyr.gift,端口为2222;
  2. 用户解析ss.tyr.gift;
  3. 按照ISP:电信、移动、联通;区域:华东、华北、华南会获得不同的解析结果;
  4. 当CloudXNS监测到电信、移动、联通等线路的解析结果宕机时,会自动将解析结果切换到 全网默认 的解析值;当某条线路的主用IP宕机时,会自动切换到该条线路的备用IP。此部分为国内中转服务器的负载均衡和failover;
  5. 当http代理数据包到达某一个国内中转服务器时,首先到达haproxy进程,haproxy进程按照配置的调度策略,选择一台后端服务器接收该数据,数据将到达某个polipo进程,polipo将http代理转换为socks代理后再转发给shadowsocks客户端进程;
  6. 当haproxy的存活检查机制检测到后端active服务器宕机时(对TCP端口的检查),将会把该服务器从后端池中剔除,当检测到恢复工作时再自动加入池中。当所有active服务器都宕机时,激活backup服务器。
如果您觉得这篇文章对您有帮助,不妨支持我一下!