안 받고 있던 할인 - Spot과 Graviton은 왜 싼가
AWS 청구서가 두 달 연속 예산을 넘겼다. 줄일 리소스를 찾다가 진짜 레버는 '얼마나 쓰느냐'가 아니라 '어떻게 사느냐'였음을 알았다. Spot과 Graviton이 왜 싼지, 그리고 낭비처럼 보이지만 끄면 안 되는 비용(RDS Proxy)까지 - 청구서의 각 숫자에 담긴 '왜'에 대한 이야기.
핵심 요약
클라우드 청구서를 줄이라는 말에 손은 먼저 '무엇을 끌까'로 가지만, 리소스가 이미 최적화돼 있으면 남은 레버는 결제 방식이다. Fargate Spot은 AWS가 남는 용량을 '2분 내 회수' 조건으로 떨이하는 것이라 70% 싸고(조건부라 staging에), Graviton은 AWS 자체 설계 ARM 칩이라 중간 마진이 없어 20% 싸다(무조건이라 프로덕션까지). 반대로 RDS Proxy처럼 낭비로 보이지만 커넥션 서지를 막는 보험인 비용도 있다. 청구서를 읽는 일은 숫자를 줄이는 게 아니라 각 숫자의 이유를 아는 일이다 - 조건을 감당하는 할인은 챙기고, 사고를 막는 비용은 남긴다.
목차
AWS 청구서가 두 달 연속 예산을 넘겼다. 넘긴 폭은 한 자릿수 퍼센트라 금액 자체가 큰 건 아니었지만, 방향이 위였다. 어디를 더 줄일까 하고 리소스를 하나씩 뒤졌는데, 결론은 뜻밖이었다. 줄일 리소스가 거의 없었다. 진짜 레버는 "얼마나 쓰느냐"가 아니라 "어떻게 사느냐"에 있었다. 그 과정에서 배운 건, 청구서의 각 숫자에는 이유가 있고 그 이유를 알아야 무엇을 끄고 무엇을 남길지 판단할 수 있다는 것이다.
리소스는 이미 짜여 있었다
먼저 쓰는 양부터 봤다. 그런데 이 부분은 이미 손이 많이 가 있었다. NAT 게이트웨이는 없애고 퍼블릭 서브넷으로 설계돼 있었고, 로그는 7일만 보관, 트레이싱은 1% 샘플링, 관측 대시보드는 무료 한도 안에 욱여넣어져 있었다. DB와 Redis는 이미 더 싼 ARM 인스턴스를 쓰고 있었고, S3는 자동 티어링, 배스천은 필요할 때만 띄우는 방식이었다. 리소스 단위로 짜낼 수 있는 건 거의 다 짜낸 상태였다.
6월 청구서를 항목별로 보면 덩치가 확연히 갈렸다. 가장 큰 건 DB(RDS)로 전체의 40%가량을 혼자 차지했고, 그다음이 컴퓨트(ECS Fargate) 약 17%였다. 로드밸런서와 WAF가 각각 10% 안팎, 퍼블릭 IP·캐시(ElastiCache)·S3가 한 자릿수 퍼센트로 뒤를 이었다. 여기서 인스턴스를 더 작게 줄이는 건 역행이었다. DB는 메모리가 부족해서 한 번 키운 이력이 있었고, WAF는 프로덕션 배포 전에 룰을 검증하려고 의도적으로 둔 것이었다. 남은 레버는 리소스가 아니라 결제 방식이었다. 같은 자원을 그대로 쓰면서, 더 싸게 사는 길.
왜 Spot은 싼가
가장 먼저 눈에 띈 건 컴퓨트를 온디맨드 정가로만 사고 있다는 점이었다. AWS에는 같은 서버를 훨씬 싸게 파는 창구가 있다. Fargate Spot이다. 온디맨드 대비 70%가량 싸다.
그렇게 싼 데는 이유가 있다. AWS는 피크 수요에 맞춰 데이터센터를 지어둔다. 평상시에는 서버가 남아돈다. 남는 용량을 놀리면 수익이 0이니, "정가 손님이 오면 2분 안에 비켜준다"는 조건을 달아 그 남는 용량을 헐값에 파는 것이다. 항공사가 출발 직전 빈 좌석을 떨이로 파는 것과 같은 원리다. 어차피 빈 채로 뜨면 매출이 0이니, 조건을 붙여서라도 파는 게 이득이다.
관건은 그 조건, 즉 "비켜달라면 비켜준다"를 감당할 수 있느냐다. 프로덕션이라면 곤란하다. 유저가 붙어 있는데 태스크가 2분 뒤 회수되면 잠깐이라도 서비스가 흔들린다. 하지만 staging은 다르다. 실제 유저가 없고, 몇 분 다운돼도 QA 한 명이 잠깐 불편할 뿐이다. 그래서 staging의 API를 Spot으로 옮겼다. 성능은 온디맨드와 완전히 동일하다. vCPU도 메모리도 같은 하드웨어에서 돌기 때문이다. 달라지는 건 오직 "회수될 수 있다"는 가용성 조건 하나뿐이고, 그건 staging이 기꺼이 감수하는 조건이었다. 적용하고 나니 staging 태스크가 Spot 위에서 정상적으로 돌기 시작했다.
왜 Graviton은 싼가
두 번째는 컴퓨트의 칩이었다. DB와 Redis는 이미 ARM 인스턴스를 쓰고 있었는데, 정작 애플리케이션을 돌리는 Fargate만 x86에 남아 있었다. AWS의 자체 ARM 칩인 Graviton으로 옮기면 같은 성능을 20%가량 싸게 쓸 수 있다.
이게 싼 이유는 Spot과 결이 다르다. 일반 인스턴스는 AWS가 인텔이나 AMD한테서 CPU를 사다 쓰는 것이라, 우리가 내는 값에 칩 제조사의 마진이 얹혀 있다. Graviton은 AWS가 직접 설계한 칩이라 그 중간 마진이 없다. 게다가 ARM 아키텍처는 같은 연산을 하는 데 전력과 발열이 적어서 데이터센터의 전기·냉각 비용도 덜 든다. AWS 입장에선 원가가 낮으니 20% 싸게 팔아도 남고, 싸게 팔아서 고객을 자기 칩 생태계로 끌어들이는 전략이기도 하다.
중요한 차이가 여기 있다. Spot의 할인에는 "회수를 감수하라"는 조건이 붙지만, Graviton의 할인에는 아무 조건이 없다. 그냥 더 싼 칩이다. 그러니 Spot은 staging에만 쓸 수 있어도, Graviton은 프로덕션까지 그대로 적용된다. 성능도 걱정할 게 별로 없다. Node.js는 Graviton에서 vCPU당 성능이 x86과 동급이거나 오히려 낫다. 유일한 관찰 지점은 콜드스타트다. 앱이 처음 뜰 때는 단일 코어 속도에 민감한데, 여기서 Graviton이 최신 x86보다 조금 느릴 수 있어 기동이 몇 초 늘 수 있다. staging에 먼저 올려 기동 시간을 재본 뒤 프로덕션으로 승격하는 순서를 뒀다. 사전 점검은 네이티브 모듈이 arm64용으로 빌드되는지 하나만 확인하면 된다.
두 할인의 성격을 정리하면 이렇다. Spot은 불편을 감수하는 대가로 받는 할인이라 조건이 붙고(그래서 staging), Graviton은 AWS 자체 칩의 원가 우위를 나눠 받는 할인이라 조건이 없다(그래서 프로덕션까지). 성능이 같은데 굳이 비싼 x86 정가에 남아 있을 이유가 없었다. 사실 이번 최적화는 새로 뭘 아꼈다기보다, 안 받고 있던 할인을 이제야 받기 시작한 것에 가깝다.
끄면 안 되는 비용도 있다
그런데 청구서를 훑다 보면 반대 방향의 함정도 있다. 낭비처럼 보이는데 실은 보험인 항목이다.
RDS Proxy가 그랬다. 매달 고정비가 얼마씩 나가고 있었고, 처음엔 절감 후보로 보였다. Proxy의 기능 중 하나가 시크릿 자동 회전을 무중단으로 처리하는 것인데, 그 회전 기능이 지금 꺼져 있었기 때문이다. 회전을 안 쓰는데 Proxy를 왜 두나 싶었다.
이건 오독이었다. Proxy의 본래 목적은 회전이 아니라 커넥션 서지를 막는 것이었다. 배포나 스케일아웃으로 태스크가 갑자기 늘면 DB로 향하는 커넥션이 급증하는데, DB에는 동시에 받을 수 있는 커넥션 상한이 있다. Proxy는 그 앞에서 커넥션을 모아 다중화해 상한을 넘지 않게 지킨다. 하필 이 DB는 메모리가 부족해서 한 번 키운 이력이 있는 인스턴스였고, 커넥션 하나하나가 메모리를 먹는다. 즉 그 고정비는 낭비가 아니라, 배포 때마다 DB가 커넥션 폭주로 쓰러지지 않게 막아주는 보험료였다. 끄는 순간 아끼는 게 아니라 방어막을 걷어내는 것이었다.
오독의 원인은 코드 주석이었다. 그 Proxy를 설명하는 주석이 회전 관점 위주로 쓰여 있어서, 읽는 사람이 "회전이 꺼졌으니 Proxy도 불필요"로 착각하기 쉬웠다. 그래서 절감안에서 Proxy를 빼는 대신, 주석에 "주 목적은 커넥션 서지 억제, 회전은 부가 기능"이라는 한 줄을 더했다. 다음에 청구서를 보는 사람이 같은 오판을 하지 않도록.
청구서의 각 숫자에는 이유가 있다
클라우드 비용을 줄이라는 말을 들으면 손이 먼저 "무엇을 끌까"로 간다. 인스턴스를 줄이고, 기능을 끄고, 리소스를 지운다. 하지만 이번에 배운 건 그 반대였다. 줄이기 전에 물어야 할 건 "이 숫자는 왜 여기 있는가"다.
어떤 숫자는 안 챙긴 할인이다. Spot과 Graviton처럼, 같은 자원을 더 싸게 사는 창구가 있는데 그냥 정가에 사고 있던 것이다. 이런 할인엔 저마다 싼 이유가 있고, 그 이유(회수를 감수하느냐, 조건 없는 원가 우위냐)를 알면 어디에 안전하게 적용할지가 정해진다. 어떤 숫자는 반대로 끄면 안 되는 보험이다. 낭비처럼 보여도 그게 막고 있는 사고가 있다면, 그 라인아이템은 지출이 아니라 방어다.
결국 청구서를 읽는 일은 숫자를 작게 만드는 일이 아니라, 각 숫자의 이유를 아는 일이다. 이유를 알면, 조건을 감당하는 할인은 챙기고 사고를 막는 비용은 남긴다. 우리가 한 일도 그게 전부였다. 새로 아낀 게 아니라, 받을 할인과 남겨야 할 보험을 구분한 것뿐이다.
자주 묻는 질문
Fargate Spot을 프로덕션에도 써도 되나요?
Spot의 트레이드오프는 2분 경고 후 태스크가 회수될 수 있다는 것입니다. 이 중단을 감당할 수 있는 환경이면 프로덕션에도 쓸 수 있습니다. 태스크가 여러 개인 HA 구성이고 몇 분의 용량 감소가 치명적이지 않다면 부분 적용도 가능합니다. 다만 단일 태스크이거나 중단에 민감한 워크로드라면 위험하니, 중단이 무해한 staging부터 시작하는 것이 안전합니다.
Graviton(ARM)으로 옮기면 성능이 떨어지지 않나요?
대부분의 워크로드에서 동급이거나 오히려 낫습니다. 특히 Node.js는 Graviton에서 vCPU당 성능이 x86과 대등합니다. 유일한 관찰 지점은 콜드스타트로, 앱이 처음 뜰 때 단일 코어 속도가 최신 x86보다 조금 느려 기동이 몇 초 늘 수 있습니다. staging에서 기동 시간을 실측한 뒤 프로덕션으로 승격하고, 네이티브 모듈이 arm64용으로 빌드되는지만 사전 확인하면 됩니다.
관련 글
- 💻 개발
미니 PC에 Docker + Cloudflare Tunnel로 셀프 호스팅하기
집에 있는 미니 PC 한 대로 여러 웹 서비스를 안전하게 공개하는 법. 포트 개방 없이 Cloudflare Tunnel 로 HTTPS 를 붙이고 Docker Compose 로 굴립니다.
- 💻 개발
종료 훅이 두 번 돌고 있었다 - SIGTERM의 주인이 둘일 때
배포마다 Sentry에 Prisma P2028 에러가 떨어졌다. 종료 순서를 고치러 들어갔다가, NestJS의 enableShutdownHooks와 graceful shutdown 라이브러리가 둘 다 SIGTERM을 잡아 종료 훅이 매번 두 번씩 실행되고 있었다는 걸 발견한 이야기.
- 💻 개발
그냥 staging에서 끄면 되잖아 - 시끄러운 알림이 감춘 진짜 버그
staging에서 매일 뜨던 Google Play 404 알림. 소스를 끄면 조용해지지만, 그것과 실패 모드를 고치는 것은 다른 문제였다. 404는 재시도해도 소용없는 영구 오류인데 5xx와 한데 뭉쳐 재시도되고 있었다는 이야기.