1. 多云网络的挑战与目标拓扑
一句话总结: 多云网络的第一个难点不是「连不通」,而是「地址重叠加拓扑失控」,先用不重叠的 IP 规划与明确的集中式拓扑定调,再谈具体云厂商的资源。
单个云内部的网络编排已经够复杂,一旦跨到两个或三个云,问题会成倍放大:每个云都有自己的 VPC 或 VNet 概念、自己的路由模型、自己的对等语义,而它们之间的中间地带——专线、VPN、DNS、出口——恰恰是 Terraform 最需要抽象的地方。先看多云网络的四类典型挑战。
| 挑战 | 典型表现 | 应对方向 |
|---|---|---|
| 地址重叠 | 两个云都用 10.0.0.0/16 | 提前做 IPAM 规划 |
| 拓扑失控 | 每加一个云就多一对对等 | 集中式中转或网状规划 |
| 路由不透明 | 不知道包走了哪条路 | 统一可观测与路由审计 |
| 出口分散 | 每个云各自出公网 | 出口收敛到统一 NAT |
1.1 IP 规划与网段不重叠
一句话总结: 多云 IP 规划的核心是「一次分配、全局唯一、留足余量」,用超网段给每个云划一块,再在云内二次切分子网。
最痛的多云事故几乎都源于地址重叠:AWS 用 10.0.0.0/16,Azure 也用 10.0.0.0/16,等到要打通时才发现无法加路由。正确做法是自上而下分配。
10.0.0.0/8 企业私有超网
10.10.0.0/16 AWS 生产(ap-northeast-1)
10.20.0.0/16 AWS 灾备(ap-southeast-1)
10.30.0.0/16 Azure 生产(japaneast)
10.40.0.0/16 GCP 生产(asia-northeast1)
10.50.0.0/16 本地数据中心
1.2 集中式与网状拓扑
一句话总结: 两三个云可以用两两对等即网状,超过三个或需要统一策略时应改为集中式——由一个 Hub 云承担中转,其余云都只与 Hub 建链。
网状拓扑的优点是路径最短、无单点;缺点是链路数按 N 乘以 N 减一再除以二增长,且策略无处统一。集中式(Hub-and-Spoke)用一个云或一个中转 VPC 做汇聚点,链路数降到 N 减一,代价是 Hub 需要足够的带宽与高可用设计。实践中常见的做法是:先网状起步,规模到三个云以上时把其中一个升级为 Hub,其余链路逐步改接 Hub。
2. AWS VPC 与对等连接
一句话总结: AWS 侧多云网络的基座是 VPC 加子网加路由表,VPC Peering 负责点对点打通,跨账号 Peering 必须由对端接受,且 Peering 不具备传递性。
AWS 的 VPC 是区域级资源,子网绑定可用区,路由表决定子网的出向路径。跨云连接在 AWS 这一端最终都表现为「VPC 上多了一条指向某个网关或对等连接的路由」。
resource "aws_vpc" "prod" {
cidr_block = "10.10.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "vpc-prod"
Env = "prod"
}
}
resource "aws_subnet" "prod_private" {
count = 2
vpc_id = aws_vpc.prod.id
cidr_block = cidrsubnet(aws_vpc.prod.cidr_block, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "subnet-prod-private-${count.index}"
}
}
2.1 同账号与跨账号 Peering
一句话总结: 同账号 Peering 可以 auto_accept 一步到位;跨账号 Peering 必须由接受方在自己的 Terraform 里执行 accept,且两侧都要各自加一条路由。
resource "aws_vpc_peering_connection" "prod_to_shared" {
vpc_id = aws_vpc.prod.id
peer_vpc_id = aws_vpc.shared.id
auto_accept = true
}
resource "aws_route" "prod_to_shared" {
route_table_id = aws_route_table.prod_private.id
destination_cidr_block = aws_vpc.shared.cidr_block
vpc_peering_connection_id = aws_vpc_peering_connection.prod_to_shared.id
}
跨账号时改为 peer_owner_id 加 peer_region,接受方执行接受动作,接受后两侧都补路由。
resource "aws_vpc_peering_connection_accepter" "peer" {
provider = aws.peer_account
vpc_peering_connection_id = aws_vpc_peering_connection.cross.id
auto_accept = true
}
需要提醒的是,Peering 不传递:A 与 B 对等、B 与 C 对等,并不代表 A 能到 C,跨云场景下这种非传递性会显著放大路由条目数量。
3. Azure VNet 与 GCP VPC 对等
一句话总结: Azure 的 VNet Peering 与 GCP 的 VPC Peering 语义相似但细节不同,Azure 需要显式开关转发流量与网关传递,GCP 则要单独开启自定义路由的导入导出。
Azure 侧的核心对象是资源组里的 VNet 与子网,Peering 是 VNet 级资源且双向独立配置;GCP 侧的核心是全局 VPC 与区域子网,Peering 同样双向。
3.1 Azure VNet Peering
一句话总结: Azure 每个方向的 Peering 都要单独建资源,allow_gateway_transit 只在持有网关的一侧开,allow_forwarded_traffic 让 Hub 能代为转发。
resource "azurerm_virtual_network" "hub" {
name = "vnet-hub"
address_space = ["10.30.0.0/16"]
location = azurerm_resource_group.net.location
resource_group_name = azurerm_resource_group.net.name
}
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "hub-to-spoke"
resource_group_name = azurerm_resource_group.net.name
virtual_network_name = azurerm_virtual_network.hub.name
remote_virtual_network_id = azurerm_virtual_network.spoke.id
allow_virtual_network_access = true
allow_forwarded_traffic = true
allow_gateway_transit = true
}
反向的 Peering 需要单独再写一个资源,并把 use_remote_gateways 打开,这样才能从 Spoke 侧借道 Hub 的网关出去。
3.2 GCP VPC Peering 与共享 VPC
一句话总结: GCP 的 VPC Peering 默认不交换自定义路由,需要显式打开导入导出;共享 VPC 则把子网集中在宿主项目,业务项目通过子网委派消费。
resource "google_compute_network" "shared" {
name = "shared-vpc"
auto_create_subnetworks = false
}
resource "google_compute_network_peering" "app_to_shared" {
name = "app-to-shared"
network = google_compute_network.app.self_link
peer_network = google_compute_network.shared.self_link
export_custom_routes = true
import_custom_routes = true
}
共享 VPC 场景下,宿主项目用 google_compute_shared_vpc_host_project 声明,业务项目用 google_compute_shared_vpc_service_project 加入,再按子网授予 roles/compute.networkUser。
4. 跨云专线与 VPN 互联
一句话总结: 跨云互联有两条主线——IPsec VPN 走公网加密、成本低但抖动大,专线走私有链路、稳定但周期长,生产环境通常两者并存做冗余。
跨云连通的本质是把两个云的私有网络接到同一张路由域里。VPN 用公网加隧道快速打通,专线则通过运营商或云厂商的互连产品提供确定性带宽与低延迟。
4.1 各云专线产品对照
一句话总结: AWS Direct Connect、Azure ExpressRoute、GCP Cloud Interconnect 解决的是同一个问题,差异主要在开通方式、BGP 会话模型与计费粒度。
resource "aws_dx_connection" "main" {
name = "dx-to-colocation"
bandwidth = "1Gbps"
location = "EqTY-2"
}
resource "aws_dx_private_virtual_interface" "main" {
connection_id = aws_dx_connection.main.id
name = "vif-to-onprem"
vlan_id = 4094
address_family = "ipv4"
bgp_asn = 65000
virtual_gateway_id = aws_vpn_gateway.main.id
}
Azure 侧对应 azurerm_express_route_circuit 加 azurerm_express_route_circuit_peering,GCP 侧对应 google_compute_interconnect_attachment 加 google_compute_router_peer。
4.2 IPsec VPN 与 BGP 路由交换
一句话总结: VPN 连接建立后,靠 BGP 会话交换路由,用 AS Path 与 local_pref 控制主备,Terraform 里通过 tunnel 参数与 BGP 配置表达。
resource "aws_customer_gateway" "onprem" {
bgp_asn = 65000
ip_address = var.onprem_public_ip
type = "ipsec.1"
}
resource "aws_vpn_connection" "main" {
customer_gateway_id = aws_customer_gateway.onprem.id
transit_gateway_id = aws_ec2_transit_gateway.hub.id
type = "ipsec.1"
static_routes_only = false
tunnel1_ike_versions = ["IKEv2"]
}
路由交换的关键在于两侧 ASN 不能冲突,且宣告的前缀必须与实际网段一致,否则会出现「隧道通了但业务不通」的典型现象。
5. DNS 与统一出口
一句话总结: 跨云互通之后,服务发现与出网收敛是紧接着的两个问题,私有托管区解决名字解析,统一 NAT 出口解决合规与可观测。
网络打通只完成了「能到」,接下来要让应用「找得到」和「出得去」。
5.1 私有托管区与解析规则
一句话总结: 每个云各有自己的私有 DNS 产品,统一做法是把同一套内部域名在各云的私有区里各建一份记录,用 IaC 保证三者一致。
resource "aws_route53_zone" "internal" {
name = "internal.example.com"
vpc {
vpc_id = aws_vpc.prod.id
}
}
resource "azurerm_private_dns_zone" "internal" {
name = "internal.example.com"
resource_group_name = azurerm_resource_group.net.name
}
resource "google_dns_managed_zone" "internal" {
name = "internal-zone"
dns_name = "internal.example.com."
visibility = "private"
}
如果各云支持条件转发或 DNS 对等,可以只保留一份权威记录,其余云配置转发规则指向它,减少不一致。
5.2 NAT 与出口收敛
一句话总结: 出口收敛的做法是在 Hub 云集中部署 NAT 网关,其余云的默认路由指向 Hub,好处是白名单与审计只需维护一处。
resource "aws_nat_gateway" "hub" {
allocation_id = aws_eip.hub.id
subnet_id = aws_subnet.hub_public.id
tags = {
Name = "nat-hub"
}
}
resource "aws_route" "spoke_default" {
route_table_id = aws_route_table.spoke_private.id
destination_cidr_block = "0.0.0.0/0"
transit_gateway_id = aws_ec2_transit_gateway.hub.id
}
集中出口的代价是 Hub 成为瓶颈与单点,需要多可用区部署 NAT 网关,并预留足够的弹性 IP 与带宽。
6. 统一网络模块设计
一句话总结: 统一网络模块的价值不是「少写几行 HCL」,而是把多云网络的拓扑决策固化成接口,让业务侧只关心「我要一个网络」而不是「AWS 还是 Azure」。
模块设计的第一原则是接口面向业务语义,而不是面向某家云的资源名。
6.1 模块输入输出与按云分支
一句话总结: 用一份变量定义网络意图,在模块内部按 cloud_provider 分支调用不同云的资源,输出统一的网络标识供上层消费。
variable "cloud_provider" {
description = "目标云厂商:aws、azure 或 gcp"
type = string
validation {
condition = contains(["aws", "azure", "gcp"], var.cloud_provider)
error_message = "cloud_provider 只能是 aws、azure 或 gcp"
}
}
variable "network_cidr" {
description = "本网络使用的 CIDR,必须来自统一 IPAM 分配"
type = string
}
output "network_id" {
description = "统一网络标识,屏蔽各云差异"
value = (
var.cloud_provider == "aws" ? aws_vpc.this[0].id :
var.cloud_provider == "azure" ? azurerm_virtual_network.this[0].id :
google_compute_network.this[0].id
)
}
模块内部用 count 或 for_each 控制各云资源是否创建,避免在 AWS 环境里出现 Azure 的 provider 依赖。
6.2 命名与标签规范
一句话总结: 多云环境下命名规范必须跨云统一,因为标签体系各不相同,只有命名是三家都支持的共同语言。
| 规范项 | 约定 | 说明 |
|---|---|---|
| 网络名 | net-用途-环境-区域 | 跨云完全一致 |
| 子网名 | snet-用途-环境-序号 | 便于对照 |
| 标签键 | env、owner、cost-center | 三家通用键 |
| 云专有标签 | 映射到各家原生字段 | 由模块内部翻译 |
模块内部把统一的 common_tags 映射到 AWS 的 tags、Azure 的 tags 与 GCP 的 labels,业务侧只维护一份键值,避免三家各写一遍。
7. 可观测与排错
一句话总结: 多云网络的排错难点在于「没有一张全局视图」,只能靠 Flow Logs 加路由核对加连通性探测三段式方法逐步定位。
跨云问题往往在「我的云能到、你的云没回包」之间来回踢皮球,因此每一段都要有独立的证据。
7.1 Flow Logs 与路由验证
一句话总结: 三家的流日志产品都支持把连接记录投递到对象存储,用 Terraform 统一开启,再配合路由表导出做交叉比对。
resource "aws_flow_log" "vpc" {
log_destination = aws_s3_bucket.flow_logs.arn
log_destination_type = "s3"
traffic_type = "ALL"
vpc_id = aws_vpc.prod.id
}
resource "azurerm_network_watcher_flow_log" "vnet" {
name = "flowlog-vnet"
network_watcher_name = azurerm_network_watcher.main.name
resource_group_name = azurerm_resource_group.net.name
target_resource_id = azurerm_virtual_network.hub.id
storage_account_id = azurerm_storage_account.logs.id
enabled = true
}
aws ec2 describe-route-tables --query "RouteTables[].Routes" \
--filters Name=vpc-id,Values=vpc-0abc123
az network vnet peering list -g rg-net --vnet-name vnet-hub -o table
gcloud compute routes list --filter="network:shared-vpc"
7.2 MTU 与重叠网段排查
一句话总结: 隧道封装会吃掉 MTU,跨云链路出现「小包通、大包断」几乎一定是 MTU 问题;网段重叠则表现为路由加不上或加上了也不生效。
排查清单如下:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 小包通大包断 | 隧道封装压低了 MTU | 调低接口 MTU 或开启 MSS 钳制 |
| 路由加不上 | 两侧网段重叠 | 回到 IPAM 重新分配 |
| 单向不通 | 一侧缺回程路由 | 补齐对端路由表 |
| 间歇性超时 | BGP 会话抖动 | 检查链路质量与 ASN 配置 |
8. 总结
| 环节 | 要点 |
|---|---|
| IP 规划 | 自上而下分配,全局唯一且留足余量 |
| 拓扑选型 | 两三个云网状,三个以上转集中式 Hub |
| 对等连接 | Peering 不传递,跨账号需对端接受 |
| 专线与 VPN | VPN 快速打通,专线保稳定,双链路冗余 |
| DNS 与出口 | 私有托管区统一命名,NAT 集中收敛出口 |
| 模块设计 | 接口面向业务语义,内部按云分支 |
| 可观测 | Flow Logs 加路由核对加连通性探测 |
| 排错 | 先看 MTU 与网段,再看单向回程路由 |
多云网络最难的部分从来不是某一家云的资源写法,而是把三套语义不同的网络模型收敛到一套拓扑与一套命名上。Terraform 在这里的作用是把「拓扑决策」变成「模块接口」,让新增一个云从「重新设计一遍网络」变成「换一个 provider 参数」。下一篇文章会把视角从网络转向算力,讨论如何用 Terraform 编排 AI 与机器学习基础设施,包括 GPU 节点池、模型存储与推理服务的弹性伸缩。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。