월요일 아침에 출근하니 재무팀에서 슬랙이 와있었다. "지난달 AWS 비용 한 번만 확인 부탁드려요." 평소엔 무시할 만한 변동이었는데 이번엔 NAT Gateway 라인 하나만 전월 대비 3.2배가 찍혀 있었다. 다른 항목은 거의 그대로였다. 보자마자 멘탈이 살짝 흔들렸다. 우리 팀은 분기 비용 가이드라인이 있어서 한 항목이 갑자기 튀면 그게 곧 회고 거리다.
여기에 진단부터 해결까지 일주일 동안 삽질한 기록을 남긴다. 결론부터 말하면 범인은 단순했고, 우리가 그동안 NAT Gateway 트래픽 구성을 너무 안 들여다본 게 진짜 문제였다.
첫 번째 가설: 누가 풀데이터를 빨아 가나
처음엔 누군가 새 워크로드 띄우면서 외부 데이터셋이라도 받아오는 줄 알았다. 그런데 워크로드 추가된 거 없었다. 노드 수도 그대로(82대), 트래픽 그래프도 P99 평소 수준. 근데 NAT Gateway의 BytesOutToDestination 메트릭만 보면 거의 두 배가 돼 있었다.
VPC Flow Logs를 Athena로 펴서 destination IP/포트 기준 top N을 뽑았다. 이때부터 좀 의외였는데, 1위가 S3 IP 대역이었다. 2위가 ECR Public, 3위가 CloudWatch Logs 엔드포인트 IP였다. 외부 트래픽이라고 생각했던 게 전부 AWS 내부 서비스로 가는 트래픽이었다.
SELECT
CASE
WHEN regexp_like(dstaddr, '^52\.21[6-9]\.') THEN 'S3-like'
WHEN regexp_like(dstaddr, '^3\.5\.') THEN 'ECR-public-like'
ELSE 'other'
END AS bucket,
SUM(bytes) / 1024 / 1024 / 1024 AS gb
FROM vpc_flow_logs
WHERE day = '2026-04-28'
AND action = 'ACCEPT'
AND srcaddr LIKE '10.%'
GROUP BY 1
ORDER BY 2 DESC;
S3로 가는 트래픽이 전체의 절반 가까이였다. NAT Gateway는 GB당 $0.045 데이터 처리 요금이 붙는데, S3로 향하는 그 모든 바이트가 그 요금을 거쳐서 다시 S3로 가고 있었다. 더 어이없는 건 S3 Gateway Endpoint는 무료라는 거다. 그냥 라우팅 테이블에 엔트리 하나만 박으면 됐던 것을.
왜 갑자기 늘었나
여기서 한 번 더 의문이 들었다. S3 트래픽이 늘 NAT 통해 나가던 거였다면 청구서가 갑자기 튈 이유가 없잖아? 평소에도 비쌌어야지.
git log 뒤지다가 한 PR이 눈에 띄었다. 4월 초에 데이터 플랫폼 팀에서 Iceberg 테이블 마이그레이션을 시작했는데, 그 일환으로 새 Spark 잡이 매일 새벽에 돌면서 raw 데이터를 S3에서 읽어다 변환하고 다시 S3에 쓰고 있었다. 입출력 합치면 일평균 800GB 정도였다. 한 달이면 24TB. NAT 데이터 처리 요금만 약 $1,080. 거기에 EKS 노드 50대 가까이가 ECR에서 이미지 풀 받을 때마다 또 NAT 거쳐서 나가는 트래픽이 있었다. Karpenter 도입한 뒤로 노드 회전이 잦아져서 이미지 풀 횟수도 늘었다.
상황 정리하면 NAT Gateway 청구서는 두 가지 원인의 합이었다:
- 데이터 플랫폼의 새 S3 워크로드 (예상 못 한 신규 트래픽)
- Karpenter 도입 후 ECR 풀 트래픽 증가 (이건 우리가 자초)
임시 조치와 진짜 조치
먼저 S3 Gateway Endpoint부터 박았다. 이건 정말로 5분이면 된다.
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 = "*"
}]
})
}
이걸 적용한 다음 날 NAT Gateway BytesOutToDestination이 그 즉시 절반 가까이 떨어졌다. 정확히는 47% 감소. Athena 쿼리에서 본 그 S3 비중과 거의 정확히 매칭됐다. 솔직히 너무 깨끗하게 떨어져서 좀 허무하기까지 했다.
그 다음 주에 ECR 인터페이스 엔드포인트를 박았다. ECR은 인터페이스 타입이라 GB당 $0.01이 붙긴 하지만, NAT 통과 $0.045 대비 22% 수준. 시간당 엔드포인트 요금이 AZ마다 $0.01씩 붙는 게 단점인데, 우리 트래픽 수준에선 손익분기점이 한참 아래였다.
resource "aws_vpc_endpoint" "ecr_dkr" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-2.ecr.dkr"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnet.private[*].id
security_group_ids = [aws_security_group.vpce.id]
private_dns_enabled = true
}
resource "aws_vpc_endpoint" "ecr_api" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-2.ecr.api"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnet.private[*].id
security_group_ids = [aws_security_group.vpce.id]
private_dns_enabled = true
}
ECR 엔드포인트 추가할 때 한 번 더 삽질했다. private_dns_enabled = true로 했으면 그냥 도메인 그대로 해석되니까 별 변경 없어도 될 줄 알았는데, 이미 떠 있던 노드의 일부가 DNS 캐시 때문에 한동안 기존 public IP로 계속 붙더라. 결국 노드 한 번씩 돌려서 정리했다. 새벽에 했어야 했는데 그 날 점심시간에 했다가 빌드 파이프라인 일부가 1-2분 흔들렸다. 미안.
한 달 뒤 청구서
다음 청구서가 나왔을 때 NAT Gateway 라인은 원래의 110% 수준이었다. 이전 청구서 대비 65% 감소. 데이터 플랫폼 워크로드는 계속 돌고 있는 상태였는데도. S3 트래픽이 통째로 빠져나간 효과가 압도적이었다.
여기서 더 줄일 수 있는 항목들도 같이 적어둔다. 우리도 아직 다 손은 못 댔다:
- CloudWatch Logs 엔드포인트 (Logs 트래픽이 의외로 많음)
- STS 엔드포인트 (IRSA 켜고 나면 토큰 갱신 트래픽이 NAT 타고 나감)
- SQS / SNS 엔드포인트 (워크로드에 따라)
회고에서 나온 이야기
분기 회고에서 두 가지 합의가 있었다.
첫째, NAT Gateway BytesOutToDestination 메트릭에 CloudWatch 알람을 걸기로 했다. 일별 + 주별 임계치. 비용 청구서를 기다릴 게 아니라 트래픽 자체에 알람을 걸어야 빨리 알아챈다.
둘째, 신규 VPC 만들 때 S3 Gateway Endpoint는 기본으로 박는다. Terraform 모듈 안에 default로 넣어버렸다. 무료인 걸 굳이 안 넣을 이유가 없다.
근데 사실 더 본질적인 교훈은 이거였다. 우리는 트래픽 경로를 너무 안 들여다보고 있었다. NAT Gateway는 그냥 "있는 거"였고 매달 비용이 얼마나 나오는지도 안 봤다. 인프라가 작동만 하면 됐지 비용 라인을 안 본다. 이게 한두 달 누적되면 결국 재무팀이 먼저 본다. 그리고 우리는 부끄러워진다.
VPC Flow Logs는 진작에 켜놨었는데 한 번도 제대로 쿼리해본 적이 없었다. 이번 일 이후로 월 1회 NAT 트래픽 top destination 리포트를 자동화해서 슬랙으로 쏘게 만들었다. 처음 이걸 봤을 때 팀원들 반응이 재밌었다. "어 우리 이걸로 이만큼 돈 쓰고 있었어?" 누구도 안 보던 영역이었다.
혹시 비슷한 경험 있으신 분들, NAT 트래픽에서 의외로 큰 비중 차지하던 destination 뭐 있었는지 댓글 남겨주시면 좋을 것 같다. 우리는 Slack webhook이 의외로 꽤 차지하고 있어서 그것도 다음에 정리할 예정이다.
'IT > AWS' 카테고리의 다른 글
| Karpenter consolidation 때문에 노드가 5분에 한 번씩 죽던 이야기 (0) | 2026.05.08 |
|---|---|
| EKS Pod Identity vs IRSA, 옮길지 말지 (1) | 2026.05.08 |
| VPC Lattice 6개월 써보니 보이는 것들 (2) | 2026.04.30 |
| IRSA에서 EKS Pod Identity로 옮기는 법 (0) | 2026.04.27 |
| NAT Gateway 비용 줄이는 법, VPC Endpoint부터 보자 (0) | 2026.04.25 |