메트릭 및 모니터링¶
이 문서는 Continuum Router의 메트릭 및 모니터링 기능을 설명합니다.
목차¶
개요¶
Continuum Router는 시스템 상태, 성능, 사용 패턴을 다룰 수 있도록 Prometheus 호환 메트릭을 제공합니다. 메트릭 시스템은 다음과 같이 설계되었습니다:
- 가벼움: 최소한의 성능 오버헤드
- 넓은 범위: 라우터의 모든 중요한 측면 포함
- 프로덕션 준비: 카디널리티 제한 및 적절한 레이블링 포함
- 쉬운 통합: 표준 Prometheus/Grafana 설정과 작동
빠른 시작¶
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 |
모델 서비스 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
model_cache_size |
Gauge | 집계 캐시의 모델 수 | — |
model_cache_hits_total |
Counter | 모델 집계 캐시 적중 | — |
model_cache_misses_total |
Counter | 모델 집계 캐시 미스 | — |
model_refresh_failures_total |
Counter | 실패한 백엔드 모델 조회 | — |
라우팅 텔레메트리는 아래의 실제 KV 캐시 및 스마트 라우팅 메트릭 패밀리에서 내보냅니다. 기존 범용 라우팅 및 모델 서비스 collector 중 지원되지 않는 항목은 등록하지 않습니다.
활성 스트리밍 응답 본문은 http_active_connections에 포함됩니다. 현재 범용 스트리밍 지속 시간 또는 응답 이후 오류 메트릭은 내보내지 않으며, 미드스트림 폴백에는 아래의 전용 실제 메트릭이 있습니다.
미드스트림 폴백 메트릭¶
미드스트림 폴백 기능이 활성화된 경우(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, 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 |
응답 캐시 메트릭¶
| 메트릭 | 유형 | 설명 | 레이블 |
|---|---|---|---|
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 이벤트 컨슈머 메트릭¶
이 메트릭은 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 |
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 지연 시간
- 백엔드 상태
- 엔드포인트별 요청 속도
- 응답 시간 백분위
- 모델 사용 분포
- 백엔드별 현재 처리 중 부하
가져오는 방법:
- 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 게이지)
- 집계 함수 확인
- 레이블 선택기 검토
- 시간 범위 검증