巧用 /etc/hosts 解决跨云 MongoDB 副本集浪费公网流量
起因
事情起因是这样的:作为一名垃圾佬,手里有好几家云厂商的(低价)服务器,之前在阿里云广州已经部署了一个 MongoDB 副本集,现在想在腾讯云广州新增一个节点,于是按照 MongoDB 的官方文档执行
rs.add("xxx.xxx.xxx.xxx:27017")
一开始以为只需要在阿里云的防火墙安全组放通腾讯云访问、腾讯云安全组放通阿里云访问,结果折腾半天发现新节点一直处在 STARTUP
状态不会进入 SECONDARY
,很纳闷。
后来用 telnet 发现不管是在阿里云还是腾讯云的机器上访问自身公网 IP:27017
也不通的,遂在各自防火墙上放通自己访问自己 27017 端口,新节点一下子就成功加入副本集了。
反思
查了一下文档,原来 MongoDB 副本集节点在加入和运行时还会尝试访问自己。而 rs.add 的时候我使用的是公网 IP,在没有设置云安全组规则之前,本机是无法通过公网 IP 连接上自己的,自然也就无法顺利加入副本集。
我一开始的误解是,自己访问自己的 IP,有点类似于访问 127.0.0.1
这个环回地址。因为以前在家庭宽带内网测试过访问公网 IP:80
能连上(但在其他网络访问则被运营商屏蔽了 80 端口),后来研究发现这通常是由于路由器支持 NAT 回环(NAT Loopback / Hairpin NAT) 功能,内网的流量在路由器直接转发而没有离开 WAN 口。所以,我带着这种「访问自身公网 IP 约等于环回」的刻板印象,忽略了安全组的放通规则。
只是这样?
但问题来了,如果说安全组放通自己就能访问,是不是意味着自己访问自己的流量也会被计入公网流量?那这样岂不是浪费了(毕竟腾讯云这台机器是大带宽但限流量),不妥!不妥!
那有解决方案吗?
有!常见的思路是组个 VLAN,让它们通过内网地址访问就行,这样就只有跨云的流量是计费的,访问自身的流量就不会计费了。
但是跨云的 VLAN 价格还是挺高的。那换 Tailscale
或者 n2n
这类 SD-WAN
或 P2P VPN
就行了!
更巧的方案
考虑到搭 Headscale
(Tailscale
的开源替代)还是略麻烦的,然后在翻 n2n
文档的时候突然灵光一闪!
如果我把节点由 IP 换成域名(MongoDB 副本集支持域名访问),然后把指向自己的域名解析到 127.0.0.1
不就规避了访问公网吗!而且还不需要引入别的工具,更简单更稳定。
于是我定了两个域名:mongo.aliyun-gz.internal
和 mongo.qcloud-gz.internal
分别指向阿里云和腾讯云,然后改 /etc/hosts
:
修改阿里云机器上的 /etc/hosts
:
127.0.0.1 mongo.aliyun-gz.internal # 本机访问自己时走环回
QCLOUD_PUBLIC_IP mongo.qcloud-gz.internal # 访问腾讯云节点时走公网
修改腾讯云机器上的 /etc/hosts
:
127.0.0.1 mongo.qcloud-gz.internal # 本机访问自己时走环回
ALIYUN_PUBLIC_IP mongo.aliyun-gz.internal # 访问阿里云节点时走公网
然后修改 MongoDB 的节点 host 为域名并应用,问题解决!
当然,这个解决方案也有个缺点,就是需要手动改 hosts
,如果有内网自建的 DNS 会好一些,但我的服务器架构目前还是非常简单的,先这样用着就行。
这个方案也适用于其他组集群的应用/中间件 :-P