# BAD — cardinality bomb
http_requests_total{user_id="u-9281", path="/api/v1/orders/55102"} 1
# GOOD — bounded dimensions
http_requests_total{route="/api/v1/orders/:id", method="POST", status="201"} 1
instrument 할 것
instrument 하지 말 것
service, job, env
개별 tenant (수천 이상)
method, status class (2xx/5xx)
raw status 600종 (가능하면 class)
route template
full query string
RED는 rate·errors·duration — 차원은 서비스 경계 수준
디버그용 일시 high-card metric은 별도 job + 짧은 retention (비권장 기본)
3. 좋은 라벨 설계
1. 질문을 먼저 — "어떤 PromQL·알람에 쓸 라벨인가?"
2. bounded 값만 — enum·template·status class
3. naming convention — snake_case, Prometheus exporter 관례
4. cardinality 예산 — 서비스당 목표 series 수 (예: 5k)
5. code review — /metrics PR에 라벨 diff
라벨
용도
job, instance
scrape target (Prometheus 부여)
service, team
ownership·대시보드 변수
env, region
staging/prod 분리
method, route, status
RED drill-down
# exposition (요지) — OpenMetrics/Prometheus text format
# TYPE http_requests_total counter
http_requests_total{service="api",method="GET",route="/health",status="200"} 42
http_request_duration_seconds_bucket{service="api",route="/health",le="0.1"} 38
http_request_duration_seconds_bucket{service="api",route="/health",le="0.5"} 41
http_request_duration_seconds_bucket{service="api",route="/health",le="+Inf"} 42
http_request_duration_seconds_sum{service="api",route="/health"} 3.2
http_request_duration_seconds_count{service="api",route="/health"} 42
histogram bucket 수는 10~20 이내 — p99 목표에 맞춰 le 선택
code/ — bad-metrics.exposition, good-metrics.exposition 대비
4. 완화 — relabel · aggregate · recording
scrape / metric relabel
# prometheus.yml — drop high-card label at ingest
scrape_configs:
- job_name: api
metric_relabel_configs:
- source_labels: [user_id]
regex: .+
action: labeldrop
- source_labels: [path]
regex: '/users/[0-9]+'
target_label: route
replacement: '/users/:id'
action
용도
labeldrop
위험 라벨 수집 전 제거
labelkeep
allowlist만 유지
replace
path → route 정규화
recording rule
# rules/recording.yml
groups:
- name: api_agg
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum by (job, status) (rate(http_requests_total[5m]))
대시보드·알람은 aggregated metric만 조회 — raw high-card 드물게
앞서 Grafana SLO 패널 — recording으로 job:error_rate5m pre-compute
기타
방법
설명
federation / remote write
tier별 보관 — raw short, agg long
logs for detail
breakdown은 LogQL — 메트릭은 요약
exemplars
histogram + trace link — trace_id 라벨 아님
5. 관측·거버넌스
# TSDB head series (Prometheus 2.x)
prometheus_tsdb_head_series
# top cardinality by metric name (예시 패턴)
topk(20, count by (__name__) ({__name__=~".+"}))
실무
PR checklist
새 라벨·새 metric — cardinality estimate
lint
promtool check rules, Grafana dashboard review
budget alert
prometheus_tsdb_head_series > threshold
postmortem
cardinality spike 원인·relabel timeline
# exposition 크기 확인
curl -s localhost:8080/metrics | wc -c
# unique series per metric (rough)
curl -s localhost:8080/metrics | grep -v '^#' | cut -d'{' -f1 | sort | uniq -c | sort -rn | head
6. 실무 체크리스트
항목
권장
라벨
bounded — route template, status class
ID
로그·트레이스 — metric label 금지
histogram
bucket 최소 — native histogram 검토 (고급)
relabel
ingest에서 drop/normalize
dashboard
sum by (job, status) — raw instance 나열 지양
ownership
팀별 series budget
안티패턴
debug label in prod
user_id, trace_id
copy-paste path
/a/1, /a/2 …
metric per feature flag value
flag 수 × route 폭발
no cardinality review
배포 후 TSDB OOM
7. 정리
cardinality = 시계열 수 — Prometheus·Grafana 성능의 핵
user_id·trace_id·raw path — 메트릭이 아니라 로그/트레이스 영역
RED 라벨은 서비스·route template·status 수준
relabel·recording rule·aggregation으로 완화
Part 2 메트릭 마무리 — 다음 분산 트레이싱 (span, trace, propagation)
다음에 다룰 것
분산 트레이싱 개요
span, trace, context propagation
해당 내용은 Observability Engineering (Charity Majors, Liz Fong-Jones, George Miranda) 및 Prometheus Documentation (prometheus.io/docs) 의 내용을 기반으로 합니다.