메트릭 및 모니터링¶
이 문서는 Continuum Router의 메트릭 및 모니터링 기능을 설명합니다.
목차¶
개요¶
Continuum Router는 시스템 상태, 성능, 사용 패턴을 다룰 수 있도록 Prometheus 호환 메트릭을 제공합니다. 메트릭 시스템은 다음과 같이 설계되었습니다:
- 가벼움: 최소한의 성능 오버헤드
- 넓은 범위: 라우터의 모든 중요한 측면 포함
- 프로덕션 준비: 카디널리티 제한 및 적절한 레이블링 포함
- 쉬운 통합: 표준 Prometheus/Grafana 설정과 작동
메트릭은 얼마나 많이, 얼마나 자주를 답합니다. 요청 하나의 인과관계, 즉 어떤 백엔드가 선택됐고 몇 번 시도했으며 각 시도가 왜 그렇게 끝났는지를 보려면 분산 트레이스 내보내기를 참고하세요. 라우터의 스팬을 OTLP로 OpenTelemetry 컬렉터에 보내는 기능입니다.
빠른 시작¶
1. 메트릭 활성화¶
메트릭은 기본적으로 활성화되어 있습니다. 메트릭 엔드포인트는 /metrics에서 사용할 수 있습니다:
2. Prometheus 설정¶
prometheus.yml에 라우터를 타겟으로 추가:
scrape_configs:
- job_name: 'continuum-router'
static_configs:
- targets: ['localhost:9090']
scrape_interval: 15s
3. Grafana 대시보드 가져오기¶
monitoring/grafana/dashboards/router-overview.json에서 제공된 대시보드를 가져옵니다.
설정¶
메트릭 설정은 메인 설정 파일을 통해 수행됩니다:
metrics:
enabled: true
port: 9090
path: /metrics
max_model_labels: 1000
max_backend_labels: 100
cardinality_limits:
max_models: 1000
max_endpoints: 100
max_error_types: 100
enable_sampling: false
sampling_rate: 1.0
# 이 프로필에서는 외부 Prometheus가 영속 저장소입니다.
persistence:
enabled: false
환경 변수¶
환경 변수를 사용하여 메트릭을 설정할 수도 있습니다:
# 메트릭 활성화/비활성화
METRICS_ENABLED=true
# 메트릭 엔드포인트 변경
METRICS_ENDPOINT=/custom/metrics
# 선택적 메트릭 활성화
METRICS_ENABLE_BODY_SIZE=true
사용 가능한 메트릭¶
HTTP 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
http_requests_total | Counter | 총 HTTP 요청 수 | method, endpoint, status_code, backend |
http_request_duration_seconds | Histogram | 요청 지연 시간 | method, endpoint, backend |
http_active_connections | Gauge | 현재 활성 요청 | backend |
http_request_size_bytes | Histogram | 요청 본문 크기 | endpoint |
http_response_size_bytes | Histogram | 응답 본문 크기 | endpoint |
백엔드 선택은 핸들러 내부에서 수행되고 모든 라우트가 백엔드를 선택하는 것은 아니므로 HTTP 미들웨어는 backend="unknown"으로 보고합니다. 백엔드별 트래픽과 부하는 백엔드 및 prefix-routing 메트릭 패밀리에서 확인할 수 있으며, 클라이언트가 제공한 헤더는 메트릭 귀속에 신뢰하지 않습니다.
오류 및 재시도 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
errors_total | Counter | 요청 미들웨어가 관찰한 HTTP 클라이언트/서버 오류 | error_type, backend |
retry_attempts_total | Counter | 최초 시도 이후의 백엔드 재시도 | backend, attempt_number |
retry_success_total | Counter | 재시도에서 성공한 요청 | backend |
retry_exhausted_total | Counter | 한 번 이상 백엔드를 재시도한 뒤 실패한 요청 | backend |
timeout_errors_total | Counter | 최종 백엔드 또는 요청 타임아웃 응답 | operation, backend |
서킷 브레이커 메트릭¶
metrics 기능과 circuit_breaker가 모두 활성화되면 일반 프록시 트래픽이 채웁니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
circuit_breaker_state | Gauge | 현재 백엔드별 서킷 상태(0 Closed, 1 Open, 2 HalfOpen) | backend |
circuit_breaker_failures_total | Counter | 백엔드 서킷에 기록된 실패 | backend, error_type |
circuit_breaker_successes_total | Counter | 백엔드 서킷에 기록된 성공 | backend |
circuit_breaker_transitions_total | Counter | 서킷 상태 전환 | backend, from_state, to_state |
요청 파라미터 정책 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
request_param_policy_total | Counter | 적용된 파라미터 동작과 거부된 정책 결정 | protocol, parameter, action, outcome |
request_param_policy_composition_total | Counter | 로컬 정책과 Hub 정책의 조합 | protocol, source, outcome |
두 메트릭 패밀리는 닫혀 있고 상한이 정해진 레이블만 사용합니다. source는 none, local, hub, intersection 중 하나이며 요청 값, 모델 id, 티어 id, 키 id, 정책 cursor는 어느 쪽에도 내보내지 않습니다.
백엔드 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
backend_healthy | Gauge | 백엔드 헬스(1=정상, 0=정상 아님), URL 레이블은 마스킹됨 | backend, url |
backend_health_check_duration_seconds | Histogram | 능동 헬스 체크 지속 시간 | backend |
backend_health_check_failures_total | Counter | 실패한 능동 헬스 체크 | backend |
backend_current_load | Gauge | 백엔드에 디스패치된 실시간 진행 중 요청 | backend |
backend_weight | Gauge | 설정된 부하 분산 가중치 | backend |
엔진 통계 메트릭¶
engine_stats.enabled: true일 때만, 그리고 엔진 통계를 지원하는 백엔드 유형(vllm, sglang, llamacpp, mlxcel, ollama, lmstudio)에 대해서만 노출된다. 폴러는 각 엔진 자체의 부하 또는 메트릭 엔드포인트(vLLM GET /metrics, SGLang GET /v1/loads와 /get_load·/metrics 폴백, llama.cpp는 /props의 기능 불리언으로 GET /slots·GET /metrics 선택, Ollama GET /api/ps, LM Studio GET /api/v0/models)를 읽어 백엔드당 하나의 스냅샷으로 정규화한다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
backend_engine_requests_running | Gauge | 엔진이 실행 중이라고 보고한 요청 수 | backend |
backend_engine_requests_waiting | Gauge | 엔진이 대기 중이라고 보고한 요청 수 | backend |
backend_engine_kv_cache_usage_ratio | Gauge | 엔진 KV 캐시 사용률(0..1) | backend |
backend_engine_kv_tokens_capacity | Gauge | 엔진 KV 캐시 토큰 용량 | backend |
backend_engine_generation_tokens_per_second | Gauge | 엔진이 보고한 생성 처리량 | backend |
backend_engine_prefix_cache_hit_ratio | Gauge | 엔진 프리픽스 캐시 적중률(0..1) | backend |
backend_engine_context_length | Gauge | 단일 요청이 쓸 수 있는 최대 컨텍스트(llama.cpp는 슬롯당 값) | backend |
backend_engine_slots_total | Gauge | 엔진이 알리는 병렬 처리 슬롯 수 | backend |
backend_engine_stats_scrape_success | Gauge | 최근 엔진 스크레이프 성공(1)/실패(0) | backend |
backend_engine_stats_scrape_duration_seconds | Gauge | 최근 엔진 스크레이프 소요 시간 | backend |
backend_engine_stats_age_seconds | Gauge | 라우터 스크레이프 시점의 스냅샷 나이 | backend |
backend_engine_prompt_tokens_total | Counter | 엔진이 처리한 프롬프트 토큰(리셋 감지로 미러링) | backend |
backend_engine_generation_tokens_total | Counter | 엔진이 생성한 토큰(리셋 감지로 미러링) | backend |
시리즈의 정직성은 세 가지 표현 규칙이 지킨다.
- 0이 아니라 부재. 엔진에 해당 개념이 없는 값의 시리즈는 아예 노출하지 않으며, 이는 어댑터별로 정적으로 결정한다. 유일한 예외는 SGLang
/v1/loads다. 이 와이어 포맷은 0 값 필드를 생략하므로(msgspecomit_defaults), 어댑터는 누락된 숫자를 진실한0으로 읽는다. 덕분에 유휴 SGLang 백엔드도backend_engine_requests_running 0을 계속 보고한다. - 실패한 스크레이프는 한 가지만 말한다. 스크레이프 오류가 나면 해당 백엔드는
backend_engine_stats_scrape_success 0만 노출하고, 성공할 때까지 나머지 시리즈는 사라진다. 고장난 스크레이프가 유휴 엔진으로 읽히는 일을 막기 위함이다. 마지막 스냅샷은 내부에 유지되며 Admin API에서staleness_seconds가 커지는 형태로 보인다. - 멀티 랭크 집계. SGLang 데이터 병렬 엔진은 DP 랭크마다 항목을 하나씩 보고한다. 랭크는 개수·용량·처리량을 합산하고 사용률·적중률은 최댓값을 취해 하나의 스냅샷으로 합친다(포화된 랭크가 중요한 랭크다). Prometheus 본문에 한 시리즈의 레이블 세트가 여러 개일 때도 같은 규칙을 적용한다.
두 *_tokens_total 카운터는 리셋 감지를 적용한다(직전 관측값보다 낮은 엔진 값은 재시작으로 간주하고 새 값을 재시작 이후 증가분으로 반영). 리셋 감지는 엔진이 카운터로 선언한 시리즈에만 적용한다. llama.cpp의 /metrics는 카운터와 게이지를 섞어 내보내며, 게이지는 정상적으로 감소할 수 있다.
핫 리로드로 제거된 백엔드의 backend_engine_* 시리즈는 다음 스크레이프에서 사라지고, 추가된 백엔드는 한 폴링 주기 안에 보고를 시작한다. 설정 표면은 config.yaml.example의 engine_stats를, JSON 스냅샷은 GET /admin/backends/{name}/engine-stats를 참고한다.
서빙 엔진은 라우터와 무관하게 버그가 있거나 침해될 수 있으므로, 엔진의 응답은 신뢰할 수 없는 입력으로 다룬다.
- 제한된 도달 범위. 폴러는 설정된 백엔드 URL, 또는 그와 같은 호스트로 해석되는
metrics_url만 가져오며 리다이렉트를 따르지 않는다. 동일 호스트 규칙은 설정 로드 시점과 매 요청 직전에 두 번 검사하고, 백엔드 URL이 파싱되지 않으면 거부 쪽으로 닫힌다. 메트릭 요청에는 백엔드 API 키가 함께 나가므로metrics_url은https백엔드를 평문http로 낮출 수 없다.allow_external_metrics_url은 호스트 규칙만 완화할 뿐 이 규칙은 해제하지 않는다. - 본문은 로그에 남지 않는다. 파싱 실패는 구조적 분류와 위치만 보고하고 문제가 된 값은 보고하지 않는다. 따라서 스크레이프 실패를 통해 엔진 본문의 일부(특히 llama.cpp
/slots본문)가 라우터 로그로 새어 나갈 수 없다. 전송 실패는 자격 증명이 제거된 URL만 보고한다. - 입력량 제한. 응답은
max_body_bytes기준으로 청크 단위로 읽고, Prometheus 파서는 본문당 샘플 수와 샘플당 레이블 수를 제한하며, 스냅샷이 보관하는 유일한 엔진 자유 텍스트(모델 이름과 엔진 버전)는 길이를 제한하고 보이지 않는 제어 문자를 제거한다. 숫자 집계는 오버플로 대신 포화한다.
모델 서비스 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_cache_size | Gauge | 집계 캐시의 모델 수 | — |
model_cache_hits_total | Counter | 모델 집계 캐시 적중 | — |
model_cache_misses_total | Counter | 모델 집계 캐시 미스 | — |
model_refresh_failures_total | Counter | 실패한 백엔드 모델 조회 | — |
model_refresh_duration_seconds | Histogram | 모델 목록 집계 1회 전체 소요 시간 | — |
model_backend_fetch_duration_seconds | Histogram | 백엔드 1개의 모델 조회 소요 시간 | backend |
model_last_refresh_duration_seconds | Gauge | 가장 최근 집계의 소요 시간 | — |
model_fetch_attempts_total | Counter | 재시도를 포함한 백엔드 모델 조회 시도 수 | — |
model_empty_responses_total | Counter | 빈 모델 목록을 만든 갱신 수 | — |
model_backends_unavailable_total | Counter | 어떤 백엔드도 모델을 반환하지 않은 갱신 수 | — |
model_rate_limit_exceeded_total | Counter | 속도 제한으로 거부된 모델 엔드포인트 요청 수 | — |
model_transient_errors_total | Counter | 재시도 가능한 백엔드별 조회 실패 | — |
model_permanent_errors_total | Counter | 재시도 불가능한 백엔드별 조회 실패 | — |
model_stale_while_revalidate_total | Counter | 갱신 중 스테일 캐시로 응답한 요청 수 | — |
model_coalesced_requests_total | Counter | 진행 중인 집계에 합쳐진 요청 수 | — |
model_background_refreshes_total | Counter | 백그라운드 재검증이 시작한 백엔드 팬아웃 수(집계 패스마다 1). 진행 중 대체된 패스(issue #1548)는 같은 자리에서 재시도되며 재시도마다 다시 세므로 팬아웃 수를 유지하고, model_background_refresh_successes_total과 model_background_refresh_failures_total은 재검증마다 1씩 증가함 (issue #1552) | — |
model_background_refresh_successes_total | Counter | 결과를 저장하고 끝난 백그라운드 재검증 수(패스 수와 무관하게 재검증마다 1) | — |
model_background_refresh_failures_total | Counter | 실패한 백그라운드 재검증 수(재검증마다 1) | — |
model_singleflight_lock_acquired_total | Counter | 싱글플라이트용 집계 잠금 획득 수 | — |
model_superseded_aggregations_total | Counter | 팬아웃 도중 풀 멤버십이나 캐시 무효화 epoch가 바뀌어 fresh 대신 만료 상태로 저장된 갱신 수 (issue #1548). 저장 후 재확인에서 잡힌 멤버십 변경(issue #1552)도 포함. 각 건은 즉시 재집계로 이어지되, 세 번 연속 대체된 뒤에는 다음 재검증이 2초의 재가동 간격을 기다림 | — |
느린 모델 목록 진단¶
model_refresh_duration_seconds는 집계가 느렸다는 사실을 알려주고, model_backend_fetch_duration_seconds는 어느 백엔드 때문에 느렸는지 알려줍니다. 모델 목록 팬아웃은 동시에 실행되므로 전체 소요 시간은 가장 느린 백엔드 하나와 같습니다.
# 5분 구간에서 갱신을 지배하는 백엔드, 95 백분위수
histogram_quantile(0.95, sum by (backend, le) (rate(model_backend_fetch_duration_seconds_bucket[5m])))
두 히스토그램 모두 버킷이 60초까지 있습니다. 시도별 request_timeout을 초과한 백엔드는 이름, 소요 시간, 시도 횟수를 담은 WARN 로그를 한 줄 남기므로, Prometheus 없이도 그리고 전역 로그 수준을 DEBUG로 올리지 않고도 같은 사실을 확인할 수 있습니다.
WARN Slow model fetch from backend backend="claude-bedrock" duration_ms=15503 attempts=3 threshold_ms=5000 outcome="error"
model_backend_fetch_duration_seconds는 백엔드 이름당 시계열 1개를 가집니다. 백엔드 이름은 운영자가 정의하고 설정으로 한정되므로 카디널리티는 보통 작습니다. 백엔드를 동적으로 등록하는 배포(AppProxy 레플리카)에서는 서로 다른 레플리카 이름마다 시계열 1개가 생긴다고 예상해야 합니다.
라우팅 텔레메트리는 아래의 실제 KV 캐시 및 스마트 라우팅 메트릭 패밀리에서 내보냅니다. 기존 범용 라우팅 및 모델 서비스 collector 중 지원되지 않는 항목은 등록하지 않습니다.
활성 스트리밍 응답 본문은 http_active_connections에 포함됩니다. 현재 범용 스트리밍 지속 시간 또는 응답 이후 오류 메트릭은 내보내지 않으며, 미드스트림 폴백에는 아래의 전용 실제 메트릭이 있습니다.
미드스트림 폴백 메트릭¶
미드스트림 폴백 기능이 활성화된 경우(streaming.mid_stream_fallback.enabled: true)에 방출되는 메트릭입니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
streaming_fallback_total | Counter | 스트리밍 폴백 시도 총 횟수. mid-stream 경로가 SSE 응답 확정 전에 수행한 pre-stream 홉도 포함합니다 | reason |
streaming_fallback_success_total | Counter | 성공한 미드스트림 폴백 복구 | original_backend, fallback_backend |
streaming_fallback_accumulated_tokens | Histogram | 폴백 전까지 누적된 추정 토큰 수 | outcome (success, failure) |
streaming_fallback_total의 reason 레이블 값¶
| 값 | 설명 |
|---|---|
timeout | 백엔드 비활성 타임아웃 초과 |
connection_error | TCP/TLS 연결 오류 |
stream_read_error | 스트림에서 바이트 읽기 오류 |
stream_ended_unexpectedly | [DONE] 마커 없이 스트림 종료 |
too_many_stream_errors | 연속 오류 이벤트 임계값 도달 |
other | 기타 실패 원인 |
주요 PromQL 쿼리¶
# 미드스트림 폴백 비율
rate(streaming_fallback_total[5m])
# 폴백 복구 성공률
sum(rate(streaming_fallback_success_total[5m])) /
sum(rate(streaming_fallback_total[5m]))
# 폴백 트리거 시점의 누적 토큰 중앙값
histogram_quantile(0.5, rate(streaming_fallback_accumulated_tokens_bucket[5m]))
Responses 우회 메트릭¶
라우터가 /v1/chat/completions 요청을 업스트림 /v1/responses로 대신 보낸 횟수를 셉니다. 항상 등록되며, 우회가 없으면 카운터가 0에 머뭅니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
responses_bridge_total | Counter | /v1/responses로 보낸 Chat Completions 요청 수 | reason |
responses_bridge_total의 reason 레이블 값¶
| 값 | 설명 |
|---|---|
responses_only_model | 모델 메타데이터에 responses_only가 설정되어 있거나, 백엔드가 Responses API만 제공합니다(Codex/ChatGPT 구독 백엔드). 해당 모델의 모든 요청이 우회됩니다. |
tools_with_reasoning | 모델은 Chat Completions로 제공되며, 비어 있지 않은 tools 배열과 업스트림이 거절하는 reasoning effort가 함께 온 이 요청 형태만 우회됩니다. Reasoning Effort 문서의 "함수 도구와 reasoning effort 조합" 절을 참고하세요. |
두 값은 운영상 의미가 다릅니다. responses_only_model은 배포 구성 자체의 성질이며 해당 모델로 가는 트래픽에 비례합니다. tools_with_reasoning은 트래픽의 성질입니다. 에이전트 트래픽 중 얼마나 많은 비율이 변환 왕복 비용을 치르고 있는지 알려주며, OpenAI가 제약을 풀고 메타데이터 플래그를 끄면 저절로 0으로 떨어집니다.
# 우회된 트래픽 중 조건부 tools 우회가 차지하는 비율
sum(rate(responses_bridge_total{reason="tools_with_reasoning"}[5m])) /
sum(rate(responses_bridge_total[5m]))
모델 실험 메트릭¶
모델 실험의 변형별 귀속 메트릭입니다. 전용 계열이므로 기존 요청, 지연 시간, 토큰 시계열의 레이블 집합은 그대로입니다. 레이블 값은 model_experiments 설정(실험 최대 32개, 실험당 변형 최대 16개)이나 닫힌 집합에서 옵니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_experiment_requests_total | Counter | 실험으로 해석된 요청 수 | experiment, variant, status(success, client_error, server_error), assignment(sticky, random) |
model_experiment_request_duration_seconds | Histogram | 응답 헤더까지의 시간 | experiment, variant |
model_experiment_tokens_total | Counter | prompt 및 completion 토큰 | experiment, variant, kind |
model_experiment_fallbacks_total | Counter | 모델 간 폴백 홉이 응답한 요청 수(fallback: cross_variant) | experiment, variant |
model_experiment_reserved_refusals_total | Counter | hide_variant_models 실험이 예약한 변형 모델을 지정해 거부된 요청 수 | experiment |
폴백 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
fallback_attempts_total | Counter | 총 폴백 시도 | original_model, fallback_model, backend |
fallback_success_total | Counter | 성공한 폴백 | original_model, fallback_model, backend |
fallback_exhausted_total | Counter | 소진된 폴백 체인 | original_model |
cross_provider_fallback_total | Counter | 크로스 프로바이더 폴백 | original_model, fallback_model, original_provider, fallback_provider |
fallback_duration_seconds | Histogram | 폴백 작업 지속 시간 | original_model, success |
fallback_dial_bound_saturated_total | Counter | 백엔드별 다이얼 상한 (fallback.fallback_policy.max_concurrent_dials_per_backend)에서 대기 상한이 만료될 때까지 기다리다 해당 백엔드에 대한 홉이 실패한 폴백 홉 다이얼 수 | backend |
응답 캐시 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
continuum_response_cache_requests_total | Counter | 결과별 캐시 조회 | result (hit, miss, skip) |
continuum_response_cache_entries | Gauge | 현재 캐시된 항목 수 | -- |
continuum_response_cache_size_bytes | Gauge | 대략적인 캐시 메모리 사용량 | -- |
continuum_response_cache_evictions_total | Counter | LRU 퇴거 | -- |
continuum_response_cache_hit_rate | Gauge | 롤링 캐시 적중률 (0.0--1.0) | -- |
continuum_cache_backend_type | Gauge | 활성 캐시 백엔드 (1 = 활성) | backend (memory, redis) |
퇴거 카운터는 인메모리 LRU 저장소에서 정확합니다. Redis와 S3는 만료 또는 퇴거를 서버 측에서 수행하고 라우터에 퇴거 이벤트를 제공하지 않으므로, 해당 백엔드에서는 값을 임의로 만들지 않습니다.
Redis 캐시 백엔드 메트릭¶
이 메트릭은 Redis 캐시 백엔드가 활성화된 경우(backend: redis) 수집됩니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
continuum_cache_redis_connections_active | Gauge | 풀의 활성 Redis 연결 | -- |
continuum_cache_redis_connections_idle | Gauge | 풀의 유휴 Redis 연결 | -- |
continuum_cache_redis_latency_seconds | Histogram | Redis 작업 지연 시간 | operation (get, set, delete) |
continuum_cache_redis_errors_total | Counter | 유형별 Redis 오류 | type (connection, timeout, other) |
continuum_cache_fallback_active | Gauge | 인메모리 폴백이 활성화되었는지 여부 (0 또는 1) | -- |
KV 이벤트 컨슈머 메트릭¶
이 메트릭은 KV 이벤트 컨슈머가 활성화된 경우(src/infrastructure/kv_index/) 수집됩니다. vLLM, SGLang, TensorRT-LLM 리스너는 router salt echo를 사용해 모든 블록 이벤트를 detokenize하지 않고도 같은 접두사 identity를 유지할 수 있습니다. 모든 백엔드 레이블 값은 카디널리티 폭발을 방지하기 위해 정규화됩니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
continuum_kv_event_received_total | Counter | 각 백엔드에서 수신된 KV 캐시 이벤트 | backend |
continuum_kv_event_processed_total | Counter | 채널을 통해 성공적으로 전달된 KV 캐시 이벤트 | backend |
continuum_kv_event_dropped_total | Counter | 백프레셔로 인해 삭제된 KV 캐시 이벤트 | backend |
continuum_kv_consumer_connected | Gauge | KV 이벤트 컨슈머 연결 여부 (1 = 연결됨, 0 = 연결 끊김) | backend |
continuum_kv_consumer_reconnects_total | Counter | 각 백엔드 컨슈머의 총 재연결 시도 횟수 | backend |
Prefix 라우팅 메트릭¶
이 메트릭은 접두사 인식 스티키 라우팅 결정 및 백엔드 분포를 추적합니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
continuum_prefix_routing_requests_total | Counter | 전략 유형별 총 접두사 라우팅 결정 | strategy (prefix_hash, overflow, fallback, unknown) |
continuum_prefix_routing_backend_distribution | Gauge | 백엔드별 인플라이트 요청 (로드 밸런싱용) | backend |
continuum_prefix_routing_prefix_cardinality | Gauge | 확인된 고유 접두사 키의 대략적인 수 | -- |
continuum_prefix_routing_requests_total은 백엔드 선택마다 한 번, PrefixAwareHash 전략 분기가 실행됐을 때만 기록됩니다. 다른 selection_strategy에서는 0으로 남습니다. 세 레이블은 ring 탐색과 그대로 대응합니다.
prefix_hash: 접두사 해시가 지목한 소유 백엔드가 요청을 받았습니다. CHWBL 부하 상한 아래였던 경우와, 모든 백엔드가 상한에 걸려 ring을 한 바퀴 돈 뒤 접두사 지역성을 지키려고 소유 백엔드를 그대로 쓴 경우를 함께 포함합니다.overflow: 소유 백엔드가 부하 상한에 도달했거나 넘어서, ring에서 시계 방향 뒤쪽 노드가 요청을 받았습니다.fallback: 요청에 접두사 키가 없어 모델 이름을 해싱했고 부하 상한 없이 ring을 걸었습니다.fallback비율이 계속 100%라면 거의 언제나prefix_routing.enabled가 false입니다.
unknown은 라우팅 경로가 아니라 정제 결과입니다. 허용 목록에 없는 레이블이 기록 함수에 도달했을 때만 나타납니다.
주요 PromQL 쿼리¶
# 접두사 라우팅 적중률 (접두사 해시 사용 비율 vs 폴백)
sum(rate(continuum_prefix_routing_requests_total{strategy="prefix_hash"}[5m])) /
sum(rate(continuum_prefix_routing_requests_total[5m]))
# 오버플로율 (CHWBL 로드 밸런싱 활성화)
rate(continuum_prefix_routing_requests_total{strategy="overflow"}[5m])
# 백엔드 부하 분포 (대체로 균등해야 함)
continuum_prefix_routing_backend_distribution
KV 캐시 인덱스 메트릭¶
이 메트릭은 인덱스 상태, 쿼리 성능, 라우팅 결정 및 오버랩 스코어링을 포함한 KV 캐시 인덱스 서브시스템을 추적합니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
continuum_kv_index_entries | Gauge | KV 캐시 인덱스의 현재 항목 수 | -- |
continuum_kv_index_events_total | Counter | KV 캐시 인덱스 변경 이벤트 (생성/퇴거) | backend, type (created, evicted) |
continuum_kv_index_query_latency_seconds | Histogram | KV 인덱스 쿼리 작업의 지연 시간 | -- |
continuum_kv_index_routing_decisions_total | Counter | 결과별 KV 인식 라우팅 결정 | decision (kv_aware, fallback) |
continuum_kv_index_overlap_score | Histogram | 스코어러가 결정한 선택의 최고 복합 점수 | -- |
continuum_kv_index_event_source_status | Gauge | 이벤트 소스 연결 상태 (1 = 연결됨, 0 = 연결 끊김) | backend, status |
routing_engine_load_decisions_total | Counter | 엔진 부하 인식 라우팅 결정 (이슈 #1447) | backend, reason (engine_load, stale_fallback, hysteresis_hold, admission_reject) |
routing_engine_load_decisions_total은 routing.engine_load.enabled가 켜져 있는 동안 스코어링 패스마다 한 번(어드미션 힌트가 거부한 선택마다 추가로 한 번) 기록됩니다. engine_load는 엔진 부하 항이 실제로 순위를 매긴 경우로 backend가 엔진이 선호한 후보를 가리키고, stale_fallback은 신선한 엔진 스냅샷이 하나도 없어 패스가 기본 스코어링과 같게 동작한 경우(backend="none"), hysteresis_hold는 데이터는 있으나 waiting_requests 편차가 balance 임계값 안에 머문 경우, admission_reject는 모든 정상 후보의 kv_cache_usage가 임계값을 넘어 어드미션 힌트가 선택을 거부한 경우(backend="none")입니다. backend 레이블 값은 다른 백엔드 레이블 시리즈와 같은 정제기를 거칩니다. 게이트 판정, 부하 정규화, 캐시된 결과, backend 레이블은 요청의 정확한 라이브 후보 집합으로 한정되므로 모델 가시성, 상태, 재시도 상태, 키별 권한으로 제외된 엔진은 해당 스코어링 결정에 영향을 주거나 레이블에 나타날 수 없습니다.
라우팅 레이블 두 개는 같은 지점에서 선택마다 한 번 기록됩니다. kv_aware는 KV 오버랩 스코어러가 이겨서 그 백엔드로 전달한 경우이고, fallback은 스코어러가 접두사 키를 가지고 실행됐지만 풀에 컴파일된 복합 점수 임계값 0.3을 넘은 백엔드가 없어 설정된 selection_strategy가 대신 결정한 경우입니다. 스코어러가 시도조차 하지 않은 선택(스코어러 미등록, 접두사 키 없음)은 둘 다 기록하지 않으므로, 두 카운터의 합은 전체 트래픽이 아니라 스코어링을 거친 선택의 수입니다. 히스토그램은 kv_aware 결정의 최고 복합 점수를 관측하며, 이 값은 오버랩 항목만이 아니라 오버랩, 부하, 헬스를 모두 반영한 가중 합입니다.
주요 PromQL 쿼리¶
# KV 인식 라우팅 비율
sum(rate(continuum_kv_index_routing_decisions_total{decision="kv_aware"}[5m])) /
sum(rate(continuum_kv_index_routing_decisions_total[5m]))
# 라우팅된 요청의 평균 오버랩 점수
histogram_quantile(0.5, rate(continuum_kv_index_overlap_score_bucket[5m]))
# KV 인덱스 쿼리 P99 지연 시간
histogram_quantile(0.99, rate(continuum_kv_index_query_latency_seconds_bucket[5m]))
# 이벤트 소스 연결 상태
continuum_kv_index_event_source_status{status="connected"}
스마트 라우팅 메트릭¶
분류 및 라우팅 파이프라인 전체를 추적하는 메트릭입니다.
분류 및 라우팅¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
smart_routing_classifications_total | Counter | 수행된 총 분류 횟수 | complexity, domain, classifier_type |
smart_routing_decisions_total | Counter | 내려진 총 라우팅 결정 수 | source_model, target_model, policy, tier |
smart_routing_classifier_duration_seconds | Histogram | 분류기 지연 시간 | classifier_type |
smart_routing_policy_no_match_total | Counter | 매칭 정책이 없는 요청 수 | - |
smart_routing_tier_no_model_total | Counter | 정책은 매칭됐으나 해당 티어에 모델이 없는 경우 | tier |
부하 관리¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
smart_routing_load_state | Gauge | 현재 부하 상태: 0=Normal, 1=Warning, 2=Critical | - |
smart_routing_tier_degradation_total | Counter | 부하로 인한 티어 하향 횟수 | load_state |
smart_routing_load_transitions_total | Counter | 부하 상태 전환 횟수 | from_state, to_state |
LLM 분류기¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
smart_routing_llm_classifier_calls_total | Counter | LLM 분류기 호출 총 횟수 | - |
smart_routing_llm_classifier_cache_hits_total | Counter | 캐시에서 반환된 분류 결과 수 | - |
smart_routing_llm_classifier_duration_seconds | Histogram | LLM 분류 전체 지연 시간 (버킷: 50ms~5s) | - |
smart_routing_llm_classifier_fallbacks_total | Counter | LLM 결과를 버리고 규칙 기반 결과를 사용한 횟수 | - |
smart_routing_llm_classifier_parse_errors_total | Counter | 재시도 전 응답 파싱 실패 횟수 | - |
smart_routing_llm_classifier_retries_total | Counter | 초기 파싱 실패 후 재시도 횟수 | - |
집계 및 운영¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
smart_routing_requests_total | Counter | 스마트 라우팅된 요청 총 수 | source_model, target_model, policy, load_state |
smart_routing_tier_usage_total | Counter | 티어 사용 분포 | tier, domain |
smart_routing_cost_estimate_total | Counter | 티어 최적화로 절감된 예상 비용 | tier |
smart_routing_policy_evaluations_total | Counter | 정책 평가 빈도 | policy_name, result |
smart_routing_model_availability | Gauge | 티어별 사용 가능한 모델 수 | model, tier |
주요 PromQL 쿼리¶
# 정책별 스마트 라우팅 요청 속도
rate(smart_routing_requests_total[5m])
# 티어 사용 분포
sum by(tier) (rate(smart_routing_tier_usage_total[5m]))
# LLM 분류기 캐시 히트율
rate(smart_routing_llm_classifier_cache_hits_total[5m]) /
rate(smart_routing_llm_classifier_calls_total[5m])
# LLM 분류기 P95 지연 시간
histogram_quantile(0.95, rate(smart_routing_llm_classifier_duration_seconds_bucket[5m]))
# LLM 분류기 폴백 비율 (안정성 지표)
rate(smart_routing_llm_classifier_fallbacks_total[5m]) /
rate(smart_routing_llm_classifier_calls_total[5m])
# LLM 기반 분류 비율
rate(smart_routing_classifications_total{classifier_type="llm_based"}[5m]) /
rate(smart_routing_classifications_total[5m])
# 정책 평가 성공률
sum by(policy_name) (rate(smart_routing_policy_evaluations_total{result="matched"}[5m]))
비즈니스 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_usage_total | Counter | 성공한 모델 요청 횟수 | model, backend |
model_tokens_processed | Counter | 성공한 모델 응답이 보고한 토큰 수 | model, type (input, output) |
Guardrail Metrics¶
가드레일이 설정되고 metrics 기능이 활성화되면 내보내집니다. 모든 가드레일 결정이 기록되므로, 운영자는 enforce 전후로 정책이 무엇을 하는지(monitor 모드에서는 무엇을 할지)를 관찰할 수 있습니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
guardrail_checks_total | Counter | 단계·판정 결과별 프로바이더 검사 | stage, provider, result |
guardrail_blocks_total | Counter | 단계·프로바이더·카테고리별 차단 판정 | stage, provider, category |
guardrail_check_duration_seconds | Histogram | 프로바이더별 검사 지연(초) | stage, provider |
guardrail_errors_total | Counter | 프로바이더 오류(타임아웃 / 하드 실패) | provider, kind |
guardrail_fail_open_total | Counter | fail-open으로 해소된 프로바이더 실패(요청 허용) | provider |
guardrail_fail_closed_total | Counter | fail-closed로 해소된 프로바이더 실패(요청 차단) | provider |
guardrail_degraded_total | Counter | 선택적 외부 PII 분석기를 사용할 수 없어 내장 탐지만 적용한 경우처럼 정밀도가 낮아진 상태로 완료된 프로바이더 검사 | provider, kind |
guardrail_verdicts_total | Counter | 모드 의미를 적용한 후 요청당 집계 판정 | stage, mode, result |
guardrail_stream_buffer_cap_trips_total | Counter | 스트리밍 출력 게이트의 4 MiB 유지 바이트 상한에 도달한 스트림 수(스트림당 1회) | strategy, outcome |
레이블 값:
stage는input,output,streaming.result는allow,block,transform,flag.kind는timeout또는error.mode는monitor또는enforce.guardrail_verdicts_total이mode를 담으므로, 게이팅하지 않는 monitor 모드 판정도 보입니다. 이 점이 monitor 후 enforce 롤아웃을 관찰 가능하게 만듭니다.provider는 설정된 프로바이더 이름이거나, 예약된 매치 리스트 라벨 두 개 중 하나입니다.guardrails.deny규칙이 검사를 결정했으면match_list_deny, allow 규칙이 결정했으면match_list_allow입니다. 차단 리스트 차단은guardrail_blocks_total에category="deny_list"도 함께 기록합니다. 허용/차단 리스트를 참고하세요.strategy는 상한에 도달한 시점의 전략(buffer_full/chunked/monitor),outcome은 상한 이후 출력 검사가 이어진 방식(degraded_chunked/compacted/truncated). 각outcome이 안전 보장에 어떤 의미인지는 버퍼 상한 절을 참고하세요.strategy="monitor"는 안전 관련 이벤트가 아니라, monitor 모드 스트림이 길어서 앞쪽 4 MiB만 관찰되었다는 뜻입니다.
주요 PromQL 쿼리¶
# 카테고리별로 무엇이 차단될지(monitor 모드 튜닝)
sum by (category) (rate(guardrail_blocks_total[1h]))
# enforce 후 단계별 차단율
sum by (stage) (rate(guardrail_verdicts_total{result="block", mode="enforce"}[5m]))
# 프로바이더 오류율(타임아웃 대 하드 실패)
sum by (provider, kind) (rate(guardrail_errors_total[5m]))
# 프로바이더별 P95 가드레일 검사 지연
histogram_quantile(0.95, sum by (le, provider) (rate(guardrail_check_duration_seconds_bucket[5m])))
전체 가드레일 가이드(개념, 프로바이더, 설정, 임계값 튜닝 워크플로)는 가드레일을 참고하세요.
API 키별 LLM 토큰 사용량¶
라우터는 LLM 토큰 소비를 API 키 단위로 쪼개어 노출합니다. 운영자는 '지난 1시간 동안 어느 키가 completion 토큰을 가장 많이 썼나', '오늘 X팀이 모델 Y에서 prompt 토큰을 얼마나 썼나'와 같은 질문에 바로 답할 수 있습니다. model_tokens_processed 집계 카운터와 독립적인 별도 메트릭이고, 용량 산정과 공정 사용 정책, 외부 비용 귀속 산정에 쓰입니다.
메트릭 정의¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
llm_tokens_total | Counter | 요청당 소비된 LLM 토큰 수 | api_key_id, model, backend, kind |
api_key_info | Gauge (상수 1) | 설정된 API 키 어노테이션을 레이블로 노출하는 정보 메트릭 | api_key_id + 어노테이션 허용 목록 |
kind 값은 다음 두 가지입니다:
prompt— 업스트림 요청 프롬프트의 토큰 수completion— 업스트림 응답 completion의 토큰 수
OpenAI 호환(prompt_tokens / completion_tokens) 응답과 Anthropic(input_tokens / output_tokens) 응답 모두 동일한 카운터로 정규화됩니다. 라우터는 OpenAI 호환 스트리밍 요청에 stream_options.include_usage=true를 자동 주입하므로, 클라이언트 동작과 무관하게 마지막 SSE 청크에서 사용량 정보를 받습니다.
api_key_id 도출 규칙¶
api_key_id는 원본 API 키가 노출되지 않도록, 라우터가 안정적이면서 역방향 복원이 불가능한 식별자를 다음 우선순위로 만들어 사용합니다:
- 요청의 bearer 토큰이 설정된 API 키 항목과 일치하면 해당 항목의
id필드를 씁니다(예:key-production-1). - 일치하지 않으면 원본 토큰의 SHA-256 해시 앞 12자에
k_접두어를 붙입니다(예:k_3f5a7c9b1e2d). - 토큰이 아예 없으면
anonymous리터럴이 들어갑니다.
모든 레이블 값은 기존 CardinalityManager를 거치므로, 토큰을 무작위로 갈아끼우는 공격이 들어와도 Prometheus 시리즈가 폭발하지 않습니다.
어노테이션 레이블과 api_key_info¶
각 API 키 항목에는 자유로운 annotations: { key: value } 맵을 달 수 있고, 운영자는 그중 어떤 키를 Prometheus 레이블로 승격할지 metrics.annotation_labels 허용 목록으로 명시합니다. 허용 목록에 없는 어노테이션은 내부 데이터로만 남습니다.
설정 스키마(기존 api_keys 블록 안):
api_keys:
api_keys:
- key: "${API_KEY_1}"
id: "key-production-1"
user_id: "user-admin"
organization_id: "org-main"
annotations:
email: "ops@example.com"
team: "platform"
environment: "prod"
owner: "alice"
metrics:
enabled: true
annotation_labels: [email, team] # 레이블 키 허용 목록
권장 표준 어노테이션 키는 email, uuid, owner, team, environment인데, 강제는 아니고 운영자가 자체 키를 추가해도 됩니다.
metrics.annotation_labels가 비어 있지 않으면 라우터는 등록된 키마다 api_key_info{api_key_id, email, team, ...} = 1을 한 번씩 발행합니다. 이 정보 메트릭을 PromQL 조인으로 llm_tokens_total에 투영하면 카운터의 레이블 집합을 키우지 않고도 메타데이터로 필터링·그룹핑할 수 있습니다:
# 이메일별 토큰 소비량 (지난 24시간, prompt + completion 합산)
sum by (email) (
increase(llm_tokens_total[24h])
* on (api_key_id) group_left(email) api_key_info
)
카디널리티와 핫 리로드¶
api_key_id카디널리티는 기본 1,000개로 제한됩니다.- API 키 어노테이션은 기존 설정 리로드 파이프라인을 통해 핫 리로드되고,
api_key_info정보 메트릭은 리로드마다 원자적으로 다시 발행됩니다.llm_tokens_total카운터 값은 리셋되지 않습니다. - 단,
api_key_info의 레이블 집합 (즉annotation_labels항목들)은 시작 시점에 고정됩니다. 허용 목록에서 키를 추가하거나 빼려면 재시작이 필요한데, Prometheus가 등록된 메트릭의 레이블 이름 변경을 허용하지 않기 때문입니다.
PromQL 예제¶
# 지난 1시간 동안 API 키별 prompt 토큰 합계
sum by (api_key_id) (
increase(llm_tokens_total{kind="prompt"}[1h])
)
# 지난 24시간 동안 completion 토큰 상위 10개 키
topk(10,
sum by (api_key_id) (
increase(llm_tokens_total{kind="completion"}[24h])
)
)
# 팀별 토큰 사용량 (annotation_labels에 team이 포함되어 있어야 함)
sum by (team) (
increase(llm_tokens_total[24h])
* on (api_key_id) group_left(team) api_key_info
)
Grafana 패널 예제¶
지난 24시간 동안 completion 토큰 상위 10개 팀을 보여주는 stat 패널:
{
"title": "Top 10 teams by completion tokens (24h)",
"type": "stat",
"targets": [
{
"expr": "topk(10, sum by (team) (increase(llm_tokens_total{kind=\"completion\"}[24h]) * on (api_key_id) group_left(team) api_key_info))",
"legendFormat": "{{team}}"
}
],
"options": {
"reduceOptions": {
"values": false,
"calcs": ["lastNotNull"]
}
}
}
지출 추이를 따라가려면 rate(llm_tokens_total[5m])를 team이나 model로 그룹화한 시계열 패널과 함께 쓰면 됩니다.
검증 절차¶
기능을 활성화한 뒤 다음 순서로 동작을 확인합니다:
- 설정된 API 키로 chat-completion 요청을 한 번 보냅니다.
/metrics를 스크랩해서llm_tokens_total{...}과api_key_info{...}시리즈가 노출되는지 확인합니다.- 스트리밍의 경우에도 카운터가 증가하는지 봅니다. 사용량은 마지막 SSE 청크에서 잡고, 라우터가 OpenAI 호환 백엔드에는
stream_options.include_usage=true를 자동 주입하므로 클라이언트 동작과 무관하게 동작합니다. - 일상적인 워크로드에서
/metrics의 카디널리티(예:wc -l < /metrics)를 측정해, 도입 전 베이스라인 대비 회귀가 없는지 확인합니다.
통합¶
Prometheus 설정¶
완전한 Prometheus 설정 예제:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'continuum-router'
static_configs:
- targets: ['router1:9090', 'router2:9090']
metric_relabel_configs:
# 필요시 높은 카디널리티 메트릭 삭제
- source_labels: [__name__]
regex: 'http_request_duration_seconds_bucket'
action: drop
Kubernetes 통합¶
Kubernetes 배포의 경우 ServiceMonitor 사용:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: continuum-router
namespace: monitoring
spec:
selector:
matchLabels:
app.kubernetes.io/name: continuum-router
endpoints:
- port: metrics
interval: 15s
path: /metrics
Prometheus Operator CRD가 설치된 경우 Helm 차트에서 serviceMonitor.enabled=true로 이 객체를 렌더링할 수 있습니다. Operator가 없는 클러스터를 위해 저장소는 Kubernetes endpoint 검색과 체크인된 알림 규칙을 포함한 독립형 Prometheus Kustomize 번들도 제공합니다.
kubectl apply -k monitoring/prometheus
kubectl -n monitoring rollout status deployment/continuum-router-prometheus
kubectl -n monitoring port-forward service/continuum-router-prometheus 9090:9090
독립형 프로필은 15일 보존 기간과 10 GiB ReadWriteOnce PersistentVolumeClaim을 요청합니다. 기본 StorageClass를 확인하고 예상 series 양에 맞게 claim 크기를 조정하거나 고가용성 모니터링에는 관리형 Prometheus 서비스를 사용하십시오. 적용 전에 클러스터 전체 검색 RBAC와 NetworkPolicy를 검토해야 합니다.
Grafana 대시보드¶
monitoring/grafana/dashboards/router-overview.json에는 다음 패널이 있습니다.
- 요청 속도
- 오류율
- P95 지연 시간
- 백엔드 상태
- 엔드포인트별 요청 속도
- 응답 시간 백분위
- 모델 사용 분포
- 백엔드별 현재 처리 중 부하
- 엔진 요청 수(running / waiting),
engine_stats기반 (이슈 #1446) - 엔진 KV 캐시 사용률,
engine_stats기반 (이슈 #1446) - 이유별 엔진 부하 라우팅 결정,
routing.engine_load기반 (이슈 #1447)
가져오는 방법:
- Grafana를 엽니다.
- 대시보드 → 가져오기를 선택합니다.
monitoring/grafana/dashboards/router-overview.json을 업로드합니다.- Prometheus 데이터 소스를 선택하고 가져옵니다.
알림¶
사전 설정된 알림 규칙이 monitoring/prometheus/alerts.yml에 있습니다:
중요 알림¶
{% raw %}
- alert: HighErrorRate
expr: sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
for: 5m
annotations:
summary: "높은 오류율: {{ $value | humanizePercentage }}"
경고 알림¶
{% raw %}
- alert: HighLatency
expr: histogram_quantile(0.95, http_request_duration_seconds) > 1
for: 5m
annotations:
summary: "P95 지연 시간 1초 초과: {{ $value | humanizeDuration }}"
- alert: TimeoutErrors
expr: sum(rate(timeout_errors_total[5m])) > 0.1
for: 10m
annotations:
summary: "빈번한 타임아웃 오류: 초당 {{ $value }}건"
예제¶
쿼리 예제¶
상태별 요청 속도¶
엔드포인트별 P95 지연 시간¶
백엔드 부하 개요¶
모델 사용량 순위¶
오류율 백분율¶
프로그래밍 방식 접근¶
메트릭에 프로그래밍 방식으로 접근할 수도 있습니다:
import requests
from prometheus_client.parser import text_string_to_metric_families
# 메트릭 가져오기
response = requests.get('http://localhost:9090/metrics')
metrics = text_string_to_metric_families(response.text)
# 메트릭 처리
for family in metrics:
for sample in family.samples:
if sample.name == 'http_requests_total':
print(f"엔드포인트: {sample.labels['endpoint']}, 카운트: {sample.value}")
사용자 정의 메트릭 수집¶
#!/bin/bash
# 30초마다 메트릭을 수집하고 파일에 저장
while true; do
timestamp=$(date +%s)
curl -s http://localhost:9090/metrics > "metrics_${timestamp}.txt"
sleep 30
done
모범 사례¶
1. 레이블 카디널리티¶
메트릭 폭발을 방지하기 위해 레이블 카디널리티를 낮게 유지:
# 좋음: 낮은 카디널리티
labels:
status: "200" # ~5개 가능한 값
method: "GET" # ~7개 가능한 값
---
# 나쁨: 높은 카디널리티
labels:
user_id: "12345" # 무제한
request_id: "abc-123" # 요청당 고유
2. 메트릭 명명¶
Prometheus 명명 규칙 준수:
snake_case사용- 메트릭 이름에 단위 포함 (
_seconds,_bytes,_total) - 표준 접두사 사용 (
http_,backend_,model_)
3. 대시보드 설계¶
- 관련 메트릭을 함께 그룹화
- 적절한 시각화 유형 사용 (현재 값에는 게이지, 시계열에는 그래프)
- 절대값과 비율 모두 포함
- 적절한 새로고침 간격 설정 (실시간에는 15-30초, 이력에는 1-5분)
4. 알림 설정¶
- 플래핑을 방지하기 위해 적절한 평가 기간 사용 (
for: 5m) - 알림 설명에 컨텍스트 포함
- 심각도에 따른 알림 라우팅 설정
- 프로덕션 전 스테이징에서 알림 테스트
5. 성능 고려 사항¶
- 필요하지 않은 경우 선택적 메트릭 비활성화
- 복잡한 쿼리에 레코딩 규칙 사용
- 적절한 메트릭 보존 정책 구현
- 장기 보존을 위해 원격 스토리지 고려
6. 보안¶
- 민감한 데이터가 노출된 경우 메트릭 엔드포인트 보호
- 프로덕션에서 Prometheus 스크래핑에 TLS 사용
- Grafana 대시보드에 인증 구현
- 메트릭 접근 로그 감사
문제 해결¶
메트릭이 나타나지 않음¶
- 설정에서 메트릭이 활성화되어 있는지 확인
- 메트릭 엔드포인트에 접근 가능한지 확인
- Prometheus 타겟 상태 확인
- 메트릭 초기화 오류에 대한 라우터 로그 검토
높은 메모리 사용량¶
- 카디널리티 제한 검토
- 무제한 레이블 확인
- 필요시 히스토그램 버킷 감소
- 메트릭 만료 활성화
잘못된 값¶
- 메트릭 유형 확인 (카운터 vs 게이지)
- 집계 함수 확인
- 레이블 선택기 검토
- 시간 범위 검증