메트릭 및 모니터링¶
이 문서는 Continuum Router의 메트릭 및 모니터링 기능을 설명합니다.
목차¶
개요¶
Continuum Router는 시스템 상태, 성능, 사용 패턴을 다룰 수 있도록 Prometheus 호환 메트릭을 제공합니다. 메트릭 시스템은 다음과 같이 설계되었습니다:
- 가벼움: 최소한의 성능 오버헤드
- 넓은 범위: 라우터의 모든 중요한 측면 포함
- 프로덕션 준비: 카디널리티 제한 및 적절한 레이블링 포함
- 쉬운 통합: 표준 Prometheus/Grafana 설정과 작동
빠른 시작¶
1. 메트릭 활성화¶
메트릭은 기본적으로 활성화되어 있습니다. 메트릭 엔드포인트는 /metrics에서 사용할 수 있습니다:
2. Prometheus 설정¶
prometheus.yml에 라우터를 타겟으로 추가:
scrape_configs:
- job_name: 'continuum-router'
static_configs:
- targets: ['localhost:8000']
scrape_interval: 15s
3. Grafana 대시보드 가져오기¶
monitoring/grafana/dashboards/router-overview.json에서 제공된 대시보드를 가져옵니다.
설정¶
메트릭 설정은 메인 설정 파일을 통해 수행됩니다:
metrics:
# 메트릭 수집 활성화/비활성화
enabled: true
# 메트릭 엔드포인트 경로
endpoint: "/metrics"
# 메트릭 폭발을 방지하기 위한 카디널리티 제한
cardinality_limit:
max_labels_per_metric: 100
max_unique_label_values: 1000
# 선택적 메트릭 (성능을 위해 기본적으로 비활성화)
optional_metrics:
enable_request_body_size: false
enable_response_body_size: false
enable_detailed_errors: true
환경 변수¶
환경 변수를 사용하여 메트릭을 설정할 수도 있습니다:
# 메트릭 활성화/비활성화
METRICS_ENABLED=true
# 메트릭 엔드포인트 변경
METRICS_ENDPOINT=/custom/metrics
# 선택적 메트릭 활성화
METRICS_ENABLE_BODY_SIZE=true
사용 가능한 메트릭¶
HTTP 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
http_requests_total |
Counter | 총 HTTP 요청 수 | method, endpoint, status |
http_request_duration_seconds |
Histogram | 요청 지연 시간 | method, endpoint |
http_active_connections |
Gauge | 현재 활성 연결 | - |
http_request_size_bytes |
Histogram | 요청 본문 크기 | method, endpoint |
http_response_size_bytes |
Histogram | 응답 본문 크기 | method, endpoint |
백엔드 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
backend_health_status |
Gauge | 백엔드 헬스 (1=정상, 0=비정상) | backend_id, backend_url |
backend_health_check_duration_seconds |
Histogram | 헬스 체크 지속 시간 | backend_id |
backend_health_check_failures_total |
Counter | 총 헬스 체크 실패 | backend_id, error_type |
backend_request_latency_seconds |
Histogram | 백엔드 요청 지연 시간 | backend_id, endpoint |
backend_connection_pool_size |
Gauge | 연결 풀 크기 | backend_id |
backend_connection_pool_active |
Gauge | 풀의 활성 연결 | backend_id |
라우팅 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
routing_decisions_total |
Counter | 총 라우팅 결정 | strategy, selected_backend |
routing_backend_selection_duration_seconds |
Histogram | 백엔드 선택 시간 | strategy |
routing_model_availability |
Gauge | 백엔드별 모델 가용성 | model, backend_id |
routing_retries_total |
Counter | 총 재시도 횟수 | backend_id, reason |
routing_circuit_breaker_state |
Gauge | 서킷 브레이커 상태 | backend_id |
모델 서비스 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_cache_hits_total |
Counter | 모델 캐시 적중 | operation |
model_cache_misses_total |
Counter | 모델 캐시 미스 | operation |
model_refresh_duration_seconds |
Histogram | 모델 목록 새로고침 지속 시간 | backend_id |
model_discovery_errors_total |
Counter | 모델 검색 오류 | backend_id, error_type |
캐시 스탬피드 방지 메트릭¶
이 메트릭은 캐시 스탬피드 방지 메커니즘을 모니터링하는 데 도움이 됩니다:
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_stale_while_revalidate_total |
Counter | 갱신이 진행 중일 때 오래된 데이터를 반환한 요청 | - |
model_coalesced_requests_total |
Counter | 새로운 집계를 트리거하는 대신 진행 중인 집계를 기다린 요청 | - |
model_background_refreshes_total |
Counter | 시작된 백그라운드 갱신 작업 | - |
model_background_refresh_successes_total |
Counter | 성공한 백그라운드 갱신 작업 | - |
model_background_refresh_failures_total |
Counter | 실패한 백그라운드 갱신 작업 | - |
model_singleflight_lock_acquired_total |
Counter | 싱글플라이트를 위해 집계 잠금이 획득된 횟수 | - |
캐시 스탬피드 메트릭 이해하기¶
- 높은
coalesced_requests: 싱글플라이트 패턴이 중복 집계를 효과적으로 방지하고 있음을 나타냄 - 높은
stale_while_revalidate: stale-while-revalidate 패턴이 갱신 중에 캐시된 데이터를 반환하고 있음을 보여줌 - 낮은
background_refresh_failures: 백그라운드 갱신이 올바르게 작동하고 있음을 확인 - 캐시 미스 시 블로킹 없음:
background_refreshes > 0일 때, 요청은 캐시 갱신을 거의 기다리지 않아야 함
스트리밍 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
streaming_active_connections |
Gauge | 활성 스트리밍 연결 | endpoint |
streaming_events_sent_total |
Counter | 전송된 총 SSE 이벤트 | endpoint, event_type |
streaming_connection_duration_seconds |
Histogram | 스트리밍 연결 지속 시간 | endpoint |
streaming_errors_total |
Counter | 스트리밍 오류 | endpoint, error_type |
미드스트림 폴백 메트릭¶
미드스트림 폴백 기능이 활성화된 경우(streaming.mid_stream_fallback.enabled: true)에 방출되는 메트릭입니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
streaming_fallback_total |
Counter | 미드스트림 폴백 시도 총 횟수 | 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]))
폴백 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
fallback_attempts_total |
Counter | 총 폴백 시도 | original_model, fallback_model, reason |
fallback_success_total |
Counter | 성공한 폴백 | original_model, fallback_model |
fallback_exhausted_total |
Counter | 소진된 폴백 체인 | original_model |
fallback_cross_provider_total |
Counter | 크로스 프로바이더 폴백 | from_provider, to_provider |
fallback_duration_seconds |
Histogram | 폴백 작업 지속 시간 | original_model |
응답 캐시 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
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) |
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 이벤트 컨슈머 메트릭¶
이 메트릭은 vLLM KV 이벤트 컨슈머가 활성화된 경우(src/infrastructure/kv_index/) 수집됩니다. 모든 백엔드 레이블 값은 카디널리티 폭발을 방지하기 위해 정규화됩니다.
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
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 | 확인된 고유 접두사 키의 대략적인 수 | -- |
주요 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 |
주요 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_id |
tokens_consumed_total |
Counter | 소비된 총 토큰 | model, operation |
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_verdicts_total |
Counter | 모드 의미를 적용한 후 요청당 집계 판정 | stage, mode, result |
레이블 값:
stage는input,output,streaming.result는allow,block,transform,flag.kind는timeout또는error.mode는monitor또는enforce.guardrail_verdicts_total이mode를 담으므로, 게이팅하지 않는 monitor 모드 판정도 보입니다. 이 점이 monitor 후 enforce 롤아웃을 관찰 가능하게 만듭니다.
주요 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 토큰을 얼마나 썼나'와 같은 질문에 바로 답할 수 있습니다. 기존의 집계 카운터(tokens_consumed_total)와 독립적인 별도 메트릭이고, 용량 산정과 공정 사용 정책, 추후의 비용 귀속 산정에 쓰입니다.
메트릭 정의¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
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:8000', 'router2:8000']
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: continuum-router
endpoints:
- port: metrics
interval: 15s
path: /metrics
Grafana 대시보드¶
제공된 Grafana 대시보드는 다음을 포함합니다:
개요 패널¶
- 요청 속도 및 오류율
- P50, P95, P99 지연 시간
- 활성 연결
- 백엔드 헬스 상태
백엔드 성능¶
- 백엔드별 지연 시간
- 헬스 체크 성공률
- 연결 풀 활용률
- 서킷 브레이커 상태
모델 사용¶
- 모델 요청 분포
- 캐시 적중률
- 토큰 소비
- 모델 가용성 매트릭스
알림 개요¶
- 활성 알림
- 알림 기록
- SLO 준수
대시보드 가져오기:
- Grafana 열기
- 대시보드 -> 가져오기로 이동
monitoring/grafana/dashboards/router-overview.json업로드- Prometheus 데이터 소스 선택
- 가져오기 클릭
알림¶
사전 설정된 알림 규칙이 monitoring/prometheus/alerts.yml에 있습니다:
중요 알림¶
{% raw %}
- alert: BackendDown
expr: backend_health_status == 0
for: 1m
annotations:
summary: "백엔드 {{ $labels.backend_id }}이(가) 다운됨"
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[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: LowCacheHitRate
expr: rate(model_cache_hits_total[5m]) / rate(model_cache_total[5m]) < 0.8
for: 10m
annotations:
summary: "캐시 적중률 80% 미만: {{ $value | humanizePercentage }}"
예제¶
쿼리 예제¶
상태별 요청 속도¶
엔드포인트별 P95 지연 시간¶
백엔드 헬스 개요¶
모델 사용량 순위¶
오류율 백분율¶
프로그래밍 방식 접근¶
메트릭에 프로그래밍 방식으로 접근할 수도 있습니다:
import requests
from prometheus_client.parser import text_string_to_metric_families
# 메트릭 가져오기
response = requests.get('http://localhost:8000/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:8000/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 게이지)
- 집계 함수 확인
- 레이블 선택기 검토
- 시간 범위 검증