为私有子网提供出站访问

AWSBeginner
立即练习

简介

配送应用运行在私有子网中,需要访问外部服务,却不需要获得公有地址。你将创建公有 NAT 网关、添加出站路由,并通过实际 HTTP 请求观察源地址转换和对外部主动入站连接的隔离。

请先完成 Control Application Access with Security Groups。本次全新环境独立提供应用、公有和私有子网、公有 Internet 路由及安全规则,CLI 已配置完成。保留这些资源和无关的参考网络;仅创建并最终删除自己的 NAT 网关、弹性 IP 地址及私有默认路由。

认证相关性

本实验为以下考试主题提供实践。

检查私有应用并分配 NAT 地址

本步骤检查预置网络,观察缺失的出站路径,并为即将创建的 NAT 网关分配地址。

使用 Terminal 执行 CLI 命令,在旁边打开 AWS View。该视图读取同一份资源状态。应用使用 private-subnet 中的私有 IPv4 地址 10.20.2.10,并不位于 public-subnet。

cd /home/labex/project

通过 Name 标签选择应用 VPC。--filters 限定结果,--query 选择 ID,$(...) 将结果保存为 shell 变量:

VPC_ID=$(aws ec2 describe-vpcs \
  --filters Name=tag:Name,Values=application-network \
  --query 'Vpcs[0].VpcId' \
  --output text)

选择该 VPC 的公有子网。NAT 网关将在这里获得 Internet 路径:

PUBLIC_SUBNET_ID=$(aws ec2 describe-subnets \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=public-subnet \
  --query 'Subnets[0].SubnetId' \
  --output text)

选择私有子网现有的路由表。你将修改它的默认路由,同时保留既有的子网关联:

PRIVATE_RT_ID=$(aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=private-routes \
  --query 'RouteTables[0].RouteTableId' \
  --output text)

检查两个预置路由表。[]. 后的投影为每个路由表选取便于阅读的字段:

aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" \
  --query 'RouteTables[].{Name:Tags[?Key==`Name`].Value|[0],Routes:Routes,Associations:Associations}' \
  --output json

两个有名称的表都包含 VPC 的 local 路由;还可能出现无名称的主路由表,请保持它不变。有名称的表已显式关联子网,因此应使用选中的 private-routes 表。公有表还将 0.0.0.0/0 路由到 Internet 网关,私有表没有默认路由。私有子网没有直接通向 Internet 网关的路由。公有 NAT 网关允许私有子网中的应用使用网关公有地址主动发起 IPv4 连接。它应位于公有子网中,该子网自身的 Internet 路由已经预置。

在 AWS View 点击 Request outbound service。私有应用的 HTTP 请求返回 Connection failed,因为没有通往外部服务 198.51.100.20:9000 的路由。点击 Request private application from outside;此时主动向 10.20.2.10:80 发起的外部 HTTP 请求也失败。应用仍保留私有地址。

弹性 IP 地址是已分配的公有 IPv4 地址。在 VPC 域中分配一个,并添加标签以标识练习资源。引号中的 --tag-specifications 值作为一个参数描述资源类型及其标签:

ALLOCATION_ID=$(aws ec2 allocate-address \
  --domain vpc \
  --tag-specifications 'ResourceType=elastic-ip,Tags=[{Key=Name,Value=parcel-nat-address},{Key=Project,Value=parcel}]' \
  --query 'AllocationId' \
  --output text)

查询已分配的地址:

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp,Domain:Domain}' \
  --output table

结果包含分配 ID、公有 IPv4 地址和 vpc 域。记下公有地址,后面将与流量结果对比。该地址属于未来的 NAT 网关,不要将其关联到应用。保持此 Terminal 打开,以继续使用保存的 ID。

创建公有 NAT 网关

本步骤在公有子网中创建 NAT 网关,并确认仅创建网关并不能为私有应用配置路由。

NAT 网关需要两个现有资源:公有子网和已分配的弹性 IP。--subnet-id 选择位置,--allocation-id 选择公有地址,--connectivity-type public 选择用于 Internet 出站流量的网关。natgateway 资源类型用于添加归属标签。保存生成的 ID:

NAT_ID=$(aws ec2 create-nat-gateway \
  --subnet-id "$PUBLIC_SUBNET_ID" \
  --allocation-id "$ALLOCATION_ID" \
  --connectivity-type public \
  --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=parcel-nat},{Key=Project,Value=parcel}]' \
  --query 'NatGateway.NatGatewayId' \
  --output text)

创建请求可能在网关就绪前返回。CLI waiter 会反复读取状态,直到满足指定条件。添加路由之前,等待 available:

aws ec2 wait nat-gateway-available --nat-gateway-ids "$NAT_ID"

waiter 成功时没有输出。查询网关状态、子网和地址关联:

aws ec2 describe-nat-gateways \
  --nat-gateway-ids "$NAT_ID" \
  --query 'NatGateways[].{ID:NatGatewayId,State:State,Subnet:SubnetId,Addresses:NatGatewayAddresses}' \
  --output json

State 为 available,Subnet 与公有子网匹配,NatGatewayAddresses 包含你的分配 ID 和公有地址。其 PrivateIp 位于公有子网范围 10.20.1.0/24 内,是 NAT 网关接口的私有地址,与弹性 IP 及应用地址不同。NAT 网关是独立资源;私有应用并未获得该地址。

AWS View 现在显示 NAT 网关。再次点击 Request outbound service。由于私有路由表没有默认路由,请求仍失败。创建网关只是提供一个可用下一跳,并不会自动为私有子网选择它。公有表现有的 Internet 网关路由保持不变。

通过 NAT 路由私有出站流量

本步骤添加私有默认路由,并比较应用私有地址与外部服务实际观察到的源地址。

路由为目标地址范围选择下一跳。0.0.0.0/0 匹配未命中更具体路由的 IPv4 目标。--nat-gateway-id 选择你的 NAT 网关,而不是 Internet 网关。仅向已保存的私有路由表添加该路由:

aws ec2 create-route \
  --route-table-id "$PRIVATE_RT_ID" \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id "$NAT_ID"

响应表示创建成功。查询私有表的路由:

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

本地路由仍存在。新默认路由包含你的 NatGatewayId,状态为 active。数据包路径现在是私有应用 → 公有子网中的 NAT 网关 → Internet 网关 → 外部服务。

当 AWS View 显示私有默认路由后,点击 Request outbound service。请求返回 Success,响应正文是外部 HTTP 服务实际观察到的源 IPv4 地址。将它与分配的公有地址比较:

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[0].PublicIp' \
  --output text

两个地址相同。网络地址转换(NAT)将应用的私有源地址替换为网关公有地址,并跟踪连接,使响应能够返回发起方。应用自身的地址仍为 10.20.2.10。

私有应用通过 NAT 路由访问外部服务

AWS View 示例:应用仍位于 10.20.2.10,私有默认路由选择可用的 NAT 网关,HTTP 响应正文显示已分配的公有地址。自动生成的资源 ID 和地址可能不同。

点击 Request private application from outside。它仍返回 Connection failed。出站连接及其返回流量不会为私有应用创建公有入站路径。NAT 网关不会接受面向该应用的主动 Internet 入站连接。

排查并恢复缺失的私有路由

本步骤删除一条路由,观察产生的故障,然后在不重建 NAT 网关的情况下恢复连通性。

网关可以处于可用状态,但客户端仍没有可用路由。仅删除私有默认路由,保留 NAT 网关和公有 Internet 路由:

aws ec2 delete-route --route-table-id "$PRIVATE_RT_ID" --destination-cidr-block 0.0.0.0/0

删除成功时没有输出。检查私有表:

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

仅保留 VPC 本地路由。AWS View 仍显示可用的 NAT 网关,却没有私有默认路由。点击 Request outbound service,新的请求会失败。之前的成功只是历史记录,并不是这次新请求的结果。

恢复通向同一网关的同一条路由:

aws ec2 create-route \
  --route-table-id "$PRIVATE_RT_ID" \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id "$NAT_ID"

再次查询路由:

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

有效的默认路由重新出现。在 AWS View 发起新的 Request outbound service,请求成功并显示相同的 NAT 公有地址。Request private application from outside 仍失败。这些观察说明可用网关与完整的客户端路由路径之间的区别。

删除练习 NAT 路径并释放地址

本步骤删除自己的路由和 NAT 网关,释放公有地址,并证明预置的私有网络仍完整保留。

先删除私有默认路由,再删除下一跳,否则表中可能留下通向已删除网关的路由:

aws ec2 delete-route --route-table-id "$PRIVATE_RT_ID" --destination-cidr-block 0.0.0.0/0

删除成功时没有输出。仅删除自己的 NAT 网关:

aws ec2 delete-nat-gateway --nat-gateway-id "$NAT_ID"

响应标识请求删除的网关。释放地址之前使用删除 waiter:

aws ec2 wait nat-gateway-deleted --nat-gateway-ids "$NAT_ID"

waiter 成功时没有输出。删除 NAT 网关会移除其网络接口并解除弹性 IP 关联,却不会释放地址分配。释放之前查询地址:

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp,Association:AssociationId}' \
  --output json

分配和公有地址仍存在,Association 为 null:地址已分配,但不再关联资源。现在释放这个独立资源:

aws ec2 release-address --allocation-id "$ALLOCATION_ID"

释放成功时没有输出。查询完整的网关库存,不要依赖可以移除的标签:

aws ec2 describe-nat-gateways \
  --query 'NatGateways[].{ID:NatGatewayId,State:State,Subnet:SubnetId}' \
  --output table

网关可能仍以 deleted 状态出现在列表中,但不应存在活动的练习网关。保留的删除记录不表示网关仍能转发流量。检查完整地址库存:

aws ec2 describe-addresses \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp}' \
  --output table

练习地址分配已不存在。现在查询私有路由:

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

VPC 本地路由仍存在,默认路由已消失。预置应用、子网、安全规则、公有 Internet 网关和参考网络均保留。库存查询失败不能证明资源已删除。

在 AWS View 点击 Request outbound service,由于你主动移除了 NAT 路径,请求再次失败。Request private application from outside 仍被阻止。这些结果与初始私有网络基线一致。

运行本步骤的完成检查。

总结

你分配了公有地址,将 NAT 网关放入公有子网,并通过它路由私有应用的出站请求。外部服务观察到转换后的 NAT 地址,外部主动连接私有应用的请求仍被阻止。删除并恢复私有默认路由说明了为什么仅有网关可用并不能建立连通性。

随后你移除了路由和 NAT 网关,释放地址,并验证私有网络基线仍保留。