본문으로 건너뛰기

1.3 Metric 모델과 Cardinality

Prometheus의 데이터 단위는 metric 이름과 label 집합으로 식별되는 time series입니다. 같은 metric이라도 label 조합이 달라지면 별도 series가 됩니다.

pg_stat_database_xact_commit{instance="db-a:9187",datname="orders"} 184203
pg_stat_database_xact_commit{instance="db-a:9187",datname="users"} 92117

위 두 줄은 이름이 같지만 datname이 달라 서로 다른 series입니다.

Metric type​

Counter​

Counter는 process가 재시작될 때를 제외하면 누적 증가합니다. transaction 수, 읽은 block 수, deadlock 누계가 대표적입니다. 현재값을 그대로 비교하지 않고 시간당 증가율을 계산합니다.

rate(pg_stat_database_xact_commit{datname!~"template.*"}[5m])

rate()는 reset을 고려하므로 두 시점의 값을 직접 빼는 것보다 안전합니다.

Gauge​

Gauge는 오르내리는 현재 상태입니다. connection 수, database 크기, replica lag에 사용합니다. Gauge에 rate()를 적용하면 문법상 실행되더라도 의미 없는 결과가 나올 수 있습니다.

pg_stat_database_numbackends{datname="orders"}

Histogram​

Histogram은 관측값을 bucket에 누적해 분포를 보존합니다. API latency처럼 p95/p99가 필요한 곳에 적합합니다. PostgreSQL Exporter의 통계 view 대부분은 gauge나 counter로 변환되므로, end-to-end query latency 분포는 애플리케이션 계측이나 별도 proxy에서 얻는 편이 낫습니다.

Series 수 계산​

한 metric에 instance 20개, database 30개, user 50개, queryid 5,000개가 붙으면 이론상 조합은 다음과 같습니다.

20 × 30 × 50 × 5,000 = 150,000,000 series

실제 조합이 모두 생기지 않더라도 queryid, SQL 원문, user처럼 값의 종류가 많은 label은 빠르게 비용을 키웁니다. Prometheus는 각 고유 label 조합을 별도로 저장하고 색인합니다.

instance/database/user/queryid label 차원이 곱해져 시계열 수가 늘어나는 구조

PostgreSQL에서 위험한 label​

다음 값은 metric label보다 log나 trace에 두는 것이 안전합니다.

  • SQL 원문
  • session ID와 process ID
  • transaction ID
  • client IP 전체
  • table 이름을 무제한으로 수집한 값
  • tenant/user/request ID

pg_stat_statements를 metric으로 내보낼 때 queryid도 cardinality를 만듭니다. collector의 statement 수 제한을 사용하고 SQL 원문 포함은 기본적으로 끕니다.

좋은 label 설계​

운영 중 실제로 집계하거나 필터링할 차원만 남깁니다.

environment="prod"
cluster="orders-primary"
instance="db-a:9187"
datname="orders"
role="primary"

prod_orders_primary_db_a처럼 모든 차원을 metric 이름에 합치지 않습니다. 이름은 측정 대상을 표현하고, 배포 환경과 instance는 label로 구분합니다.

Prometheus 공식 naming guide에 따라 시간은 seconds, 크기는 bytes, 비율은 0부터 1 사이 값으로 통일하면 서로 다른 dashboard와 rule을 조합하기 쉽습니다.

Cardinality를 점검하는 질문​

새 collector나 label을 추가하기 전에 확인합니다.

  1. label 값의 종류는 현재 몇 개이며 계속 증가하는가?
  2. instance/database/table 수를 곱하면 series가 몇 개 생기는가?
  3. 이 차원으로 alert하거나 집계할 일이 실제로 있는가?
  4. 같은 질문을 log query로 늦게 조회해도 되는가?
  5. scrape 시간과 scrape_samples_post_metric_relabeling이 얼마나 증가했는가?

참고: Prometheus Metric and label naming, Prometheus Instrumentation