NAT Gateway 的三笔冤枉钱
目录
NAT Gateway 有两笔费用:一笔按网关存在的小时数收,一笔按过它的字节数收。两笔都可能白花,其中每 GB 那笔的口径最容易看错,Amazon VPC 定价页的原话是:
Data processing charges apply for each gigabyte processed through the NAT gateway regardless of the traffic’s source or destination
regardless of the traffic's source or destination:目的地在同一个可用区、同一个区域,还是地球另一端,单价一样。所以只要一个包从私有子网绕出去,这笔钱就照收,跟它本来有近路可走无关。
IPv4 的出公网流量(调外部 API、拉公网镜像)没有别的走法,该走 NAT 还是走 NAT,这部分每 GB 的钱省不掉。IPv6 多一条路,给实例 IPv6 地址、路由指向 egress-only internet gateway,出向就不用过 NAT:这个网关只往外走、不让公网主动连进来,本身不收钱,过它的字节按 EC2 的公网数据传输计费。
冤枉钱来自三件事:一件出在小时费上,两件出在每 GB 那笔上,各自有不同的成因和修法。
| 浪费 | 怎么产生的 | 解法 | 实测 |
|---|---|---|---|
| 网关空转 | NAT 建完就没人拆,小时费按个数一直计 | 确认没流量后删网关,连 EIP 一起释放 | 8 个网关,每月 400 多美元,另加 29 美元的 EIP |
| 到 AWS 服务的流量绕公网 | 私有子网里 0.0.0.0/0 指向 NAT,S3 和 ECR 镜像层都从那儿出去 |
建 gateway endpoint(S3、DynamoDB 免费)或按流量算回本点的 interface endpoint | 一个 crash loop 每天 412 GB、38 美元 |
| SaaS 流量照走公网 | SaaS 给的是公网域名,peering 建好也没人走 | 对端给私有端点后,用 private hosted zone 把域名解析过去 | peering 只服务按私有 IP 直连的调用 |
下面单价一律按东京区列,都是 2026-09-13 从 AWS 定价接口查的,机制照 AWS 官方文档核对过。账单上那几个数字不在东京区,出现的时候单独标。圣保罗区的两笔都是 $0.093,比东京贵一档,按东京区算出来的回本点换到别的区,得拿本区那两个数重算。
8 个空转的 NAT 网关#
东京区用 Pricing API 查 EC2 这个 offer code,两笔数字是一样的:
aws pricing get-products --service-code AmazonEC2 --region us-east-1 \
--filters 'Type=TERM_MATCH,Field=location,Value=Asia Pacific (Tokyo)' \
'Type=TERM_MATCH,Field=productFamily,Value=NAT Gateway' \
--output json | jq -r '.PriceList[] | fromjson
| .terms.OnDemand[].priceDimensions[]
| "\(.pricePerUnit.USD) \(.description)"'
0.0620000000 $0.062 per GB Data Processed by NAT Gateways
0.0620000000 $0.062 per NAT Gateway Hour
小时费按网关个数走,不看用量,东京区 $0.062 一小时约合一个月 45 美元。这 8 个挂在几个没有工作负载的 VPC 里,30 天 BytesOutToDestination 和 ActiveConnectionCount 都是 0,每月加起来 400 多美元;它们不在东京区,各区小时价不同,别拿 45 美元去乘 8。
证明「没人用」,NAT 那几个字节指标随便挑一个都行,反正都是 0;要按计费口径加出总量的时候才需要挑对是哪两个,那是后面那节的事。指标之外还可以看 VPC 里除了 NAT 自己那几张 ENI 还有没有别的:
aws ec2 describe-network-interfaces --filters Name=vpc-id,Values=vpc-xxxxxxxx \
--query 'NetworkInterfaces[].[InterfaceType,Description]' --output text
删 NAT 时把 Elastic IP 一起释放。这 8 个 EIP 现在就在收费:2024-02-01 之后公网 IPv4 不分挂没挂在资源上都收,PublicIPv4:InUseAddress 和 PublicIPv4:IdleAddress 各区都是 $0.005 每地址每小时,8 个就是每月 29 美元,删掉网关只是把这笔钱从 InUse 挪到 Idle,单价不变,release-address 之后才停。(这个账号拿不到 resource 级的 Cost Explorer,这条是拿账单地址数跟 EIP 库存卡出来的。圣保罗区八月的 InUse 折合 44 个地址;这个区非 NAT 的公网 IP 最多凑得出 39 个,43 个已关联 EIP 减去在 NAT 上的 7 个,再加 3 个自带公网 IP 的实例。差的 5 个只能是 NAT 的,另外两个区也对不上。这是按数量卡出来的,不是逐个资源的归属。)
到 S3 和 ECR 的流量绕了公网#
一个包要不要按 GB 付钱,取决于应用连的是哪个地址:
| 应用连的地址 | 走哪条路 | 每 GB 单价(东京区) |
|---|---|---|
| 公网域名解析出的公网 IP | 私有子网 → NAT → IGW | $0.062,另加对应的数据传输费 |
| VPC peering 对端的私有 IP | 私有子网 → peering | 同 AZ 不收,跨 AZ 进出各 $0.01 |
| interface endpoint 的 ENI | 私有子网 → endpoint ENI | $0.01(首 1 PB),另加 $0.014 每 endpoint 每 AZ 每小时 |
| gateway endpoint(到 2026-09-13 只有 S3 和 DynamoDB) | 私有子网 → 路由表里的 prefix list | 0 |
单价来源:NAT 的 $0.062 和 peering 跨 AZ 的 $0.01 都在 VPC 定价页;endpoint 那两个数字在 AmazonVPC 这个 offer code 的定价清单里(aws pricing get-products --service-code AmazonEC2 查的是另一个清单,抓不到这两个),东京区的 usagetype 是 APN1-VpcEndpoint-Hours($0.014)和 APN1-VpcEndpoint-Bytes($0.01)。跨 AZ 那笔只有 peering 那行有,2022-04-01 起走 interface endpoint 的跨 AZ 流量 AWS 不另收,表里的 $0.01 就是全部。gateway endpoint 免费同样是定价页的原话:
There are no data processing or hourly charges for using Gateway Type VPC endpoints.
这个 0 只在本区成立:gateway endpoint 靠路由表里那条本区 prefix list 生效,发往别的区同一个服务的包不匹配它,照旧落回 0.0.0.0/0 走 NAT。这条和另外几条非价格的分界,展开在两种 endpoint 怎么选那篇里。
免费这一档值得先做完,因为它没有回本点要算。最明显的一例是 ECR 拉镜像,ECR 的 VPC endpoint 文档写得很直接:
For your Amazon ECS tasks to pull private images from Amazon ECR, you must create a gateway endpoint for Amazon S3. The gateway endpoint is required because Amazon ECR uses Amazon S3 to store your image layers.
同一页还列了各平台版本要哪几个 endpoint:
Amazon ECS tasks hosted on Fargate using platform version
1.4.0or later require both Amazon ECR VPC endpoints and the Amazon S3 gateway endpoints.
ecr.api 和 ecr.dkr 这两个 interface endpoint 只管住 API 调用(取 token、拿 manifest)。镜像层在 prod-<region>-starport-layer-bucket 这个 S3 桶里,没有 S3 gateway endpoint 就仍旧从 NAT 出去再回来。出问题的那个 VPC 就是这个配法:两个 ECR endpoint 建了,PrivateDnsEnabled 也是 true,S3 的没建,562 MB 的层一个字节都没少走。
interface endpoint"] API --> ECR[("Amazon ECR")] T -- "下载镜像层 562 MB" --> RT{"子网路由表"} RT -- "没建 S3 gateway endpoint
落到 0.0.0.0/0" --> NAT["NAT Gateway
按 GB 收费"] RT -. "建了走 prefix list,不收钱" .-> GW["S3 gateway endpoint"] NAT --> S3[("starport-layer-bucket")] GW -.-> S3
任务每重启一次就重拉一次镜像:Fargate 的任务跑在按需新起的执行环境里,不像 EC2 那样有上一任务留下的本地镜像缓存,一起来就重新下载一遍(这条我没找到 AWS 文档的正面说明,是从重启次数和流量对得上反推的)。任务 ENI 所在子网的路由表里,默认路由指向 NAT,这 562 MB 每次都要过一遍收费口。412 GB 除以 562 MB 差不多是每天 750 次,跟那个循环的重启次数对得上。
建 gateway endpoint 有两点容易漏。一是它靠路由表生效,创建时要把每一张私有子网的路由表都勾上,漏掉的那张照旧走 NAT;以后新加的子网带的是新路由表,同样不会自动带上这条,悄悄退回 NAT。二是 ECR 文档这条:
If your containers have existing connections to Amazon S3, their connections might be briefly interrupted when you add the Amazon S3 gateway endpoint.
已有的 S3 长连接会断一下。AWS 文档给的解法是另建一个带 gateway endpoint 的 VPC 再把集群迁过去;这个成本太高的话,挑个低峰时段直接加也行,风险是有状态的 S3 传输可能要重试一次。
gateway endpoint 覆盖不到的 AWS 服务,interface endpoint 要算回本点#
AWS 自己在 NAT gateway 的计费说明页把两种 endpoint 并列给出来:
If most traffic through your NAT gateway is to AWS services that support interface endpoints or gateway endpoints, consider creating an interface endpoint or gateway endpoint for these services.
S3 和 DynamoDB 之外的 AWS 服务要走私有路径只能用 interface endpoint,而它有小时费。
东京区一个 interface endpoint 铺 3 个 AZ,$0.014 乘 3 乘 730 小时是 30.7 美元。每 GB 从 $0.062 降到 $0.01,每 GB 省 $0.052,两边相除得出月流量大约 600 GB 以上才开始省钱,再往上线性增长:一个月 10 TB 的服务,NAT 是 $635,interface endpoint 是 $102 加 $31。
流量不到这个量级就别建,付了小时费省不下多少。peering 没有小时费,不用算回本点:跨 AZ 每 GB 进出各 $0.01,已经比 NAT 的 $0.062 低一档;资源跟对端放同一个 AZ,这两笔也都省掉。
SaaS 给的公网域名:peering 建好了流量也不走过去#
第三方账号里的托管服务(Confluent Cloud、ClickHouse Cloud 这类)给的连接串是公网域名。哪怕实例就开在你同一个区,DNS 给出来的是公网 IP,单价就落到表格第一行。自己 VPC 里的 RDS、MSK 不在这条路径上:DB 实例和 broker 的 ENI 建在你的子网里,端点解析回私有 IP,本来也不过 NAT。
应用连的如果还是那个公网域名,DNS 解析回公网 IP,包照样出 NAT,peering 在旁边闲着。要确认得从子网里实际解析一次,或者看账号里有没有 private hosted zone 覆盖这个域名:
aws route53 list-hosted-zones --query 'HostedZones[].[Name,Config.PrivateZone]' --output text
这个账号里一个覆盖 SaaS 域名的私有 zone 都没有,那 peering 就只服务于按私有 IP 直连的那部分调用。要让 SaaS 流量走 peering,得补上这条域名解析,而它能不能成,取决于对端给不给你按私有 IP 访问的端点。
从账单查到具体网关#
上面三件事都是从账单倒推出来的。先把这笔钱在账单里框出来:NAT 的两笔费用在 Cost Explorer 里是 EC2 - Other 下的两个 usage type:
aws ce get-cost-and-usage \
--time-period Start=2026-08-01,End=2026-09-01 --granularity MONTHLY \
--metrics UnblendedCost UsageQuantity \
--group-by Type=DIMENSION,Key=USAGE_TYPE \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["EC2 - Other"]}}' \
--query 'ResultsByTime[0].Groups[?contains(Keys[0],`NatGateway`)]'
出来的是 <区域前缀>-NatGateway-Bytes 和 -Hours 两行。-Hours 的 UsageQuantity 除以当月小时数正好是网关个数,可以用它跟 describe-nat-gateways 的数量对一下账,两边对不上通常意味着这个月有网关新建或删除过。
框出来之后要落到具体网关。AWS/NATGateway 下有四个字节指标,容易数重:
BytesInFromSource:实例发给 NAT 的BytesOutToDestination:NAT 发往目的地的BytesInFromDestination:目的地回给 NAT 的BytesOutToSource:NAT 回给实例的
去程在前两个里各计一次,回程在后两个里各计一次。我按 BytesInFromSource 加 BytesInFromDestination 取数,每个字节只数一次。我没找到 AWS 文档把计费和这四个指标对应起来的地方,要跟账单对齐,就拿这两个指标的和跟 Cost Explorer 里 -NatGateway-Bytes 的 UsageQuantity 对一次。单看回程那个指标:
aws cloudwatch get-metric-statistics --namespace AWS/NATGateway \
--metric-name BytesInFromDestination \
--dimensions Name=NatGatewayId,Value=nat-xxxxxxxxxxxxxxxxx \
--start-time 2026-08-13T00:00:00Z --end-time 2026-09-12T00:00:00Z \
--period 86400 --statistics Sum \
--query 'sort_by(Datapoints,&Timestamp)[].[Timestamp,Sum]' --output text
开头那个 crash loop 就是这样撞见的:按天拉出来,某一天从 0.1 GB 跳到 196 GB,之后七天稳定在 412 GB 上下,循环停掉当天回到 0。这个 VPC 在圣保罗区,412 GB 按那里的 $0.093 算下来是每天 38 美元。任务起来就退是数据库那边的问题,这篇不碰;这笔账单是每次重启重拉那 562 MB 攒出来的,两边各有各的修法。
这两步能回答「哪个网关在烧钱」,回答不了「这些字节发给谁」:Cost Explorer 没有目的地维度,CloudWatch 也没有,要归因只有 VPC Flow Logs。我不建议在 VPC 级别直接打开并写进 CloudWatch Logs,日志费可能比省下的流量费还贵;给 NAT 自己那几张 ENI 单独建,投到 S3,开一两天,用 Athena 按 pkt-dstaddr 聚合出 top 20 然后关掉。建 Flow Log 时记得选自定义格式:pkt-dstaddr 是 version 3 字段,默认格式只有 version 2 的那批,里面的 dstaddr 在 NAT 的 ENI 上记的是 NAT 自己那一跳,聚出来看不到真实目的地。拿到那张表再决定哪些目的地值得建 endpoint 或 peering。
小结#
- 三笔里只有第一笔现在就能算出总账:8 个空转网关每月 400 多美元的小时费,加上它们 EIP 的 29 美元,删干净就没了。另外两笔不能直接加上去:crash loop 那 38 美元一天是数据库那边修好之后停的;gateway endpoint 省下来的是平时拉镜像那部分流量,得按稳态的量另算;SaaS 那笔要等对端给出私有端点。
- 顺序上先删空转网关,再给 S3 建 gateway endpoint(不要钱,顺带解决 ECR 镜像层),然后按 600 GB 这个量级判断要不要 interface endpoint,最后再考虑 SaaS 的私有域名解析。
- 删网关时连 EIP 一起释放。公网 IPv4 这笔钱删网关省不掉,只是从 InUse 挪到 Idle,单价一样。
- 建 endpoint 之前先做归因。没有 Flow Logs,账单只能说出哪个网关贵,说不出这些字节发给谁,很容易把 endpoint 建在没什么流量的目的地。