AWS VPC Peering 配置实战

AWS 上不同 VPC 之间默认完全隔离。VPC Peering 在两个 VPC 之间建立一条专用通道,让双方通过私网 IP 互相访问。

本文从基础概念讲起,通过一个跨 VPC 访问 RDS 超时的真实案例,演示 Peering 的完整配置过程和常见踩坑点。

1. 基础概念速览

把 AWS 网络想象成一片写字楼园区:

  • VPC(Virtual Private Cloud)是一栋独立的大楼。每栋楼有自己的地址段(CIDR),比如 10.42.0.0/16 表示所有房间门牌号都以 10.42 开头。不同大楼之间默认不互通。
  • 子网(Subnet)是大楼里的楼层。每个子网占据 CIDR 的一小段,比如 10.42.10.0/24,并且绑定在某个可用区(AZ)。EC2、ECS 任务、RDS 实例都部署在某个子网里。
  • 安全组(Security Group)是每扇门上的门禁系统。它控制 “ 谁可以从哪个端口进来 “。比如一条规则 tcp/5432 from 10.42.0.0/16 表示:允许来自 10.42 网段的流量访问 PostgreSQL 端口。

1.1 路由表:决定流量往哪走

路由表是整个网络通信的核心,理解它才能理解 Peering 问题。

可以把路由表想象成楼层出口处的路标。每当一个数据包要发出去,系统查路由表,找到匹配的 “ 目的地 “,然后把包送到对应的 “ 下一跳 “:

目的地(Destination)下一跳(Target)含义
10.42.0.0/16local目的地在本 VPC 内,直接内部转发
0.0.0.0/0nat-xxx所有其他流量走 NAT 网关出互联网
10.43.0.0/16pcx-xxx目的地在另一个 VPC,走 Peering 通道

三点需要注意:

  1. 匹配规则是最长前缀优先。发往 10.42.10.5 的包,10.42.0.0/160.0.0.0/0 都能匹配,但 /16/0 更精确,所以走 local
  2. 路由表是单向的。你在 A 的路由表加了 10.43.0.0/16 → pcx-xxx,只解决了 A → B 的去程。B 要回包给 A,B 的路由表也必须有 10.42.0.0/16 → pcx-xxx。双向都要配。
  3. 每个子网绑定一张路由表。同一个 VPC 里不同子网可以有不同的路由表。排查问题时,必须看服务所在子网绑定的是哪张表。

有了这些基础,接下来看 VPC Peering 的工作机制和限制条件。

2. VPC Peering 详解

2.1 工作原理

VPC Peering 在两个 VPC 之间建立一条点对点的网络连接。建立后,两边的资源通过私网 IP 直接通信,就像在同一个网络里一样。

建立 Peering 需要三步:

  1. 创建 Peering 连接(一方发起请求)
  2. 对方接受(同账号下可以自动接受)
  3. 双方都添加路由(最容易遗漏的一步)

2.2 关键限制

  • 不可传递(Non-transitive):A↔B 有 Peering,B↔C 有 Peering,不等于 A↔C 互通。A 要访问 C 必须单独建 A↔C 的 Peering。
  • CIDR 不能重叠:如果两个 VPC 的地址段有重叠(比如都用了 10.0.0.0/16),无法建立 Peering。
  • 同账号、跨账号、跨区域都可以建 Peering,但跨区域的流量费用更高。

2.3 计费

VPC Peering 本身不收费——创建和维持连接都免费。费用只产生在数据传输上:

场景费用
同区域、同可用区(AZ)免费
同区域、跨可用区$0.01/GB(每个方向)
跨区域按跨区域数据传输费计费(双向,费率因区域而异)

对于大多数同区域场景,只要服务和数据库在同一个 AZ,Peering 流量几乎零成本。

理解了原理和限制,下面用一个真实案例演示完整的排查和修复过程。

3. 实战场景

3.1 环境说明

我们有三个 VPC:

1
2
3
coding-infra-dev-vpc   vpc-0a1b2c3d4e5f60001   10.42.0.0/16
coding-infra-prod-vpc vpc-0a1b2c3d4e5f60002 10.43.0.0/16
vis-server-vpc vpc-0a1b2c3d4e5f60003 10.228.0.0/16
  • Dev VPC(10.42.0.0/16):运行 ECS Fargate 服务,需要访问 Prod 数据库
  • Prod VPC(10.43.0.0/16):运行 Prod RDS(PostgreSQL),地址为 10.43.11.179
  • Ops VPC(10.228.0.0/16):运行运维跳板机(10.228.1.232),用于日常运维

已有一条 Peering 连接:

1
2
pcx-0a1b2c3d4e5fa0001
vis-server-vpc (10.228.0.0/16) <-> coding-infra-prod-vpc (10.43.0.0/16)

运维跳板机通过这条 Peering 可以正常访问 Prod RDS。

3.2 问题现象

Dev VPC 中的 ECS 服务连接 Prod RDS 时报错:

1
dial tcp 10.43.11.179:5432: i/o timeout

但从跳板机连接同一个 RDS 完全正常:

1
2
nc -zvw5 coding-infra-prod-my-app-postgres.abcdefgh1234.us-west-2.rds.amazonaws.com 5432
# Connection to ... (10.43.11.179) 5432 port [tcp/postgresql] succeeded!

RDS 本身没有问题,问题出在 Dev VPC 到 Prod VPC 的网络路径上。

3.3 变更前的网络拓扑

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    subgraph dev ["Dev VPC (10.42.0.0/16)"]
        ecs["ECS Fargate"]
    end
    subgraph prod ["Prod VPC (10.43.0.0/16)"]
        rds[("Prod RDS")]
    end
    subgraph ops ["Ops VPC (10.228.0.0/16)"]
        jump["运维跳板机"]
    end
    ops <--->|"Peering pcx-...a0001 ✅"| prod
    dev -. "❌ 无 Peering,超时" .-> prod

接下来逐层排查,找出问题根因。

4. 问题分析

两个 VPC 之间的通信需要同时满足三个条件:

  1. 两个 VPC 之间存在 Peering 连接
  2. 双方的路由表都有指向 Peering 的路由
  3. 安全组允许对应端口的访问

逐一排查。

4.1 安全组:已放通 ✅

Prod RDS 的安全组(sg-0a1b2c3d4e5f80002)已经包含:

1
2
tcp/5432 from 10.228.0.0/16
tcp/5432 from 10.42.0.0/16

Dev VPC 的网段 10.42.0.0/16 已被放通,安全组不是瓶颈。

4.2 去程不通:Dev 路由表缺少到 Prod 的路由 ❌

ECS 服务所在子网的路由表(rtb-0a1b2c3d4e5f90001):

1
2
10.42.0.0/16 → local
0.0.0.0/0 → nat-0a1b2c3d4e5fb0001

ECS 服务(10.42.10.142)发包到 RDS(10.43.11.179)时:

  1. 查路由表:10.43.11.179 匹配 10.42.0.0/16(local)?❌ 不匹配
  2. 匹配兜底路由 0.0.0.0/0 → 走 NAT 网关 → 出公网
  3. 10.43.11.179 是私网地址,公网上不可达 → 超时

根因:Dev VPC 和 Prod VPC 之间没有 Peering 连接,路由表里自然没有对应的路由条目。

4.3 回程也错:Prod 路由表指向错误的 Peering ❌

Prod RDS 所在子网的路由表(rtb-0a1b2c3d4e5f90002):

1
2
10.42.0.0/16  → pcx-0a1b2c3d4e5fa0001   # ❌ 错误
10.228.0.0/16 → pcx-0a1b2c3d4e5fa0001 # ✅ 正确

pcx-...a0001 是 Ops VPC ↔ Prod VPC 的 Peering。

第二条没问题:Prod 回包给 Ops 跳板机(10.228.x.x),走这条 Peering 正确。

第一条有问题:Prod 回包给 Dev VPC(10.42.x.x),却指向了 Ops↔Prod 这条 Peering。这条 Peering 只连接 10.228.0.0/16 和 10.43.0.0/16,不通向 10.42.0.0/16。

Peering 不可传递——流量不能 Dev → Ops → Prod 这样中转。即使 Prod RDS 收到了请求,回程包也会被丢弃

4.4 小结

去程:Dev 路由表没有到 Prod 的路由,流量走 NAT 出公网后丢失。回程:Prod 路由表中 10.42.0.0/16 指向了错误的 Peering,回程包同样丢失。双向都不通。

修复需要同时解决两个方向的问题。

5. 修复步骤

5.1 创建 Dev ↔ Prod Peering

1
2
3
4
aws ec2 create-vpc-peering-connection \
--vpc-id vpc-0a1b2c3d4e5f60001 \
--peer-vpc-id vpc-0a1b2c3d4e5f60002 \
--tag-specifications 'ResourceType=vpc-peering-connection,Tags=[{Key=Name,Value=dev-to-prod}]'

得到新的 Peering ID:

1
pcx-0a1b2c3d4e5fa0002

5.2 接受 Peering

1
2
aws ec2 accept-vpc-peering-connection \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0002

5.3 开启 DNS 解析

让 Peering 两端可以解析对方的私有 DNS 名称(比如 RDS 端点域名):

1
2
3
4
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0002 \
--requester-peering-connection-options AllowDnsResolutionFromRemoteVpc=true \
--accepter-peering-connection-options AllowDnsResolutionFromRemoteVpc=true

5.4 添加 Dev → Prod 去程路由

在 ECS 服务所在子网的路由表中,添加到 Prod VPC 的路由:

1
2
3
4
aws ec2 create-route \
--route-table-id rtb-0a1b2c3d4e5f90001 \
--destination-cidr-block 10.43.0.0/16 \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0002

5.5 修正 Prod → Dev 回程路由

把 Prod RDS 子网路由表中 10.42.0.0/16 的下一跳,从错误的旧 Peering 改到新建的 Dev↔Prod Peering:

1
2
3
4
aws ec2 replace-route \
--route-table-id rtb-0a1b2c3d4e5f90002 \
--destination-cidr-block 10.42.0.0/16 \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0002

5.6 确认旧链路不受影响

Ops VPC → Prod VPC 的路由保持不变:

1
10.228.0.0/16 → pcx-0a1b2c3d4e5fa0001

运维跳板机访问 Prod RDS 的能力不受影响。

5.7 变更后的网络拓扑

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    subgraph dev ["Dev VPC (10.42.0.0/16)"]
        ecs["ECS Fargate"]
    end
    subgraph prod ["Prod VPC (10.43.0.0/16)"]
        rds[("Prod RDS")]
    end
    subgraph ops ["Ops VPC (10.228.0.0/16)"]
        jump["运维跳板机"]
    end
    ops <--->|"旧 Peering pcx-...a0001 ✅"| prod
    dev <--->|"新 Peering pcx-...a0002 ✅"| prod

变更后的路由表:

Dev 私网路由表(rtb-0a1b2c3d4e5f90001):

1
2
3
10.42.0.0/16 → local
10.43.0.0/16 → pcx-0a1b2c3d4e5fa0002 # 新增
0.0.0.0/0 → nat-0a1b2c3d4e5fb0001

Prod RDS 子网路由表(rtb-0a1b2c3d4e5f90002):

1
2
3
10.42.0.0/16  → pcx-0a1b2c3d4e5fa0002   # 已修正
10.228.0.0/16 → pcx-0a1b2c3d4e5fa0001 # 不变
10.43.0.0/16 → local

路由配置完成后,验证双向链路是否正常。

6. 验证

6.1 连接 Prod RDS

通过 Dev VPC 中的服务连接 Prod RDS,执行查询确认:

1
SELECT current_database(), current_user, inet_server_addr();
1
2
3
 current_database | current_user | inet_server_addr
------------------+--------------+------------------
my_app | app_user | 10.43.11.179

连接成功,目标地址确实是 Prod RDS 10.43.11.179

6.2 确认旧链路正常

从运维跳板机执行:

1
2
nc -zvw5 coding-infra-prod-my-app-postgres.abcdefgh1234.us-west-2.rds.amazonaws.com 5432
# Connection to ... (10.43.11.179) 5432 port [tcp/postgresql] succeeded!

旧 Peering 链路不受影响。

验证通过后,最后准备好回滚方案,确保出问题时能快速恢复。

6.3 回滚方案

如果变更引发问题,按以下顺序回滚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 删除 Dev → Prod 的路由
aws ec2 delete-route \
--route-table-id rtb-0a1b2c3d4e5f90001 \
--destination-cidr-block 10.43.0.0/16

# 2. 恢复 Prod 路由表中 10.42.0.0/16 的旧指向
aws ec2 replace-route \
--route-table-id rtb-0a1b2c3d4e5f90002 \
--destination-cidr-block 10.42.0.0/16 \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0001

# 3. 删除新 Peering
aws ec2 delete-vpc-peering-connection \
--vpc-peering-connection-id pcx-0a1b2c3d4e5fa0002

7. 经验总结

  1. 不要从名字推断网络归属。看到服务在 coding-infra-dev-cluster,不能假设它经过 vis-server-vpc。ECS Fargate 服务的真实网络归属由 awsvpc 模式下的子网决定,必须查任务 ENI 确认。
  2. Peering 不可传递。A↔B 和 B↔C 互通,不代表 A↔C 互通,需要单独建 Peering。
  3. 路由必须双向配。光配了去程路由还不够,回程路由也要指向正确的 Peering。很多 “ 超时 “ 问题的根因就是回程路不通。
  4. 排查跨 VPC 连接失败时的检查清单:
    • Peering 连接是否存在且状态为 active
    • 请求方子网的路由表是否有去程路由
    • 目标方子网的路由表是否有回程路由
    • 安全组是否放通了源网段和目标端口
  5. 善用信号区分故障层次。比如 502 cannot reach database 说明网络层不通。如果变成 400 Bad Request,说明网络已通,只是应用层协议不对。这种变化本身就是有价值的诊断信号。