🔥 Metric: 숫자 하나가 찍히는 과정

1604자
19분

16:9 가로 일러스트레이션. 가운데에 가로축 'Time'과 세로축 'Value'의 좌표 그리드가 놓여 있고 그 위로 에메랄드 그린과 앰버 톤의 작은 점들을 시간 버킷마다 흩어 표시한다. 그리드 왼쪽에는 슬레이트 그레이 둥근 알약 세 개가 세로로 쌓여 'Namespace', 'MetricName', 'Dimensions[]' 라벨을 달고 있고, 각 알약에서 가는 에메랄드 그린 연결선이 그리드로 들어간다. 그리드 위쪽에는 'PutMetricData → 1 point' 라벨, 아래쪽에는 'Sum / Avg / Min / Max' 시간 버킷 마커가 적혀 있는 흰 배경, 둥근 모서리, 절제된 그림자의 프로페셔널 IT 표지

같은 커스텀 메트릭을 두 번 찍었다고 생각한 적이 있다. 첫 호출에 dimension 값을 service=order 한 줄로 보냈고, 두 번째 호출에는 손이 미끄러져 service=Order로 적었다. 콘솔의 Metrics 트리를 열었더니 같은 메트릭 이름 아래 점이 두 곳에 떠 있었다. 한 점은 service=order 줄에, 다른 한 점은 service=Order 줄에. 한 글자 차이로 같은 메트릭이 두 메트릭이 됐다.

CloudWatch란 무엇인가: AWS 모니터링의 중심 도구에서 메트릭의 형태를 한 줄로 정의했다. namespace + dimension + 시간순 숫자. 그 한 줄을 풀면 점이 어떻게 나뉘는지, 같은 좌표 위에서 어떻게 통계로 모이는지, 시간이 지나면 AWS가 해상도를 어떻게 다시 묶는지가 차례로 드러난다.

namespace, MetricName, Dimensions = 좌표 키

16:9 가로 다이어그램. 왼쪽 슬레이트 그레이 'Coordinate Key' 패널 안에 에메랄드 그린 알약 세 개를 세로로 쌓아 둔다. 위에서 아래로 'Namespace: App/Order', 'MetricName: Latency', 'Dimensions[] (메트릭당 30개까지): service=order, region=ap-northeast-2, env=prod' 세 묶음. 가운데에 '키로 매핑' 라벨이 붙은 화살표가 오른쪽으로 향하고, 오른쪽에는 옅은 옐로우 톤 '시간 버킷 위 점들' 패널 안에 가로 시간축과 세로 Value 축을 그어 둔 그리드가 있고, period 1, 2, 3, 4 네 칸에 에메랄드와 앰버 점들을 흩어 둔다. 아래쪽 슬레이트 캡션 'Namespace, MetricName, Dimensions 조합이 좌표 키. AWS는 같은 좌표 위에 시간순으로 (Value, Timestamp) 점을 쌓는다.'를 적은 흰 배경, 둥근 모서리, 절제된 그림자의 프로페셔널 IT 다이어그램

PutMetricData 한 호출은 네 가지를 함께 보낸다. Namespace 한 줄, MetricData[] 배열, 그 배열의 각 항목인 MetricDatum이 가진 MetricName, Dimensions[], Value, Timestamp, Unit, StorageResolution. 이 중 좌표 키 역할을 하는 건 셋이다. namespace, MetricName, dimensions의 값까지 포함한 조합. 이 조합이 한 글자라도 다르면 CloudWatch는 별도 메트릭으로 잡는다. 글자 단위 식별이라 service=orderservice=Order가 다른 좌표인 이유가 그 때문이다.

dimension 한도는 메트릭당 30개. 한 메트릭 데이터 포인트에 dimension을 30개까지 붙일 수 있다는 뜻이다. 이름과 값 페어 하나가 다르면 그 자체로 새 좌표가 되니, 30개를 다 채우려면 그 메트릭이 정말 그만큼 여러 축으로 나뉘어야 한다는 의미다. 페이로드 한도는 따로 정해져 있다. 한 PutMetricData 호출은 1 MB 안에 들어가야 하고, MetricData[]에는 최대 1,000개 항목이 들어간다. 이 한도들은 한 호출의 한도이지 한 메트릭의 한도가 아니다.

Timestamp은 자유롭게 적을 수 있는 게 아니다. AWS는 과거 2주 이내, 미래 2시간 이내 윈도 안에서만 받는다. 이 윈도가 의미하는 건 두 가지다. 수집기가 잠시 멈췄다 다시 붙으면 잃었던 점을 뒤늦게 같은 좌표로 채워 넣을 수 있다는 점, 그리고 24시간을 넘긴 timestamp로 보낸 점이 콘솔에 나타나려면 최소 48시간이 더 필요하다는 단서가 함께 따라붙는다는 점.

16:9 가로 일러스트레이션. 흰 배경에 부드럽게 곡선을 그리는 긴 트랙이 화면을 가로지르고, 트랙 위로 작은 에메랄드 그린 점 하나가 옅은 흔적을 남기며 이동한다. 트랙 위 세 지점에 슬레이트 그레이 'KEY', 앰버 'TIME BUCKET', 에메랄드 'STATISTIC' 정거장이 있어, 한 점이 좌표에서 시간 버킷, 통계로 이동하는 여정을 작은 운송 비유로 담아낸 편집형 일러스트레이션. 사람도 로고도 없는 절제된 슬레이트, 에메랄드, 앰버 색조의 프로페셔널 IT 일러스트

같은 좌표에 점이 여러 개 들어오면

16:9 가로 다이어그램. 왼쪽 슬레이트 'period 안의 raw 점들' 패널에는 1분 윈도 안에 12개의 에메랄드 점이 흩어져 있고, 'Max ↑', 'Average = Sum / SampleCount', 'Min ↓' 세 개의 가로 점선이 그어져 있다. 가운데 화살표 '자동 집계'가 오른쪽으로 향하고, 오른쪽 옐로우 '버킷에서 나오는 통계' 패널에는 카드 여섯 줄 'SampleCount: 버킷 안 점 개수', 'Sum: 모든 Value의 합', 'Average = Sum / SampleCount (정의식)', 'Minimum: 최솟값', 'Maximum: 최댓값', 'Percentile (p95, p99): raw Value 또는 Values+Counts 필요'가 쌓여 있다. 아래쪽 슬레이트 캡션 'Average는 별도 측정값이 아니라 Sum과 SampleCount의 정의식. percentile은 raw 점이 있을 때만 닿는다.'

같은 좌표(namespace × MetricName × dimensions)에 여러 점이 들어오면, CloudWatch는 그 점들을 period 단위 버킷에 모은다. 버킷 안에서 통계를 자동으로 계산한다. SampleCount(점 개수), Sum(합), Minimum(최솟값), Maximum(최댓값), Average. Average에는 정의식이 있다. Average = Sum / SampleCount. 그래서 같은 메트릭에서 Sum과 Average를 동시에 보면 그 둘은 분리된 숫자가 아니라 같은 데이터의 두 관점이다.

Percentile은 단서가 따라온다. 점들의 원본 분포를 알고 있어야 p95와 p99를 정확히 계산할 수 있어서, 클라이언트가 raw Value를 발행하거나 Values + Counts 배열을 보낼 때만 percentile이 가능하다. 클라이언트가 미리 계산한 StatisticSet(Sum, Min, Max, SampleCount만 들고 들어오는 형태)을 보내면 일반적으로 percentile을 못 얻는다. 단 SampleCount=1이거나 Min=Max인 특수 케이스에서는 점들의 분포가 사실상 결정되니 거기서만 가능하다. percentile이 필요하면 raw로 넣어야 한다는 한 줄을 단단히 정해 두면 된다.

Values + Counts는 한 호출에 한 메트릭당 최대 150개 값을 묶어 보낼 수 있다. 100명 사용자에게 응답한 latency 100점을 한 호출에 묶고 그 좌표에 percentile을 걸면, CloudWatch는 점의 분포 위에 p95와 p99를 다시 계산한다. 그게 raw로 발행하는 이유다.

Standard 60s vs High-resolution 1s

16:9 가로 비교 다이어그램. 가운데를 가는 슬레이트 점선이 좌우로 나누고, 왼쪽 'Standard, StorageResolution = 60' 패널에는 1분 간격 그리드 위에 6개의 에메랄드 점이 일정 간격으로 적혀 있고, 그 아래 그린 토널 박스에 '조회 가능 해상도: 60초의 배수만', '발행 빈도: 1분에 한 번 PutMetricData (배치 가능)', '메트릭 단가: 첫 1만 개까지 $0.30/월' 세 줄이 적혀 있다. 오른쪽 'High-resolution, StorageResolution = 1' 패널에는 1초 간격 그리드 위에 23개의 앰버 점이 촘촘히 적혀 있고, 그 아래 옐로우 토널 박스에 '조회 가능 해상도: 1, 5, 10, 30초 또는 60초의 배수', '발행 빈도: 1초에 한 번, 한 메트릭이 한 달 약 $26 호출비', '메트릭 단가: 동일 $0.30/월 (호출 비용을 그 위에 추가 곱셈)' 세 줄이 적혀 있다. 아래쪽 슬레이트 캡션 '메트릭 단가는 같지만, 1초 해상도는 호출 빈도가 60배라 호출 비용이 청구서를 결정한다.'

StorageResolution 파라미터는 두 값을 받는다. 60이면 standard(분 단위), 1이면 high-resolution(1초 단위). 기본값은 60. 1로 켜면 AWS는 발행 해상도를 1초 단위까지 허용하고, 조회 옵션도 1초, 5초, 10초, 30초 또는 60초의 배수로 폭이 더 넓다. 그제서야 분 단위 미만 조회를 쓸 수 있다.

비용 이야기가 그 옆에 따라온다. 메트릭 한 개의 단가는 standard와 high-resolution 모두 동일하게 첫 1만 개까지 $0.30/월이다. 단가가 같다면 해상도를 항상 1로 켜면 되지 않냐고 생각할 수 있는데, 거기서 비용이 새기 시작한다. 1초 해상도를 진짜 보려면 1초마다 점을 찍어야 한다. PutMetricData는 호출 1,000건당 $0.01이 따로 붙는다. 한 메트릭을 1초마다 찍으면 한 달에 약 260만 호출이 나가고, $0.01/1,000 단가만 계산해도 호출 비용이 약 $26(무료 한도 1M 호출을 빼면 약 $16) 단위로 메트릭 단가 위에 얹는다. 메트릭의 외형은 같지만 그 메트릭이 부르는 청구서의 무게는 해상도 결정 한 줄에 따라 다르다.

시간이 지나면 AWS가 해상도를 다시 묶는다

16:9 가로 timeline 다이어그램. 가운데 가로 띠가 네 개의 색 segment로 이어져 있다. 왼쪽부터 앰버 '1초 (high-res): 3시간', 에메랄드 '1분 (standard): 15일', 시안 '5분 (rolled-up): 63일', 인디고 '1시간 (rolled-up): 455일 ≈ 15개월'. 띠 위쪽으로 't = 0', '3시간', '15일', '63일', '455일' 시간 마커가 점선으로 내려오고, segment 사이마다 곡선 화살표 '5분으로 자동 집계', '1시간으로 자동 집계'가 위로 휘어 다음 segment로 넘어간다. 아래쪽 슬레이트 박스 '읽는 법'에는 '데이터가 사라지는 게 아니라 더 큰 시간 버킷으로 다시 모인다', '분 단위로 찍은 점도 15일이 지나면 5분 해상도로만 조회 가능, 63일이 지나면 1시간 해상도로만 보인다', '보고 싶은 시간 윈도가 곧 발행 해상도를 결정한다' 세 줄이 적혀 있다.

같은 좌표 위에 점이 쌓인 다음에는 보존 일정이 자동으로 진행한다. 공식 문서가 명시하는 형태는 이렇다.

  • 60초 미만 해상도(high-res 1초): 3시간 보존
  • 60초 해상도(standard 1분): 15일 보존
  • 5분(300초) 해상도: 63일 보존
  • 1시간(3600초) 해상도: 455일 ≈ 15개월 보존

여기서 자주 미끄러지는 건 보존 기간이라는 표현이다. AWS는 데이터를 지우지 않고 해상도만 거칠게 다시 만든다. 1분으로 찍은 점도 15일이 지나면 5분 해상도로만 조회할 수 있고, 다시 63일이 지나면 AWS는 1시간 해상도만 노출한다. 점들의 raw 묶음을 같은 좌표 위에서 더 큰 시간 버킷으로 다시 묶는 구조다.

이 타임라인이 정해 두는 한 줄은 보고 싶은 시간 윈도가 곧 발행 해상도를 결정한다 정도다. 분기 단위 추세가 필요한 메트릭을 1초로 찍을 이유가 없다. 1시간 해상도에 자동으로 모일 항목이니까. 반대로 어떤 사고를 분 단위 미만으로 다시 보고 싶다면 그 사고가 벌어지는 동안 1초로 찍어 두지 않으면 사후에 고해상도 데이터가 없다.

언제 이걸 쓰지 말아야 하는가

세 가지 경우에 CloudWatch metric이 정답이 아니다.

첫째, 좌표 카드가 폭발하는 경우. dimension 값에 사용자 ID, 요청 ID, 타임스탬프 같은 고유 식별자를 넣으면 CloudWatch는 한 메트릭을 백만 단위로 나눈다. AWS 문서가 고유성이 큰 필드는 dimension에 넣지 말라고 분명히 적은 이유다. 그럴 때는 메트릭이 아니라 Logs InsightsOpenSearch 쪽이 더 맞는다.

둘째, 분 단위 미만 해상도가 상시로 필요한 경우. 1초 해상도를 메트릭마다 별도 호출로 100개를 켜면 호출 비용만 한 달에 $1,500–$2,500 사이로 자란다(무료 한도 흡수 여부에 따라). 짧은 사고 분석 구간이 아니라 상시 1초 해상도가 필요하다면, 메트릭으로 보지 말고 Prometheus, OpenTelemetry 쪽 timeseries DB를 별도로 두는 편이 맞는다.

셋째, latency p99와 p999가 사용자 SLO를 직접 결정하는 경우. Values + Counts가 한 호출에 150값까지인 한도와 raw 발행을 강제하는 단서를 같이 보면, 트래픽이 큰 시스템의 latency p99를 정밀하게 보려면 클라이언트 쪽이 발행 부담을 그대로 진다. 그럴 때는 Datadog, New Relic 같은 APM 도구가 자기 SDK 안에서 raw를 들고 있다가 직접 percentile을 계산해 보내는 구조라 더 잘 맞는다.

한 점이 만든 좌표가 청구서를 만든다

CloudWatch에서 한 메트릭의 무게는 점의 개수가 아니라 좌표의 카드에 따라 정한다. namespace 한 줄, MetricName 한 줄, dimensions 30개 한도의 조합이 만드는 키가 곧 청구 단위이고, 그 키 위에서 점이 쌓이는 해상도(60s 또는 1s)가 호출 비용과 보존 윈도를 함께 결정한다. 한 점을 찍기 전에 좌표 카드와 발행 해상도 두 줄을 미리 정해 두면 청구서는 그 두 줄을 따라간다.

참고 자료

YouTube 영상

채널 보기
투영과 예측, 그리고 선형 결합 | 선형대수학
트라이(Trie) 자료구조: 파이썬으로 삽입(Insert) 연산 구현하기 | Trie 자료구조 이야기
내적의 기하학적 의미와 코사인 유사도 원리 | 선형대수학
행렬의 기본 연산 - 행렬 덧셈, 스칼라 곱, 전치 | 선형대수학
AI는 왜 수백 차원의 벡터를 사용할까? 고차원 공간과 행렬 | 선형대수학
마지막편, 트라이 노드를 50% 이상 줄이는 방법? 압축 트라이 성능 분석 | Trie 자료구조 이야기
트라이(Trie)에서 단어를 삭제하는 방법 | Trie 자료구조 이야기
숫자 하나가 AI 모델의 운명을 바꾼다? | 선형대수학