プライベートサブネットのECSやEC2からインターネットへ出るには、NATゲートウェイが必要です。ところが、NATゲートウェイは通過したデータ量にも課金されるため、コンテナイメージの取得やログの送信が多い環境では、思った以上に料金がかさみます。
この記事では、VPCエンドポイントを使ってNATゲートウェイを通る通信を減らす方法と、そのTerraformコードを紹介します。あわせて、「エンドポイントを作ると逆に高くなる」ケースも確認します。
NATゲートウェイの料金の内訳
NATゲートウェイの料金は、次の2つの合計です(東京リージョン、執筆時点の目安)。
- 時間料金:1台あたり1時間0.062USD(約45USD/月)
- データ処理料金:通過したデータ1GBあたり0.062USD
時間料金は、NATゲートウェイを置く限り避けられません。削れるのはデータ処理料金のほうです。
プライベートサブネットからの通信には、実はAWSのサービス宛てのものが多く含まれます。
- ECRからのコンテナイメージの取得
- CloudWatch Logsへのログ送信
- S3へのファイルの読み書き
- Secrets Managerからの値の取得
これらを、NATゲートウェイを通さずにVPCの中から直接届けるのがVPCエンドポイントです。
2種類のVPCエンドポイント
VPCエンドポイントには、料金も仕組みも異なる2種類があります。
ゲートウェイ型
- 対象:S3とDynamoDBのみ
- 仕組み:ルートテーブルに経路を追加する
- 料金:無料
インターフェース型
- 対象:ECR、CloudWatch Logs、Secrets Managerなど多数
- 仕組み:サブネットにネットワークインターフェース(ENI)を作る
- 料金:エンドポイント1つ・AZ1つあたりの時間料金(東京で1時間約0.014USD)と、データ1GBあたり0.01USD
ゲートウェイ型は無料なので、S3を使うなら必ず作っておくべきです。インターフェース型は、通信量によって得にも損にもなります。
S3のゲートウェイ型エンドポイント
ルートテーブルに関連付けるだけで、S3宛ての通信がエンドポイント経由になります。
variable "vpc_id" {
type = string
}
variable "private_route_table_ids" {
type = list(string)
}
variable "private_subnet_ids" {
type = list(string)
}
data "aws_region" "current" {}
data "aws_vpc" "this" {
id = var.vpc_id
}
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${data.aws_region.current.name}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
tags = {
Name = "s3-gateway-endpoint"
}
}
ECRのイメージ本体(レイヤー)はS3に保存されています。そのため、ECRのエンドポイントを作る場合も、このS3ゲートウェイ型エンドポイントは必須です。
インターフェース型エンドポイント
ECR(2種類)とCloudWatch Logsのエンドポイントを作ります。まず、エンドポイント用のセキュリティグループを用意します。
resource "aws_security_group" "vpc_endpoint" {
name = "vpc-endpoint-sg"
vpc_id = var.vpc_id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [data.aws_vpc.this.cidr_block]
}
}
locals {
interface_endpoints = [
"ecr.api",
"ecr.dkr",
"logs",
]
}
resource "aws_vpc_endpoint" "interface" {
for_each = toset(local.interface_endpoints)
vpc_id = var.vpc_id
service_name = "com.amazonaws.${data.aws_region.current.name}.${each.value}"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoint.id]
private_dns_enabled = true
tags = {
Name = "${each.value}-endpoint"
}
}
ポイントは次の3つです。
- private_dns_enabled = true:通常のAPIのドメイン名(例:logs.ap-northeast-1.amazonaws.com)がエンドポイントのIPアドレスに解決されるようになる。アプリやSDKの設定を変える必要がない
- セキュリティグループ:エンドポイントはHTTPS(443番)で受け付けるので、VPC内からの443番を許可する
- for_each:エンドポイントを追加したいときは、localsのリストにサービス名を足すだけで済む
ECRには「ecr.api」(認証などのAPI)と「ecr.dkr」(イメージの取得)の2つが必要です。片方だけではイメージを取得できません。
なお、private_dns_enabledを使うには、VPCのDNSサポートとDNSホスト名の両方が有効になっている必要があります。
インターフェース型で逆に高くなるケース
インターフェース型エンドポイントは、AZの数だけ時間料金がかかります。東京リージョンの目安で計算してみます。
- エンドポイント1つ・2AZ:0.014USD × 2AZ × 730時間 = 約20USD/月
- 今回の3つ(ecr.api、ecr.dkr、logs):約61USD/月
一方、NATゲートウェイからエンドポイントに移すと、1GBあたり0.062USD − 0.01USD = 0.052USDが浮きます。
約61USDの固定費を取り戻すには、3つのエンドポイント合計で月に約1,200GBの通信を移す必要があります。
- 小規模な開発環境:月の通信量が数十GB程度なら、インターフェース型は作らないほうが安い
- イメージの取得が多い本番環境:タスクの起動やデプロイが頻繁で、通信量が数百GB〜TB単位なら効果が出る
インターフェース型エンドポイントには、インターネットを経由しないというセキュリティ上の利点もあります。コストだけでなく、要件に合わせて判断してください。
どの通信が多いかを確かめる
エンドポイントを作る前に、NATゲートウェイを通っている通信の量と中身を確認しておくと判断を誤りません。
- CloudWatchメトリクス:NATゲートウェイのBytesOutToDestinationで、月の通信量をつかむ
- VPCフローログ:宛先IPアドレスを集計し、どのサービス宛ての通信が多いかを調べる
- Cost Explorer:使用タイプ「NatGateway-Bytes」で、データ処理料金の推移を見る
まとめ
- NATゲートウェイは時間料金に加え、データ処理料金がかかる
- S3のゲートウェイ型エンドポイントは無料なので、必ず作る
- インターフェース型はAZ数分の固定費がかかるため、通信量が少ない環境では逆に高くなる
- 作る前に、NATゲートウェイを通る通信の量と宛先を確認する
「とりあえず全部作る」と固定費がかさみ、「何も作らない」とデータ処理料金がかさみます。通信量を見て、効果のあるエンドポイントだけを選びましょう。
料金は変わることがあるため、最新の金額はAWSの公式料金ページで確認してください。