본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: 메트릭(Metrics) 수집과 분석
2026년 2월 14일·인프라·

4장: 메트릭(Metrics) 수집과 분석

OpenTelemetry 메트릭의 종류(Counter, Gauge, Histogram), 카디널리티 관리, Exemplars를 통한 메트릭-트레이스 연결, Prometheus 호환을 학습합니다.

14분696자9개 섹션
monitoringobservability
공유
opentelemetry4 / 11
1234567891011
이전3장: 분산 추적(Distributed Tracing)다음5장: 로그 통합과 상관관계

학습 목표

  • OpenTelemetry 메트릭의 종류(Counter, Gauge, Histogram, UpDownCounter)를 이해합니다
  • 수집 주기와 집계(Aggregation) 방식을 학습합니다
  • 카디널리티(Cardinality)의 개념과 관리 전략을 파악합니다
  • Exemplars를 활용한 메트릭-트레이스 연결 방법을 학습합니다
  • Prometheus와의 호환성과 OTLP 내보내기를 실습합니다

메트릭이란

**메트릭(Metrics)**은 시간에 따른 수치 측정값입니다. CPU 사용률, 요청 처리량, 응답 지연 시간 같은 지표를 일정 주기로 수집하여 시스템의 건강 상태를 파악합니다. 트레이스가 개별 요청의 상세 경로를 보여준다면, 메트릭은 전체 시스템의 통계적 상태를 보여줍니다.

메트릭의 핵심 장점은 고정된 저장 비용입니다. 요청 수가 아무리 많아도 메트릭은 집계된 숫자 하나로 표현되므로, 트레이스나 로그에 비해 저장과 쿼리 비용이 훨씬 낮습니다.


OpenTelemetry 메트릭 종류

Counter — 단조 증가 카운터

Counter는 오직 증가만 하는 누적 카운터입니다. 요청 수, 처리된 바이트 수, 에러 발생 횟수처럼 "총 누적량"을 측정할 때 사용합니다.

counter_example.py
python
from opentelemetry import metrics
 
meter = metrics.get_meter("order-service", "1.0.0")
 
# Counter 생성
request_counter = meter.create_counter(
    name="http.server.request.count",
    description="Total number of HTTP requests received",
    unit="requests",
)
 
# 요청이 들어올 때마다 증가
def handle_request(method, path, status_code):
    request_counter.add(1, {
        "http.request.method": method,
        "url.path": path,
        "http.response.status_code": status_code,
    })

Counter의 값은 절대로 감소하지 않습니다. "현재 초당 요청 수"를 알고 싶다면 Prometheus의 rate() 함수처럼 시간 구간 대비 증가율을 계산합니다.

UpDownCounter — 양방향 카운터

UpDownCounter는 증가와 감소가 모두 가능한 카운터입니다. 현재 활성 연결 수, 큐에 대기 중인 작업 수처럼 "현재 진행 중인 양"을 측정합니다.

updowncounter_example.py
python
active_connections = meter.create_up_down_counter(
    name="http.server.active_requests",
    description="Number of active HTTP requests being processed",
    unit="requests",
)
 
def handle_request():
    active_connections.add(1)  # 요청 시작
    try:
        process()
    finally:
        active_connections.add(-1)  # 요청 완료

Gauge — 순간 값

Gauge는 특정 시점의 순간 값을 기록합니다. CPU 사용률, 메모리 사용량, 온도처럼 "지금 현재의 상태"를 나타냅니다.

gauge_example.py
python
# 비동기 Gauge -- 콜백으로 값 수집
def get_cpu_usage(options):
    import psutil
    cpu_percent = psutil.cpu_percent()
    options.observe(cpu_percent, {"host.name": "web-01"})
 
cpu_gauge = meter.create_observable_gauge(
    name="system.cpu.utilization",
    callbacks=[get_cpu_usage],
    description="Current CPU utilization percentage",
    unit="%",
)
 
# 비동기 방식으로 메트릭 수집 주기마다 콜백이 호출됨
Info

OpenTelemetry에서 Gauge는 기본적으로 비동기(Observable) 방식입니다. SDK가 메트릭을 내보내는 시점에 콜백 함수를 호출하여 현재 값을 수집합니다. 이는 Prometheus의 Gauge와 다른 접근 방식이므로 주의가 필요합니다.

Histogram — 분포 측정

Histogram은 값의 분포를 측정합니다. 응답 지연 시간, 요청 크기, 처리 시간의 분포를 버킷(bucket)으로 나누어 기록합니다. 평균뿐 아니라 p50, p90, p99 같은 백분위수(percentile)를 계산할 수 있어 성능 분석에 필수적입니다.

histogram_example.py
python
import time
 
request_duration = meter.create_histogram(
    name="http.server.request.duration",
    description="Duration of HTTP server requests",
    unit="s",
)
 
def handle_request(method, path):
    start = time.time()
    try:
        result = process_request()
    finally:
        duration = time.time() - start
        request_duration.record(duration, {
            "http.request.method": method,
            "url.path": path,
        })

기본 버킷 경계값은 다음과 같습니다.

text
[0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000]

HTTP 응답 시간처럼 밀리초 단위의 측정에는 커스텀 버킷이 더 적합합니다.

custom_buckets.py
python
from opentelemetry.sdk.metrics.view import View, ExplicitBucketHistogramAggregation
 
# 커스텀 버킷 설정
latency_view = View(
    instrument_name="http.server.request.duration",
    aggregation=ExplicitBucketHistogramAggregation(
        boundaries=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
    ),
)

메트릭 종류 요약

종류방향동기/비동기대표 용도
Counter단조 증가동기요청 수, 에러 수, 전송 바이트
UpDownCounter양방향동기활성 연결, 큐 크기, 스레드 수
HistogramN/A (분포)동기응답 시간, 요청 크기
Gauge양방향비동기CPU 사용률, 메모리 사용량
ObservableCounter단조 증가비동기시스템 카운터(네트워크 패킷 등)

카디널리티 관리

**카디널리티(Cardinality)**는 속성 값의 고유한 조합 수를 의미합니다. 메트릭 속성의 카디널리티가 높으면 저장해야 할 시계열(time series)의 수가 폭발적으로 증가합니다.

카디널리티 폭발 예시

cardinality_explosion.py
python
# 나쁜 예 -- 카디널리티 폭발
request_counter.add(1, {
    "http.request.method": "GET",      # 5가지 정도
    "url.path": "/users/12345",        # 수백만 가지 (사용자 ID 포함)
    "user.id": "usr-98765",            # 수백만 가지
    "http.response.status_code": 200,  # 수십 가지
})
# 시계열 수: 5 x 1,000,000 x 1,000,000 x 20 = 폭발
 
# 좋은 예 -- 카디널리티 관리
request_counter.add(1, {
    "http.request.method": "GET",      # 5가지
    "url.template": "/users/:id",      # 수십 가지 (경로 패턴)
    "http.response.status_code": 200,  # 수십 가지
})
# 시계열 수: 5 x 30 x 20 = 3,000 (관리 가능)
Warning

카디널리티 폭발은 Prometheus 같은 메트릭 백엔드의 OOM(Out of Memory)을 유발하는 가장 흔한 원인입니다. 메트릭 속성에 사용자 ID, 요청 ID, IP 주소 같은 고유 값을 절대 포함하지 마세요. 이런 상세 정보는 트레이스 속성으로 기록하는 것이 적합합니다.

카디널리티 관리 전략

  1. 속성 값 정규화 — URL 경로를 패턴으로 변환 (/users/123 → /users/:id)
  2. 고카디널리티 속성 제거 — View를 사용하여 불필요한 속성 제거
  3. 속성 값 제한 — 허용 목록(allowlist)으로 속성 값 범위 제한
  4. Collector에서 필터링 — attributes 프로세서로 속성 삭제/변환
view_cardinality.py
python
from opentelemetry.sdk.metrics.view import View
 
# 특정 메트릭에서 고카디널리티 속성 제거
cardinality_view = View(
    instrument_name="http.server.request.duration",
    attribute_keys=["http.request.method", "http.response.status_code"],
    # url.path 같은 고카디널리티 속성은 자동 제거
)

Exemplars — 메트릭에서 트레이스로

Exemplars는 메트릭 데이터 포인트에 연결된 트레이스의 참조입니다. "p99 지연 시간이 2초를 넘었다"는 메트릭 알림을 받았을 때, 해당 시점의 느린 트레이스를 바로 조회할 수 있게 해주는 핵심 연결 고리입니다.

Exemplar 설정

exemplar_config.py
python
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics.exemplar import AlwaysOnExemplarFilter
 
# Exemplar 필터 활성화
provider = MeterProvider(
    metric_readers=[
        PeriodicExportingMetricReader(
            OTLPMetricExporter(endpoint="http://collector:4317"),
            export_interval_millis=10000,
        )
    ],
    exemplar_filter=AlwaysOnExemplarFilter(),
)
Tip

Exemplars는 Grafana에서 특히 강력합니다. Prometheus 메트릭 그래프에서 Exemplar 점을 클릭하면 Tempo의 해당 트레이스로 바로 이동할 수 있습니다. 이 연결은 메트릭 이상 탐지에서 근본 원인 분석(RCA)까지의 시간을 크게 단축합니다.


수집 주기와 집계

수집 주기(Export Interval)

메트릭은 일정 주기로 집계되어 내보내집니다. 기본 수집 주기는 60초이며, 환경에 따라 조정할 수 있습니다.

export_interval.py
python
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
 
reader = PeriodicExportingMetricReader(
    exporter=OTLPMetricExporter(endpoint="http://collector:4317"),
    export_interval_millis=15000,  # 15초마다 내보내기
    export_timeout_millis=10000,   # 내보내기 타임아웃 10초
)

집계 임시성(Aggregation Temporality)

OpenTelemetry 메트릭은 두 가지 집계 임시성을 지원합니다.

임시성설명호환 백엔드
Cumulative프로세스 시작부터의 누적값Prometheus (기본)
Delta마지막 내보내기 이후의 변화량Datadog, Dynatrace

Prometheus와 연동할 때는 Cumulative, 상용 APM과 연동할 때는 Delta가 일반적입니다. OTLP Exporter에서 설정할 수 있습니다.


Prometheus 호환

OpenTelemetry 메트릭은 Prometheus와의 호환성을 깊이 고려하여 설계되었습니다.

Collector에서 Prometheus로 내보내기

collector-prometheus.yaml
yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"
 
exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
    namespace: "otel"
    const_labels:
      environment: "production"
 
service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]
prometheus.yml
yaml
scrape_configs:
  - job_name: "otel-collector"
    scrape_interval: 15s
    static_configs:
      - targets: ["collector:8889"]

메트릭 이름 변환

OTel 메트릭 이름은 Prometheus 형식으로 자동 변환됩니다.

OTel 이름Prometheus 이름
http.server.request.duration (Histogram, 단위: s)otel_http_server_request_duration_seconds
http.server.request.count (Counter)otel_http_server_request_count_total
system.cpu.utilization (Gauge, 단위: %)otel_system_cpu_utilization_percent

네임스페이스 접두사(otel_), 단위 접미사(_seconds, _total), 점을 밑줄로 변환하는 규칙이 자동 적용됩니다.


SDK 초기화 전체 예제

트레이스와 메트릭을 동시에 설정하는 전체 초기화 코드입니다.

telemetry_setup.py
python
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.metrics.view import View, ExplicitBucketHistogramAggregation
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource
 
 
def setup_telemetry():
    resource = Resource.create({
        "service.name": "payment-service",
        "service.version": "2.0.0",
        "deployment.environment": "production",
    })
 
    # Traces
    trace_provider = TracerProvider(resource=resource)
    trace_provider.add_span_processor(
        BatchSpanProcessor(OTLPSpanExporter(endpoint="http://collector:4317"))
    )
    trace.set_tracer_provider(trace_provider)
 
    # Metrics
    latency_view = View(
        instrument_name="http.server.request.duration",
        aggregation=ExplicitBucketHistogramAggregation(
            boundaries=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
        ),
    )
    
    metric_reader = PeriodicExportingMetricReader(
        OTLPMetricExporter(endpoint="http://collector:4317"),
        export_interval_millis=15000,
    )
    
    metric_provider = MeterProvider(
        resource=resource,
        metric_readers=[metric_reader],
        views=[latency_view],
    )
    metrics.set_meter_provider(metric_provider)
 
    return trace.get_tracer("payment-service"), metrics.get_meter("payment-service")

정리

이번 장에서는 OpenTelemetry 메트릭의 네 가지 종류(Counter, UpDownCounter, Histogram, Gauge)와 각각의 용도를 학습했습니다. 카디널리티 관리의 중요성과 View를 통한 제어 방법을 알아보았고, Exemplars를 통해 메트릭에서 트레이스로 연결하는 방법도 살펴보았습니다. Prometheus와의 호환성도 확인하여 기존 Prometheus 기반 인프라와의 통합 방법을 파악했습니다.

다음 장에서는 3대 신호의 마지막인 로그(Logs)를 다룹니다. OTel 로그 데이터 모델, 로그와 트레이스의 상관관계, 기존 로거(Python logging, Go slog)와의 브릿지 방법을 학습합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#monitoring#observability

관련 글

인프라

5장: 로그 통합과 상관관계

OpenTelemetry 로그 데이터 모델, 로그-트레이스 상관관계, 기존 로거 브릿지(Python logging, Go slog), 구조화 로그와 로그 레벨 전략을 학습합니다.

2026년 2월 16일·14분
인프라

3장: 분산 추적(Distributed Tracing)

스팬의 내부 구조와 종류, 부모-자식 관계, 샘플링 전략(Head/Tail/Rate)을 학습하고 Python으로 분산 추적을 직접 구현합니다.

2026년 2월 12일·15분
인프라

6장: OTel SDK 계측 실전

자동 계측과 수동 계측의 차이를 이해하고, Python/Node.js/Go 각 언어별 SDK 활용법과 커스텀 스팬/메트릭 생성을 실습합니다.

2026년 2월 18일·11분
이전 글3장: 분산 추적(Distributed Tracing)
다음 글5장: 로그 통합과 상관관계

댓글

목차

약 14분 남음
  • 학습 목표
  • 메트릭이란
  • OpenTelemetry 메트릭 종류
    • Counter — 단조 증가 카운터
    • UpDownCounter — 양방향 카운터
    • Gauge — 순간 값
    • Histogram — 분포 측정
    • 메트릭 종류 요약
  • 카디널리티 관리
    • 카디널리티 폭발 예시
    • 카디널리티 관리 전략
  • Exemplars — 메트릭에서 트레이스로
    • Exemplar 설정
  • 수집 주기와 집계
    • 수집 주기(Export Interval)
    • 집계 임시성(Aggregation Temporality)
  • Prometheus 호환
    • Collector에서 Prometheus로 내보내기
    • 메트릭 이름 변환
  • SDK 초기화 전체 예제
  • 정리