当本地网络通过 Cloud VPN 到达 VPC 时,Compute Engine 服务的目的地通常是虚拟机的内部地址。外部 IPv4 地址并不是可以塞进同一条私有路由的第二个地址。这一领域的多数混乱来自把承担三项工作的三个地址当成可以互换,所以先给你这个数据包用的是哪一个命名。

三个地址,三项工作

在调试其中任何一个之前,先把三个都命名清楚,因为答案会改变你下一步要检查的对象。

AddressJob
Cloud VPN endpointwhere the IPsec peers meet
Internal IPthe address your route and firewall must name
External IPv4the platform's inbound mapping, not a guest address

第一个属于隧道。第二个属于你的路由和防火墙设计。第三个属于入站互联网访问,它是被映射的,而不是被挂载的。对其中一个成立的方案,对另外两个什么也说明不了。

为什么外部地址不是第二块网卡

地址指南是判断 Compute Engine 上内部与外部可达性差异的权威依据。Compute Engine 的外部 IPv4 是映射到实例唯一内部 NIC 上的一对一关系,因此客体系统永远看不到第二块网卡,也看不到那块网卡上的第二个地址。虚拟机在内部报告的仍然是一个主内部地址。

正因如此,一个通过隧道走向 10.24.1.7 的私有客户端,和一个直接指向虚拟机外部地址的客户端,走在不同的路径上。为公网地址开放防火墙规则,不会改变承载隧道流量的 VPC 路由;修改客体路由,也不会移动平台的映射关系。选定你路由所命名的那个地址,然后只检查管辖它的对象。这条纪律能把这套排查路径上的大部分猜测去掉。

用 gcloud 走一遍这条路径

从实例出发,从同一个对象里读出两个地址,这样你就知道文档讲的是哪一个:

gcloud compute instances describe app-1 --zone=europe-west1-b \
  --format="value(networkInterfaces[0].networkIP,networkInterfaces[0].accessConfigs[0].natIP)"

然后确认这条路径真正依赖的路由和权限:

gcloud compute routes list --filter="destRange:10.24.1.0/24"
gcloud compute firewall-rules list --filter="sourceRanges:10.99.0.0/24"
gcloud compute vpn-tunnels describe TUNNEL_NAME --region=europe-west1

读取隧道宣告和学到的网段、工作负载子网的 VPC 路由,以及允许本地源的防火墙规则。在客体上的监听进程还有意义之前,这三者必须先一致。

然后检查客体:服务是否监听在内部地址上,返回路径是否重新经过 VPC?只绑定在公网地址上的监听,即使在云侧每个对象都正确的情况下,也会在这个测试中失败。

公网前缀是另一个问题

以上任何一条都不意味着 VPN 内承载的每个地址都必须是 RFC1918。只要平台支持相应路由、运营方拥有地址授权,远端网络完全可以合法地使用公网空间编号。这是正常的设计选择。

这与把普通的外部 NAT 地址当成另一个内部子网地址不是一回事。一个是你要路由的前缀;另一个是平台对入站访问施加的映射。

外部 IPv6 和专门设计的地址安排各有自己的语义。如果你的方案依赖其中之一,请读平台关于该特性的文档,而不是从 IPv4 的情形外推。IPv4 映射是常见情形,但不是唯一情形,例外各有各的规则。

地址段重叠是另一个问题

私有地址段重叠是人们伸手去抓公网地址的真实原因,而这是错误的杠杆。把目的地改成虚拟机的 NAT 地址并不能解决重叠;它只改变了数据包声称自己要去的地址,却没有定义返回路径如何工作。

要有意识地解决重叠:重新编号、采用受支持的转换设计,或者经由前缀不冲突的网络路由。每一种都是有责任人的设计。NAT 地址不是设计。

把两个字段分开记录

把隧道对端和工作负载目的地记为两个独立的 runbook 字段,各自配上支撑它的路由和防火墙规则。IPsec 隧道起来了,只能说明两端达成了一致;它没有说明工作负载在哪个地址上可达,也没有说明它的私有路由是否完整。

在另一侧用真实客户端验证:解析名称,确认返回的是内部地址,建立连接,并检查返回流量。Compute Engine IP 地址文档 就是上文所用内部与外部区分的参考。

相关文章