第一次设计时,所有人都会画同一张图:VPN 进 VPC,NAT gateway 出公网,收工。但当 VPN 终结在 virtual private gateway 上时,AWS 并不支持这条路径,而且失败得足够安静,团队往往要重建不止一次。能跑通的方案需要 transit gateway,这会同时改变两侧的网关和路由表。动手画之前,先比较这两种形态。

看起来理所当然、却不受支持的拓扑

AWS 的NAT gateway 文档画出一条让只做过单 VPC 设计的人意外的一笔:来自 Site-to-Site VPN 连接的流量无法经 virtual private gateway 路由到 NAT gateway。终结 VPN 的网关类型,直接决定转换路径是否存在。这个错误之所以能存活,是因为隧道状态是 UP,而同一子网里的 EC2 实例也能正常上网。这两项观察都没有检验真正坏掉的那个方向。

实际上,报文带着本地网络的源地址到达,却找不到任何受支持的下一跳来做地址转换。取决于路由表,它会在出方向边界被丢弃;而由于隧道保持 UP、同子网实例仍能上网,这个故障常被当成本地防火墙问题上报。

virtual private gateway 与 transit gateway

virtual private gateway 属于单个 VPC。它终结 VPN,把路由写进该 VPC 的路由表,然后就到此为止。它没有 attachment 模型,因此无法把流量交给另一个 VPC,也无法交给共享出口段。这种单 VPC 的覆盖范围就是全部限制,在 VPC 内新增任何路由都改变不了它。

transit gateway 是一个区域枢纽:VPN、每个 spoke VPC 以及共享服务 VPC 都以 attachment 的形式出现在上面。它有自己的路由表,用来决定某个前缀由哪个 attachment 接收。

正是这个枢纽让集中出口成为可能:VPN 作为一个 attachment 落地,出口 VPC 作为另一个落地,transit gateway 路由表把去往公网的前缀从前者送往后者。用 virtual private gateway 时没有第二个 attachment 可送,所以这个设计无法事后补救。

受支持的出口路径,逐跳来看

参考设计有五跳,每一个箭头都是你必须配置的路由决策。流量从本地网络进入 Site-to-Site VPN attachment,transit gateway 路由表把它转发到出口 VPC 的 attachment,再由子网路由表送到公网 NAT gateway。NAT gateway 转换源地址,internet gateway 把报文送出去。

On-premises network
  -> Site-to-Site VPN attachment on the transit gateway
  -> transit gateway route table
  -> egress VPC attachment
  -> public NAT gateway in that VPC
  -> internet gateway

两个细节决定最后几跳是否成立。NAT gateway 必须是公网的,带 elastic IP 和指向 internet gateway 的路由,因为私有 NAT gateway 只在不同网络之间做转换,永远不会成为公网出口。

按你打算服务的每个可用区各规划一个 NAT gateway,并让每张子网路由表指向本区的那一个。在笔记里把 transit gateway 路由表和 VPC 路由表当成两个独立对象,因为一个选 attachment,另一个选 VPC 内部的资源。只在其中一张里配默认路由,会让一半路径没有定义,从而产生最难归因的部分故障。

这些设计恰恰在回程路径上翻车

NAT gateway 对已经见过的流是有状态的,所以出站连接的应答会原路返回,不需要新的入站规则。这个特性并不会把本地服务发布到公网,把它当成能发布,就是一个团队花一整天去调一条根本不会建立的连接的原因。对客户端发起的流量,四条路由必须一致:本地策略、transit gateway 路由表、出口子网路由表以及 NAT 子网路由表。漏掉最后一条,请求离开了大楼,应答却死在 AWS 内部。

用一个真正位于 VPN 之后的客户端验证,检查它呈现给对端的公网源地址。如果那就是 NAT gateway 的 elastic IP,说明转换正在发生;如果是别的地址,你测到的是另一条出口。

这个故障的不对称版本值得尽早识别。当某个可用区的 NAT 子网路由表有回到客户端前缀的路由、而另一个可用区没有时,新建连接会随流量落到哪个区而成功或失败,这种模式常被误读成公网不稳定。与其只修工单所在的那个区,不如比较你服务的每个区的子网路由表,因为缺一条路由就是全部故障,对面完全不需要改动。

什么时候显式代理是更好的设计

在出口 VPC 的实例上跑 HTTP 代理是合理替代方案,而且通常更便宜。客户端被配置为使用它,代理自己向外建立连接,回程路径变成代理的问题,而不是一道对称路由难题。对少量应用来说,这能省掉大部分 transit gateway 工作。它还带来 NAT gateway 无法产生的按用户日志。

但它并不透明。任何忽略代理设置的东西,以及所有非 HTTP 协议,都不会走这条路。

选择取决于应用实际在说什么,以及日志里需要多少身份信息。它们说 HTTP 的地方,代理站得住;需要为任意协议提供通用出口的地方,AWS 支持的是 transit gateway 方案。提前想清楚,比建完路由表再发现要便宜。

相关文章