不论我走到哪里,我的网络环境都是一致的。
实现目标
已知:
- 我的服务分散于ABC三地。
- 三地间网络可能会出现一定时间的全面中断。
- 三地间网络传输过程中充满了监听者。
- 三地间部分公网服务不互通,比如A地无法访问B地的某服务。
要求:
- 不论何时何地,访问我的服务应该都具有类似的体验。
- 不论何时何地,访问到的公网资源应该是三地公网服务的总和。
实现思路
CDN。其实就是想要CDN。但是因为已经有了一些其他的沉没成本,因此就直接自己动手搞。
实现细节
实现起来其实就是围绕着CDN思路展开。实现途径主要就是:域名解析,dns定制,nginx分流。物料准备:ABC三地各一台拥有公网ip的机器。(都是钱……)
域名解析
先说域名解析,这个最简单。就是将域名设置不同地区解析到不同ip。
我个人的做法是这样的:
对于xloypaypa.pub这个域名根据地域不同分别解析到a.xloypaypa.pub
,b.xloypaypa.pub
,和c.xloypaypa.pub
。然后再将三个子域名解析到对应的ip。
这样做的理由有两个:
- 方便管理。甚至可以考虑让不同的人管理不同地区的服务。
- 允许强制访问某地区。方便调试,且后面nginx那边显然会用到这个特性。
dns定制
显然为了保证公网服务的体验一致。我需要保证我在abc三地的服务器里的V姓网络工具收到请求后知道到底该往哪里去,而默认各地的解析是按各地的服务走的,因此要统一三地的dns解析。
于是我写了一个dns-relay,已经放在了open infra的主页上了。这个中继会把每次的dns查询分别向三地都查一次,然后根据写好的逻辑,选一个结果返回。举个例子对于bilibili我们采A地的解析结果,对于知名搜索引擎baidu我们采B地的解析结果。这样V姓网络工具再根据目标ip处于何地来决定向何处转发。
就使用上来说,只需要无脑将公网请求发往各地的V姓网络工具,就可以实现各地体验一致。
最后举个例子:
假设,现在处于C地访问A地公网资源。那么访问流程大概如下:
- 将访问请求发往C地V服务。
- C地V服务向C地DNS服务查询DNS。
- C地DNS服务向ABC三地DNS服务器查询当地dns。
- C地DNS服务选择A地DNS结果。如果A地查询失败,则采B或C地的结果。
- C地V服务根据C地DNS服务的结果选择将请求发往A地服务器。
- A地V服务访问公网。
- A地V服务将结果返回给C地V服务。
- C地V服务将结果返回给用户。
![](https://zh.xloypaypa.pub/content/images/2021/02/Notes_210207_003831.jpg)
Nginx分流
上面我们已经保证了公网访问的一致。那么对于自己的服务的一致的方法就变得比较比较显而易见了。简而言之,就是各地都接受服务的访问,然后看情况发往目标地。
我个人的做法是维护一个公共的nginx配置,同时各地再维护一个特例的nginx配置。其中特例配置会覆盖公共配置。
公共的nginx配置需要定义的内容是:某某某服务,转发往A地的域名。比如blog.xloypaypa.pub,这个服务host在B地。公共配置中,server_name ~^blog\..*xloypaypa\.pub$;
的upstream都是blog.b.xloypaypa.pub
。
对于A地和C地的nginx,特例配置里就不需要做任何事情。如果收到请求,那么就直接根据公共配置转发往B地。
对于B地来说,特例配置里需要在特例里添加同样的server_name的配置,并将upstream指向这个服务的ip。这样如果B地收到请求,就会忽略公共配置,而直接访问那个服务。
如果到此为止,就会有个缺陷:就是如果A地与B地网络不通的情形。假设三地之间只有AB两地不通,那么其实A地可以通过C地转发请求进而访问B地管辖的服务。因此对于A地来说,会做类似如下配置:
upstream b.xloypaypa.pub {
server b.xloypaypa.pub:443;
server c.xloypaypa.pub:443 backup;
}
upstream c.xloypaypa.pub {
server c.xloypaypa.pub:443;
server b.xloypaypa.pub:443 backup;
}
也就是说,A地访问B地会优先直接访问,如果失败再采用C地的地址。
但这其实会导致另一个缺陷,那就是AB和CB都不通的时候,AB两地可能会对请求进行无限转发。因此这里只是提供一个简单可用的伪代码,具体重试策略啥的还是要自行取舍(nginx这一块还蛮复杂的)。
大概网络结构就是这样。我觉得还是比较简单,就是贵了一些。但如果本来就有内网穿透的需求的话,其实成本就是沉没成本,就还好。至于扩展性我认为其实还可以,显然是可以很容易新增一个D地。