IT/AWS

NAT Gateway 청구서, VPC Endpoint로 줄이는 법

gfrog 2026. 5. 23. 21:48
반응형

매달 AWS 청구서를 열어볼 때 가장 짜증나는 항목이 뭘까. 우리 팀은 단연 NAT Gateway였다. EC2랑 RDS는 어느 정도 예상 가능한데, NAT는 매번 "어, 이게 왜 이렇게 나왔지?" 하면서 Cost Explorer를 파게 된다. 특히 EKS 클러스터를 운영하다 보면 이미지 pull, 로그 전송, S3 접근 같은 트래픽이 죄다 NAT를 거치게 되는데, 이 데이터 처리 비용이 의외로 크다.

이번 글은 우리 팀에서 NAT Gateway 데이터 처리 비용을 한 달 만에 60% 가까이 줄인 방법을 정리한 거다. 정공법이지만 의외로 안 챙겨 쓰는 팀이 많아서 가이드로 남긴다.

왜 NAT Gateway가 비싼가

NAT Gateway 요금 구조부터 짚고 가자. 두 가지 축이 있다.

  • 시간당 고정비: $0.045/h (서울 리전 기준). AZ당 하나 둔다고 하면 3 AZ면 한 달에 약 $97 정도 나간다. 이건 어쩔 수 없다.
  • 데이터 처리 비용: $0.045/GB. 이게 진짜 문제다.

월 1TB 트래픽이 NAT를 통과하면 데이터 처리만 $45다. 우리 팀처럼 ECR pull이 잦고, CloudWatch Logs를 적극적으로 쓰는 환경에서는 월 10TB는 우습게 나간다. 즉 처리 비용만 $450, 한화로 약 60만원. 적은 돈은 아니다.

여기서 핵심은 AWS 서비스 트래픽도 NAT를 거치면 다 처리 비용이 붙는다는 점이다. 같은 리전 내 S3, ECR, DynamoDB로 가는 트래픽도 마찬가지. 이걸 VPC Endpoint로 우회시키면 그만큼이 통째로 빠진다.

Gateway Endpoint 먼저 — 공짜다

VPC Endpoint는 두 종류가 있다.

  1. Gateway Endpoint — S3, DynamoDB 두 개만 지원. 요금 없음.
  2. Interface Endpoint — 그 외 대부분의 AWS 서비스. AZ당 $0.01/h + $0.01/GB.

Gateway Endpoint는 라우팅 테이블에 prefix list만 박아주면 끝나는데, 추가 비용이 0원이다. 이걸 안 쓰고 있다면 지금 당장 만들어라. Terraform으로 한 줄이다.

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.ap-northeast-2.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = aws_route_table.private[*].id

  policy = jsonencode({
    Statement = [{
      Effect    = "Allow"
      Principal = "*"
      Action    = "*"
      Resource  = "*"
    }]
  })
}

resource "aws_vpc_endpoint" "dynamodb" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.ap-northeast-2.dynamodb"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = aws_route_table.private[*].id
}

이거 하나 추가했더니 우리 팀은 S3 트래픽 약 4TB/월이 NAT에서 빠졌다. 단순 계산해도 $180/월 절감.

한 가지 주의: Gateway Endpoint는 같은 리전 내의 S3에만 적용된다. 크로스 리전 S3 요청은 여전히 NAT를 탄다. 그리고 endpoint policy를 너무 빡세게 잡으면 SDK가 조용히 fail하니까, 처음엔 풀어놓고 트래픽 확인 후 좁히는 걸 추천한다.

Interface Endpoint — 손익분기점 계산이 핵심

Interface Endpoint는 다르다. AZ당 시간 요금이 붙는다. 멀티 AZ 환경에서 3 AZ에 깔면 endpoint 하나당 한 달에 약 $21.6의 고정비가 나간다. 여기에 $0.01/GB 데이터 처리비.

NAT Gateway 데이터 처리비가 $0.045/GB니까 차이는 $0.035/GB. 즉 월 약 617GB 이상이 해당 서비스로 흐르면 Interface Endpoint가 이득이다.

서비스별로 트래픽이 얼마나 나는지 모르겠다면 VPC Flow Logs에서 destination을 prefix list로 필터링해서 확인하면 된다. 단, AWS 공개 IP는 prefix list가 자주 바뀌어서 ip-ranges.json을 받아 매칭하는 스크립트가 필요하다. 귀찮으면 그냥 Cost Explorer의 NAT Gateway 데이터 처리비를 한 달 보고, "큰 트래픽을 만드는 서비스 후보"를 추정하는 식으로도 충분하다.

내 경험상 거의 매번 ECR이 1순위다.

ECR Endpoint — EKS 운영자가 무조건 챙겨야 하는 것

EKS 노드가 컨테이너 이미지를 pull할 때마다 NAT를 탄다. 이미지가 작으면 모를까, ML 워크로드처럼 5GB짜리 이미지를 매일 수십 번 pull한다면 답이 없다. 우리 팀이 처음 측정했을 때 ECR pull 트래픽만 월 6TB였다. $270/월이 NAT 데이터 처리로 빠지고 있었던 셈이다.

ECR Interface Endpoint는 두 개를 같이 깔아야 한다. apidkr. 그리고 S3 Gateway Endpoint도 필수다 (실제 이미지 layer는 ECR이 내부적으로 S3에 저장한다).

locals {
  ecr_endpoints = ["ecr.api", "ecr.dkr"]
}

resource "aws_vpc_endpoint" "ecr" {
  for_each            = toset(local.ecr_endpoints)
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.ap-northeast-2.${each.value}"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpce.id]
  private_dns_enabled = true
}

private_dns_enabled = true가 핵심이다. 이게 켜져 있어야 기존 SDK/CLI 코드가 그대로 endpoint를 타게 된다. 안 켜면 endpoint별로 DNS 이름이 따로 생성돼서 클라이언트 설정을 다 바꿔야 한다.

Security Group 설정은 자주 까먹는 부분이다. Endpoint의 SG에 VPC CIDR로부터 443 inbound를 허용해야 한다. 이거 빠지면 endpoint는 생겼는데 timeout만 나는 상황이 된다. 처음 도입하고 30분 정도 트러블슈팅한 다음에야 깨달았다.

그 외에 챙길만한 Endpoint들

ECR 다음으로 손익분기점을 자주 넘는 것들:

  • CloudWatch Logs (logs) — 로그 적극적으로 쓰면 빠르게 본전 뽑는다. 우리는 월 3TB 정도 흘러서 명백히 이득.
  • SQS (sqs), SNS (sns) — 큐 기반 워크로드면 효과 큼.
  • STS (sts) — 트래픽은 작지만 IRSA/Pod Identity 환경에서 호출 빈도가 높다. 그리고 인증 트래픽이 NAT에 묶이면 신뢰성도 떨어진다. 손익분기점과 별개로 보안/안정성 관점에서 권장.
  • Secrets Manager (secretsmanager) — 같은 이유로 작은 트래픽이라도 깔아두는 편.

반대로 굳이 안 깔아도 되는 것들: 트래픽 거의 없는 일회성 API 호출 위주의 서비스들(예: CloudFormation, IAM 같은 control plane). 이런 건 고정비만 까먹는다.

도입 후 검증

도입했다고 끝이 아니다. 실제로 NAT 트래픽이 줄었는지 확인해야 한다. 두 가지 방법.

첫 번째는 CloudWatch의 NATGateway 네임스페이스에서 BytesOutToDestination, BytesInFromSource 메트릭을 보는 거다. Endpoint 도입 전후로 명확하게 떨어져야 정상이다.

두 번째는 VPC Endpoint 쪽 메트릭. AWS/PrivateLinkEndpoints 네임스페이스의 BytesProcessed가 endpoint로 흐른 트래픽이다. NAT에서 줄어든 만큼 여기서 늘어나야 산수가 맞다.

만약 endpoint를 만들었는데 NAT 트래픽이 안 줄어든다면 거의 다음 중 하나다:

  1. private_dns_enabled가 꺼져 있거나 (VPC의 enableDnsSupport, enableDnsHostnames도 켜져 있어야 한다)
  2. Endpoint SG가 트래픽을 막고 있거나
  3. Endpoint policy가 너무 좁거나
  4. 클라이언트가 강제로 region-specific endpoint URL을 박아 쓰고 있거나

3번은 의외로 흔하다. 처음엔 풀어놓고 시작해라.

마무리

요약하면 순서는 이렇다. S3/DynamoDB Gateway Endpoint를 일단 무조건 깔고, 그다음 ECR을 챙기고, CloudWatch Logs와 STS/Secrets Manager까지 깔면 대부분의 NAT 데이터 처리비는 정리된다. 우리 팀은 이렇게 4개 끊고 한 달 청구서에서 NAT 데이터 처리비가 약 58% 줄었다.

한 가지 덧붙이자면, 이건 IaC로 관리하는 게 정신 건강에 좋다. 콘솔에서 클릭으로 만들면 어느 endpoint가 어디에 붙어있는지, policy는 어떻게 됐는지 추적이 안 된다. 그리고 endpoint 관련 비용이 잘못 잡혀서 오히려 손해가 나는 경우도 있어서, Terraform module로 묶어서 "이 VPC에는 이런 endpoint를 항상 깐다"는 표준을 만들어두면 깔끔하다.

다음에는 Endpoint 정책을 SCP 레벨에서 강제하는 방법도 다뤄볼까 한다. 멀티 어카운트 환경이면 그쪽이 본질이다.

반응형