이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Counter, Gauge, Histogram은 각각 어떤 운영 질문에 답하는가?
  • Spring 백엔드에서 Micrometer metric을 설계할 때 label을 왜 조심해야 하는가?
  • 평균 latency 대신 histogram과 percentile을 봐야 하는 이유는 무엇인가?

개요

Metrics는 애플리케이션 상태와 요청 흐름을 숫자로 집계한 신호다.

로그가 개별 사건을 설명한다면, 메트릭은 사건이 얼마나 자주, 얼마나 크게, 어떤 추세로 발생하는지 보여 준다.

Counter, Gauge, Histogram은 서로 다른 질문에 답한다.

이 차이를 모르면 잘못된 metric type으로 dashboard와 alert를 만들게 된다.

Counter

Counter는 계속 증가하는 누적 횟수다.

요청 수, 오류 수, 주문 생성 수, retry 수처럼 “몇 번 발생했는가”를 셀 때 쓴다.

Counter.builder("order_created_total")
        .tag("result", "success")
        .register(meterRegistry)
        .increment();

Counter 자체 값보다 일정 시간 동안의 증가율이 중요하다.

예를 들어 5xx counter의 rate가 갑자기 증가하면 장애 신호가 된다.

Gauge

Gauge는 현재 값을 나타낸다.

queue size, active connection, thread pool active count, cache size처럼 위아래로 움직이는 값을 표현한다.

AtomicInteger queueSize = new AtomicInteger();
 
Gauge.builder("order_queue_size", queueSize, AtomicInteger::get)
        .register(meterRegistry);

Gauge는 순간값이므로 scrape 시점에 따라 달라질 수 있다.

짧게 튀는 값을 놓칠 수 있으므로 alert 기준을 바로 걸 때는 주의한다.

Histogram과 Timer

Histogram은 값의 분포를 본다.

Spring에서는 요청 latency를 Timer로 기록하는 경우가 많다.

Timer.Sample sample = Timer.start(meterRegistry);
try {
    orderService.create(command);
} finally {
    sample.stop(Timer.builder("order_create_duration")
            .publishPercentileHistogram()
            .tag("result", "success")
            .register(meterRegistry));
}

평균 latency는 일부 느린 요청을 숨긴다.

p95, p99 같은 percentile은 사용자가 체감하는 tail latency를 보는 데 중요하다.

Label 설계

Metric label은 분석 기준이지만 비용과 성능에 직접 영향을 준다.

method, status, endpoint, exception, region처럼 값 종류가 제한된 label은 유용하다.

user_id, order_id, email, trace_id처럼 값 종류가 계속 늘어나는 label은 위험하다.

cardinality가 폭증하면 저장 비용이 커지고 query가 느려진다.

개별 요청 추적은 metric label이 아니라 log와 trace가 맡아야 한다.

Spring 기본 Metric

Spring Boot와 Micrometer는 HTTP server request metric을 제공한다.

curl -fsS http://localhost:8080/actuator/prometheus | grep http_server_requests

이 metric으로 method, uri, status, exception, outcome 기준 요청 수와 latency를 볼 수 있다.

단, uri label이 path variable 값을 그대로 포함하면 cardinality 문제가 생긴다.

/orders/{id}처럼 template 기준으로 잡히는지 확인한다.

운영 체크리스트

  • Counter, Gauge, Histogram을 질문에 맞게 선택했는가?
  • HTTP 요청 수, 오류율, latency p95/p99를 볼 수 있는가?
  • label에 user id, order id, trace id 같은 고유 값이 들어가지 않는가?
  • endpoint label이 path template 기준으로 제한되는가?
  • business metric과 technical metric을 구분했는가?
  • metric 이름과 단위가 문서화되어 있는가?
  • alert가 단일 순간값이 아니라 지속 시간과 함께 정의되어 있는가?

장애 신호

  • 평균 latency는 정상인데 p99가 크게 상승한다.
  • 5xx counter rate가 증가하지만 endpoint label이 없어 범위를 모른다.
  • queue gauge가 계속 증가하고 consumer 처리량은 그대로다.
  • metric series 수가 급증해 수집 시스템이 느려진다.
  • uri label에 실제 주문 번호가 들어간다.
  • alert가 순간 spike마다 울려 담당자가 무시하기 시작한다.

안전한 완화 조치

Metric cardinality 문제가 보이면 먼저 새 label 추가를 멈춘다.

고유 값 label을 제거하고 로그나 trace로 이동한다.

Latency 장애는 평균이 아니라 histogram과 percentile을 확인한다.

Gauge 기반 alert는 짧은 spike에 흔들리지 않도록 지속 시간을 둔다.

Metric 이름을 바꿔야 한다면 dashboard와 alert query도 함께 수정한다.

인프라 담당자와 공유할 자료

증상: 주문 API latency 증가
metric: http_server_requests_seconds
scope: uri=/api/orders, method=POST, status=2xx
baseline: p95 240ms
current: p95 920ms, p99 2400ms
related metric: order_queue_size 20 -> 180
의심: queue 적체 또는 downstream 지연

메트릭 공유는 metric 이름, label 조건, baseline, current value, 관측 시간을 함께 적어야 한다.

실전 팁

  • Counter는 rate로 본다.
  • Gauge는 현재 상태지만 순간값임을 기억한다.
  • Latency는 평균보다 percentile을 본다.
  • 고유 식별자는 metric label에 넣지 않는다.
  • metric 이름에는 단위나 의미가 드러나게 한다.

위험 신호!

  • 모든 값을 label로 넣는다.
  • p95/p99 없이 평균 latency만 본다.
  • alert 기준에 지속 시간이 없다.
  • dashboard 그래프 이름만 있고 query를 모른다.
  • business metric과 technical metric을 섞어 해석한다.

확인 질문

확인 질문

  • Counter와 Gauge의 차이는 무엇인가?
    • Counter는 누적 횟수이고 Gauge는 현재 상태 값이다.
  • Histogram이 latency 분석에 필요한 이유는 무엇인가?
    • 평균으로는 일부 느린 요청을 숨길 수 있어 p95, p99 같은 분포 기반 지표로 tail latency를 봐야 하기 때문이다.
  • Metric label에 user id를 넣으면 왜 위험한가?
    • 값 종류가 사용자 수만큼 늘어 cardinality가 폭증하고 저장 비용과 query 성능 문제가 생기기 때문이다.

참고 문서