Search
🙏

IteratorAgeMilliseconds: 글로벌 크립토 거래소의 실시간 데이터 파이프라인에서 CPU가 아닌 '고객 지연'을 측정해야 하는 이유

들어가며

글로벌 암호화폐 거래소는 24시간 365일 무중단으로 운영되며, 예측 불가능한 시장 이벤트에 대응해야 하는 곳이죠. 게다가 언제 터질지 모르는 시장 이벤트들에 늘 대비해야 합니다. 토큰 가격이 갑자기 폭락하거나, 대규모 청산이 발생하거나, 비트코인 반감기 같은 '블랙스완' 이벤트가 터지면 트래픽이 평소의 수백 배로 치솟는 건 예사일입니다.
이런 살벌한 환경에서 Kinesis Data Streams를 통해 실시간 데이터 파이프라인을 운영할 때, 우리가 흔히 쓰는 CPU 사용률 기반의 오토스케일링은 생각보다 심각한 한계를 드러냅니다. 그래서 오늘은 AWS Kinesis의 GetRecords.IteratorAgeMilliseconds라는 지표가 도대체 무엇인지, 그리고 왜 이 친구가 크립토 거래소의 오토스케일링에서 CPU보다 훨씬 더 중요한지 기술적으로 한 번 살펴보죠.

1. IteratorAgeMilliseconds, 도대체 뭘까요?

1.1 기술적인 정의부터 보자면

GetRecords.IteratorAgeMilliseconds는 Amazon Kinesis Data Streams의 CloudWatch 메트릭 중 하나인데요. 쉽게 말해 "지금 컨슈머가 처리하고 있는 데이터가 얼마나 오래된 데이터냐?"를 밀리초(ms) 단위로 보여주는 겁니다.
공식은 아주 간단합니다.
IteratorAge = 현재 시간 - 마지막으로 읽은 레코드가 스트림에 들어온 시간
위 모니터링 지표 상황:데이터를 처리하는 속도가 데이터를 생산하는 속도를 따라가지 못하고 있어(지연 발생), 스트림 내에 처리되지 않은 레코드가 점점 쌓이고 있는 상황입니다.
세부 메트릭 분석:
1.
Iterator Age 증가 (지연 발생)
Iterator Age가 증가하고 있다는 것은 컨슈머 애플리케이션(데이터를 가져가서 처리하는 쪽)이 프로듀서(데이터를 넣는 쪽)가 데이터를 넣는 속도만큼 빠르게 처리하지 못하고 있음을 의미합니다. 즉, 데이터 처리가 밀리고 있습니다.
Get records 그래프에서도 스트림에 남아있는(처리 대기 중인) 레코드 수가 증가하는 것을 확인할 수 있습니다.
2.
Incoming Data (Count) - 레코드 수 (병목 의심)
Incoming data (count) 메트릭은 스트림으로 들어오는 개별 레코드의 개수를 보여줍니다.
빨간색 선은 현재 단일 샤드(Single Shard)가 처리할 수 있는 최대 용량(한계치)를 나타내는데, 현재 1,000명의 레이서(데이터 소스)가 활동하면서 들어오는 레코드 수가 이 한계치에 거의 도달했습니다.
즉, 초당 들어오는 데이터 건수(TPS)가 샤드 용량 한계에 가까워져 성능 저하의 원인이 될 수 있습니다.
3.
Incoming Data - Sum (Bytes) - 데이터 크기 (여유 있음)
반면, Incoming data – sum (bytes) 그래프는 들어오는 데이터의 총 용량(바이트 크기)을 보여줍니다.
이 수치는 빨간색 선(최대 용량)보다 훨씬 아래에 머물러 있어, 데이터 전송 대역폭(Bandwidth) 측면에서는 아직 충분히 여유가 있는 상태입니다.

1.2 비즈니스 관점에서는 이게 왜 중요할까요?

이건 단순히 기술적인 숫자가 아니에요. 고객이 보고 있는 데이터가 얼마나 낡은 것인지를 적나라하게 보여주는 지표거든요.
0~3초: 아주 쾌적한 실시간 상태입니다.
3~15초: 살짝 느린 감이 오죠? 고객이 "어? 왜 좀 느리지?" 하고 느낄 수 있습니다.
15~20초: 심각합니다. 고객 불만이 터져 나오기 시작하죠.
20초 이상: 이건 장애 상황입니다. 고객들이 경쟁사로 이탈하고, 데이터가 유출되거나, 텔레그램이나 X에서는 “00플랫폼 다운됬다”라는 이야기가 돌아다니죠.

1.3 데이터 유실, 진짜 무서운 이야기

Kinesis Data Streams에는 데이터 보관 기간(Retention Period)이라는 게 있어요. 보통 24시간이고 최대 365일까지 늘릴 수 있죠. 만약 IteratorAge가 이 보관 기간을 넘어가 버리면 어떻게 될까요?
컨슈머가 아직 읽지도 못한 데이터가 스트림 뒤편에서부터 삭제되어 영구적으로 사라집니다. 금융 규제 위반이나 감사 실패로 이어질 수 있는 아찔한 상황인 거죠.

2. 24시간 365일 운영되는 크립토 거래소의 트래픽은 뭐가 다를까요?

2.1 미친듯한 변동성

글로벌 크립토 시장은 전통적인 금융 시장과는 차원이 다릅니다.
평소 새벽에는 TPS(초당 트랜잭션)가 800 정도라면,
비트코인 등 코인의 청산이 발생하거나, 예기치 못하는 상황이 생기면 TPS는 수만을 넘어 수십만까지 튑니다. 무려 수 백에 달할수 있죠.
테라-루나 폭락 사태 때 기억하시나요? 대부분의 메이저 글로벌 거래소가 앱 자체가 접속이 안되거나 거래의 먹통이 발생하였습니다.
주식 시장처럼 장 마감 시간이 있는 것도 아니고, 서킷 브레이커가 항상 작동하는 것도 아니에요. 뉴스 하나 뜨면 단 몇 십초만에 거래량이 폭발합니다.

2.2 실시간성, 타협할 수 없는 가치

트레이더들은 실시간 가격을 보고 매수/매도 버튼을 누릅니다. 차트가 조금만 늦게 업데이트되어도 고객은 바로 떠납니다. 통계를 보면 10초만 지연돼도 고객의 상당수가 이탈한다고 합니다. 상상해보세요. 여러분이 비트코인이나 이더리움을 구매하는데, 호가창이 단 몇초라도 느리다면 그 거래소를 사용할까요? 업비트와 같은 국내 메이저 거래소는 오히려 탑 해외 거래소라는 Coinbase나 Kraken보다 거래 속도나 앱 성능면에서 훨씬 우세합니다. 못믿겠다면, 실제 해당 거래소들을 가입하고 거래 해보시면 얼마나 앱 성능이나 거래 속도가 현격히 느리다는 느낌을 받으실수 있습니다.

3. 왜 CPU 기반 오토스케일링으로는 부족할까요?

3.1 범인은 바로 I/O Wait 때문입니다.

Kinesis 컨슈머가 하는 일을 한번 살펴볼까요?
1.
Kinesis에서 데이터를 읽어오고 (I/O)
2.
데이터를 파싱하고 변환해서 (CPU)
3.
캔들(OHLCV)을 계산한 뒤 (CPU)
4.
DB(TimescaleDB 등)에 저장하고 (I/O)
5.
WebSocket으로 고객에게 쏴줍니다 (I/O)
문제는 거래가 급격히 발생하는 이벤트 때 생깁니다. DB에 저장하고 WebSocket으로 쏘는 I/O 작업 시간이 폭증하죠.

3.2 CPU 메트릭의 착시 현상

이게 진짜 무서운 점인데요. 시스템은 I/O 응답을 기다리느라 멈춰있는데, CPU 입장에서는 "난 할 일 다 했고 그냥 기다리는 중인데?"라며 유휴(Idle) 상태로 잡힙니다.
실제 상황: 데이터가 산더미처럼 쌓여서 차트는 10분이나 지연되고 있음.
CloudWatch: "CPU 사용률 35%, 아주 평온합니다. 스케일링 필요 없어요."
그렇기 때문에 아무리 잘 모니터링 파이프라인을 구축해도, CPU기반 ASG에 의존하면 에러를 즉각적으로 못잡는 상황이 발생합니다.

3.3 HPA의 구조적 한계

Kubernetes의 기본 HPA(Horizontal Pod Autoscaler)는 CPU나 메모리의 '평균'을 봅니다. I/O 병목은 감지도 못하고, 일부 파드가 죽어서 재시작되면 오히려 메모리 평균이 낮아져서 스케일링이 안 되는 역설적인 상황도 발생합니다.

4. IteratorAge 기반 스케일링은 뭐가 다를까요?

4.1 핵심은 '고객 경험'

IteratorAge 기반 스케일링의 철학은 "시스템이 바쁜가?"가 아닙니다. "고객이 몇 초 전 데이터를 보고 있는가?"를 기준으로 삼는 거죠. 고객이 느끼는 지연이 늘어나면, CPU가 놀고 있든 말든 무조건 서버를 늘리는 겁니다.
이러한 강력한 특성 때문에, AWS 공식문서에서도 이런 메커니즘을 강력 추천합니다
AWS 공식 문서에서 따르면, Kinesis 컨슈머 상태를 볼 때GetRecords.IteratorAgeMilliseconds에 알람을 걸어두는 게 모범 사례라고 적혀있죠.

4.3 KEDA와 함께라면 쉽습니다

쿠버네티스 환경이라면 KEDA(Kubernetes Event-Driven Autoscaling)를 쓰면 됩니다. KEDA는 AWS CloudWatch랑 붙어서 이 IteratorAge 지표를 보고 알아서 파드를 늘려주거든요.
설정 파일(ScaledObject)은 대략 이런 식이에요.
triggers: - type: aws-cloudwatch metadata: metricName: GetRecords.IteratorAgeMilliseconds targetMetricValue: "15000" # 15초 넘어가면 늘려!
HCL
복사

5. 임계치(Threshold)는 어떻게 잡을까요?

5.1 SLA 기준으로 역산하기

무작정 정하는 게 아니라, 우리 서비스의 목표(SLA)를 기준으로 계산해야 합니다. 예를 들어, "실시간 차트 업데이트는 3초 이내여야 한다"고 칩시다. 그런데 내부 처리(DB 저장 등)에 최대 8초, 네트워크 전송에 2초, 약간의 버퍼 5초를 둔다면?
KEDA 임계치 = 15,000ms (15초)
즉, 데이터가 15초 이상 밀리면 "어? 이거 곧 사고 나겠는데?"라고 판단하고 즉시 서버를 늘리는 겁니다.

5.2 스케일링 정책: 늘릴 땐 과감하게, 줄일 땐 신중하게

크립토 시장에선 단 몇 초의 거래 지연이 수백만 달러 손실로 이어집니다. 그러니 스케일 아웃은 즉시 반응하게 하고, 스케일 인은 5분 정도 지켜보면서 천천히 줄이는 게 안전합니다. (AWS 아키텍처 시험을 보면, scale-out/scale-in policy에 대한 이런 문제가 나오죠)

7. 운영 꿀팁 몇 가지

7.1 Shard는 미리미리 넉넉하게

Kinesis의 'On-Demand' 모드도 좋지만, 100배씩 튀는 트래픽을 즉시 따라잡긴 힘듭니다. 트래픽이 미친듯이 예상된다면 Shard 개수를 미리 넉넉하게(20% 여유) 확보해 두는 게 좋다고 생각합니다. 물론 이는 각 회사의 정책과 서비스를 추구하는 방식에 따라 다르겠죠.

7.2 Enhanced Fan-Out (EFO) 활용

실시간성이 진짜 중요한 차트 업데이트 같은 기능Enhanced Fan-Out을 쓰는 전략도 많이 사용되죠. 전용 파이프라인을 뚫어주는 거라 다른 컨슈머 영향 없이 쾌적하게 데이터를 받을 수 있습니다. Kinesis의 숨겨진 터보 버튼 (Enhanced Fan-Out)

7.3 하류 시스템(Downstream) 보호

무조건 컨슈머만 늘린다고 능사가 아닙니다. DB가 터지려고 하는데 컨슈머만 늘리면 오히려 DB에 기름을 붓는 격이 될 수 있습니다. 서킷 브레이커를 적용해서 DB가 힘들 때는 잠깐 쉬어갈 수 있게 해줄 수 있어야 됩니다. 애플리케이션 레벨에서도 가능하지만 인프라 단에서 물리적으로 DB Connection Pool을 제한 함으로써도 가능합니다. 쉽게 말해서, Consumer Pods와 RDS DB사이에 RDS Proxy를 하나 중간에 배치해서, DB가 감당 가능한 만큼만 쿼리를 흘러보내는거죠. 흔히 아는 Proxy, Reverse Proxy처럼 한 단계가 중간에 있다고 생각하면 됩니다.

나가며

요약하자면, CPU 사용률은 시스템이 얼마나 '바쁜지'를 보여주지만, IteratorAge는 고객이 느끼는 '품질'을 보여줍니다. 모니터링 시에도 단순 CPU/RAM 지표로 측정하는게 아니라, 여러가지 상황별 지표를 적절히 설정함으로써, 오탐을 줄이면서도 숨은 범인을 찾을 수 있습니다.