메트릭
메트릭 (Metrics)
vLLM은 V1 엔진의 관찰 가능성(observability)과 용량 계획을 지원하는 풍부한 메트릭 세트를 제공합니다.
출처: 문서
본문
목표 (Objectives)
- 프로덕션 모니터링을 돕는 엔진·요청 레벨 메트릭의 포괄적인 커버리지를 제공합니다.
- 프로덕션 환경에서 사용될 것으로 기대되는 Prometheus 통합에 우선순위를 둡니다.
- 임시 테스트, 디버깅, 개발, 탐색적 사용 사례를 위한 로깅 지원(즉 메트릭을 info 로그에 출력)을 제공합니다.
배경 (Background)
vLLM의 메트릭은 다음과 같이 분류할 수 있습니다:
- 서버 레벨 메트릭: LLM 엔진의 상태와 성능을 추적하는 전역 메트릭. 보통 Prometheus에서 Gauge 또는 Counter로 노출됩니다.
- 요청 레벨 메트릭: 개별 요청의 특성(예: 크기와 타이밍)을 추적하는 메트릭. 보통 Prometheus에서 Histogram으로 노출되며, vLLM을 모니터링하는 SRE가 추적하는 SLO인 경우가 많습니다.
개념적 모델은 서버 레벨 메트릭이 요청 레벨 메트릭의 값을 설명하는 데 도움을 준다는 것입니다.
v1 메트릭
v1에서는 광범위한 메트릭 세트가 vllm: 접두사를 사용하는 Prometheus 호환 /metrics 엔드포인트로 노출됩니다. 예:
vllm:num_requests_running(Gauge) — 현재 실행 중인 요청 수.vllm:kv_cache_usage_perc(Gauge) — 사용된 KV 캐시 블록의 비율 (0–1).vllm:prefix_cache_queries(Counter) — 프리픽스 캐시 쿼리 수.vllm:prefix_cache_hits(Counter) — 프리픽스 캐시 히트 수.vllm:prompt_tokens_total(Counter) — 처리된 총 프롬프트 토큰 수.vllm:generation_tokens_total(Counter) — 생성된 총 토큰 수.vllm:request_success_total(Counter) — 종료된 요청 수(finish reason별).vllm:request_prompt_tokens(Histogram) — 입력 프롬프트 토큰 수의 히스토그램.vllm:request_generation_tokens(Histogram) — 생성 토큰 수의 히스토그램.vllm:time_to_first_token_seconds(Histogram) — 첫 토큰까지의 시간 (TTFT).vllm:inter_token_latency_seconds(Histogram) — 토큰 간 지연 시간(연속 스트림 출력 사이의 시간).vllm:request_time_per_output_token_seconds(Histogram) — 요청별 출력 토큰당 시간 (TPOT).vllm:e2e_request_latency_seconds(Histogram) — 종단 간 요청 지연 시간.vllm:request_prefill_time_seconds(Histogram) — 요청 prefill 시간.vllm:request_decode_time_seconds(Histogram) — 요청 decode 시간.
이들은 추론·서빙 -> 프로덕션 메트릭에서 문서화돼 있습니다.
Grafana 대시보드 (Grafana Dashboard)
vLLM은 이런 메트릭을 Prometheus로 수집·저장하고 Grafana 대시보드로 시각화하는 방법의 참고 예제도 제공합니다.
Grafana 대시보드에 노출된 메트릭 부분 집합은 어떤 메트릭이 특히 중요한지 알려줍니다:
vllm:e2e_request_latency_seconds_bucket— 초 단위 종단 간 요청 지연 시간.vllm:prompt_tokens— 프롬프트 토큰.vllm:generation_tokens— 생성 토큰.vllm:inter_token_latency_seconds— 연속 스트림 출력 사이에 측정된 초 단위 토큰 간 지연 시간. 각 출력이 정확히 한 토큰이면 TPOT에 근사하지만, 출력이 여러 토큰이거나 집계 가중치가 다르면 요청 레벨 TPOT와 다릅니다.vllm:request_time_per_output_token_seconds— 초 단위 요청별 출력 토큰당 시간(TPOT).(end-to-end latency - TTFT) / (number of output tokens - 1)로 계산됩니다. 토큰을 하나 이하 생성한 요청은 0으로 기록됩니다.vllm:time_to_first_token_seconds— 초 단위 첫 토큰까지의 시간 (TTFT) 지연 시간.vllm:num_requests_running(또한_swapped,_waiting) — RUNNING, WAITING, SWAPPED 상태의 요청 수.vllm:kv_cache_usage_perc— vLLM이 사용하는 캐시 블록 비율.vllm:request_prompt_tokens— 요청 프롬프트 길이.vllm:request_generation_tokens— 요청 생성 길이.vllm:request_success— finish reason별로 종료된 요청 수: EOS 토큰이 생성됐거나 최대 시퀀스 길이에 도달.vllm:request_queue_time_seconds— 큐 대기 시간.vllm:request_prefill_time_seconds— 요청 prefill 시간.vllm:request_decode_time_seconds— 요청 decode 시간.vllm:request_max_num_generation_tokens— 시퀀스 그룹의 최대 생성 토큰 수.
이 대시보드를 추가한 PR에서 여기서 내린 선택에 대한 흥미롭고 유용한 배경을 볼 수 있습니다.
vllm:inter_token_latency_seconds는 스트림 출력 이벤트마다 한 샘플(연속 출력 사이의 벽시계 간격)을 기록하는 반면, vllm:request_time_per_output_token_seconds는 종료된 요청마다 한 번씩 (end-to-end latency - TTFT) / (number of output tokens - 1)로 기록됩니다. 이 두 메트릭은 출력이 여러 토큰을 묶을 때(예: 추측 디코딩) 또는 평균 ITL과 요청별 평균 TPOT가 서로 다른 가중치로 집계될 때 다릅니다. 벤치마크 정의는 지연 시간 메트릭 이해하기를 참고하세요. 토큰을 하나 이하 생성한 요청은 TPOT 0으로 기록되지만, vllm bench serve는 이를 TPOT 통계에서 제외합니다. 따라서 Prometheus 히스토그램과 벤치마크 TPOT 통계는 다를 수 있습니다. 요청 레벨 TPOT에는 vllm:request_time_per_output_token_seconds를, 출력 간 지연 분포를 구체적으로 원할 때는 vllm:inter_token_latency_seconds를 사용하세요.
Prometheus 클라이언트 라이브러리 (Prometheus Client Library)
Prometheus 지원은 처음에 aioprometheus 라이브러리로 추가됐지만, 곧 prometheus_client로 전환됐습니다. 근거는 두 PR 모두에서 논의됩니다.
그 전환 과정에서 HTTP 메트릭을 추적하는 MetricsMiddleware를 잠시 잃었지만, prometheus_fastapi_instrumentator로 복원됐습니다:
$ curl http://0.0.0.0:8000/metrics 2>/dev/null | grep -P '^http_(?!.*(_bucket|_created|_sum)).*'
http_requests_total{handler="/v1/completions",method="POST",status="2xx"} 201.0
http_request_size_bytes_count{handler="/v1/completions"} 201.0
http_response_size_bytes_count{handler="/v1/completions"} 201.0
http_request_duration_highr_seconds_count 201.0
http_request_duration_seconds_count{handler="/v1/completions",method="POST"} 201.0
다중 프로세스 모드 (Multi-process Mode)
역사적으로 메트릭은 엔진 코어 프로세스에서 수집되고 multiprocess 모드로 API 서버 프로세스에서 사용 가능하게 했습니다. Pull Request #7279 참고.
최근에는 메트릭을 API 서버 프로세스에서 수집하며, multiprocess 모드는 --api-server-count > 1일 때만 사용합니다. Pull Request #17546과 API 서버 스케일아웃의 세부사항을 참고하세요.
내장 Python/프로세스 메트릭 (Built in Python/Process Metrics)
다음 메트릭은 prometheus_client가 기본으로 지원하지만 multiprocess 모드를 사용할 때는 노출되지 않습니다:
python_gc_objects_collected_totalpython_gc_objects_uncollectable_totalpython_gc_collections_totalpython_infoprocess_virtual_memory_bytesprocess_resident_memory_bytesprocess_start_time_secondsprocess_cpu_seconds_totalprocess_open_fdsprocess_max_fds
따라서 --api-server-count > 1일 때는 이 메트릭들을 사용할 수 없습니다. vLLM 인스턴스를 구성하는 모든 프로세스에 대한 통계를 집계하지 않으므로 이 메트릭의 관련성이 의문시됩니다.
메트릭 설계 (Metrics Design)
메트릭 설계의 상당 부분이 "Even Better Observability" 기능에서 계획됐습니다. 예를 들어 상세 로드맵이 제시된 곳을 참고하세요.
레거시 PR들 (Legacy PRs)
메트릭 설계의 배경을 이해하는 데 도움이 되도록, 원래의 (이제 레거시인) 메트릭을 추가한 관련 PR을 몇 개 나열합니다:
메트릭 구현 PR들 (Metrics Implementation PRs)
메트릭 구현 Issue #10582과 관련된 PR들입니다:
- Pull Request #11962
- Pull Request #11973
- Pull Request #10907
- Pull Request #12416
- Pull Request #12478
- Pull Request #12516
- Pull Request #12530
- Pull Request #12561
- Pull Request #12579
- Pull Request #12592
- Pull Request #12644
메트릭 수집 (Metrics Collection)
v1에서는 매 forward pass 사이의 시간을 최소화하기 위해 계산과 오버헤드를 엔진 코어 프로세스 밖으로 옮기고자 합니다.
V1 EngineCore 설계의 전체 아이디어는:
- EngineCore는 내부 루프입니다. 여기서 성능이 가장 중요합니다.
- AsyncLLM은 외부 루프입니다. (이상적으로는) GPU 실행과 겹쳐지므로 "오버헤드"는 가능하면 여기 있어야 합니다. 그래서 가능하면
AsyncLLM.output_handler_loop가 메트릭 북키핑에 이상적인 위치입니다.
프론트엔드 API 서버에서 메트릭을 수집하고, 엔진 코어 프로세스가 프론트엔드에 반환하는 EngineCoreOutputs에서 얻을 수 있는 정보에 메트릭을 기반으로 해서 이를 달성합니다.
구간 계산 (Interval Calculations)
많은 메트릭이 요청 처리의 다양한 이벤트 사이의 시간 구간입니다. 구간 계산에는 "벽시계 시간"(time.time())이 아니라 "단조 시간"(time.monotonic()) 기반의 타임스탬프를 사용하는 것이 좋습니다. 전자는 시스템 클럭 변화(예: NTP)의 영향을 받지 않기 때문입니다.
또한 단조 클럭은 프로세스마다 다릅니다 — 각 프로세스는 자신의 참조점을 갖습니다. 따라서 서로 다른 프로세스의 단조 타임스탬프를 비교하는 것은 무의미합니다.
그래서 구간을 계산하려면 같은 프로세스의 두 단조 타임스탬프를 비교해야 합니다.
스케줄러 통계 (Scheduler Stats)
엔진 코어 프로세스는 스케줄러에서 몇 가지 핵심 통계(예: 마지막 스케줄러 패스 이후 스케줄링되거나 대기 중인 요청 수)를 수집해 그 통계를 EngineCoreOutputs에 포함시킵니다.
엔진 코어 이벤트 (Engine Core Events)
엔진 코어는 특정 per-request 이벤트의 타임스탬프도 기록해 프론트엔드가 이 이벤트 사이의 구간을 계산할 수 있게 합니다.
이벤트는:
QUEUED— 요청이 엔진 코어에 수신되어 스케줄러 큐에 추가됐을 때.SCHEDULED— 요청이 처음으로 실행 스케줄링됐을 때.PREEMPTED— 다른 요청이 완료될 자리를 만들기 위해 요청이 대기 큐에 다시 들어갔을 때. 이후 다시 스케줄링되고 prefill 단계를 재시작합니다.NEW_TOKENS—EngineCoreOutput에 포함된 출력이 생성됐을 때. 이는 주어진 반복의 모든 요청에 공통이므로EngineCoreOutputs에 단일 타임스탬프를 사용해 기록합니다.
그리고 계산되는 구간은:
- 큐 구간 —
QUEUED와 가장 최근SCHEDULED사이. - Prefill 구간 — 가장 최근
SCHEDULED와 그 뒤의 첫NEW_TOKENS사이. - Decode 구간 — (가장 최근
SCHEDULED이후) 첫NEW_TOKENS와 마지막NEW_TOKENS사이. - 추론 구간 — 가장 최근
SCHEDULED와 마지막NEW_TOKENS사이. - 토큰 간 구간 — 연속
NEW_TOKENS사이.
다르게 말하면: 프론트엔드가 보이는 이벤트의 타이밍을 사용해 이 구간을 계산하는 가능성을 탐구했습니다. 그러나 프론트엔드는 QUEUED와 SCHEDULED 이벤트의 타이밍을 볼 수 없고, 같은 프로세스의 단조 타임스탬프를 기반으로 구간을 계산해야 하므로 ... 엔진 코어가 이 모든 이벤트의 타임스탬프를 기록해야 합니다.
구간 계산과 선점 (Interval Calculations vs Preemptions)
decode 중 선점이 발생하면 이미 생성된 토큰은 재사용되므로, 선점이 토큰 간, decode, 추론 구간에 영향을 준다고 간주합니다.
prefill 중 선점이 발생하면(그런 이벤트가 가능하다고 가정) 첫 토큰까지의 시간과 prefill 구간에 영향을 준다고 간주합니다.
프론트엔드 통계 수집 (Frontend Stats Collection)
프론트엔드가 단일 EngineCoreOutputs(즉 단일 엔진 코어 반복의 출력)를 처리하면서 그 반복에 관련된 다양한 통계를 수집합니다:
- 이 반복에서 생성된 새 토큰의 총 수.
- 이 반복에서 완료된 prefill이 처리한 프롬프트 토큰의 총 수.
- 이 반복에서 스케줄링된 요청들의 큐 구간.
- 이 반복에서 prefill을 완료한 요청들의 prefill 구간.
- 이 반복에 포함된 모든 요청의 토큰 간 구간.
- 이 반복에서 prefill을 완료한 요청들의 첫 토큰까지의 시간(TTFT). 단, 입력 처리 시간을 고려하기 위해 이 구간을 프론트엔드가 요청을 처음 수신한 시각(
arrival_time) 기준으로 계산합니다. 현재arrival_time은 토큰화가 시작될 때 시작됩니다.
주어진 반복에서 완료된 요청에 대해서도 기록합니다:
- 추론·decode 구간 — 위에서 설명한 대로 scheduled 및 첫 토큰 이벤트 기준.
- 종단 간 지연 시간 — 프론트엔드
arrival_time과 프론트엔드가 최종 토큰을 수신한 사이의 구간.
KV 캐시 상주 메트릭 (KV Cache Residency Metrics)
샘플링된 KV 캐시 블록이 얼마나 오래 상주하는지, 얼마나 자주 재사용되는지 설명하는 히스토그램 세트도 방출합니다. 샘플링(--kv-cache-metrics-sample)은 오버헤드를 아주 작게 유지합니다. 블록이 선택되면 다음을 기록합니다:
lifetime— 할당 ⟶ 제거(eviction)idle before eviction— 마지막 접근 ⟶ 제거reuse gaps— 블록이 재사용될 때 접근 사이의 멈춤
이들은 Prometheus 메트릭에 직접 매핑됩니다:
vllm:kv_block_lifetime_seconds— 각 샘플링 블록이 존재하는 시간.vllm:kv_block_idle_before_evict_seconds— 마지막 접근 후의 유휴 꼬리.vllm:kv_block_reuse_gap_seconds— 연속 접근 사이의 시간.
엔진 코어는 SchedulerStats를 통해 원시 eviction 이벤트만 보냅니다. 프론트엔드가 이를 소비해 Prometheus 관측으로 바꾸고, 로깅이 켜져 있을 때는 LLM.get_metrics()로 같은 데이터를 노출합니다. 한 차트에서 lifetime과 idle time을 보면 갇힌 캐시(stranded cache)나 긴 decode 동안 프롬프트를 고정하는 워크로드를 쉽게 발견할 수 있습니다.
메트릭 게시 — 로깅 (Metrics Publishing - Logging)
LoggingStatLogger 메트릭 게시자는 매 5초마다 몇 가지 핵심 메트릭이 담긴 INFO 로그 메시지를 출력합니다:
- 현재 실행/대기 중인 요청 수
- 현재 GPU 캐시 사용량
- 지난 5초 동안 처리된 초당 프롬프트 토큰 수
- 지난 5초 동안 생성된 초당 새 토큰 수
- 가장 최근 1k KV 캐시 블록 쿼리에 대한 프리픽스 캐시 히트율
메트릭 게시 — Prometheus (Metrics Publishing - Prometheus)
PrometheusStatLogger 메트릭 게시자는 /metrics HTTP 엔드포인트를 통해 Prometheus 호환 형식으로 메트릭을 제공합니다. 그러면 Prometheus 인스턴스를 구성해 이 엔드포인트를 폴링(예: 매 초)하고 값을 시계열 데이터베이스에 기록할 수 있습니다. Prometheus는 Grafana를 통해 자주 사용되며, 이 메트릭을 시간에 따라 그래프로 표시할 수 있습니다.
Prometheus는 다음 메트릭 유형을 지원합니다:
- Counter: 시간이 지나며 증가하고 결코 줄지 않는 값. 일반적으로 vLLM 인스턴스가 재시작하면 0으로 리셋됩니다. 예: 인스턴스 수명 동안 생성된 토큰 수.
- Gauge: 위아래로 움직이는 값. 예: 현재 실행 스케줄링된 요청 수.
- Histogram: 버킷으로 기록되는 메트릭 샘플의 수. 예: TTFT가 <1ms, <5ms, <10ms, <20ms 등인 요청 수.
Prometheus 메트릭은 레이블을 붙일 수도 있어, 일치하는 레이블에 따라 메트릭을 결합할 수 있습니다. vLLM에서는 모든 메트릭에 model_name 레이블을 추가하는데, 여기에는 그 인스턴스가 서빙하는 모델 이름이 포함됩니다.
출력 예:
$ curl http://0.0.0.0:8000/metrics
# HELP vllm:num_requests_running Number of requests in model execution batches.
# TYPE vllm:num_requests_running gauge
vllm:num_requests_running{model_name="meta-llama/Llama-3.1-8B-Instruct"} 8.0
...
# HELP vllm:generation_tokens_total Number of generation tokens processed.
# TYPE vllm:generation_tokens_total counter
vllm:generation_tokens_total{model_name="meta-llama/Llama-3.1-8B-Instruct"} 27453.0
...
# HELP vllm:request_success_total Count of successfully processed requests.
# TYPE vllm:request_success_total counter
vllm:request_success_total{finished_reason="stop",model_name="meta-llama/Llama-3.1-8B-Instruct"} 1.0
vllm:request_success_total{finished_reason="length",model_name="meta-llama/Llama-3.1-8B-Instruct"} 131.0
vllm:request_success_total{finished_reason="abort",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
...
# HELP vllm:time_to_first_token_seconds Histogram of time to first token in seconds.
# TYPE vllm:time_to_first_token_seconds histogram
vllm:time_to_first_token_seconds_bucket{le="0.001",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.005",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.01",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.02",model_name="meta-llama/Llama-3.1-8B-Instruct"} 13.0
vllm:time_to_first_token_seconds_bucket{le="0.04",model_name="meta-llama/Llama-3.1-8B-Instruct"} 97.0
vllm:time_to_first_token_seconds_bucket{le="0.06",model_name="meta-llama/Llama-3.1-8B-Instruct"} 123.0
vllm:time_to_first_token_seconds_bucket{le="0.08",model_name="meta-llama/Llama-3.1-8B-Instruct"} 138.0
vllm:time_to_first_token_seconds_bucket{le="0.1",model_name="meta-llama/Llama-3.1-8B-Instruct"} 140.0
vllm:time_to_first_token_seconds_count{model_name="meta-llama/Llama-3.1-8B-Instruct"} 140.0
참고: 광범위한 사용 사례에서 사용자에게 가장 유용한 히스토그램 버킷 선택은 간단하지 않으며 시간이 지나며 개선이 필요할 것입니다.
캐시 구성 정보 (Cache Config Info)
prometheus_client는 Info 메트릭을 지원합니다. 이는 값이 영구적으로 1로 설정된 Gauge와 동등하지만 레이블을 통해 흥미로운 키/값 쌍 정보를 노출합니다. 이는 변하지 않는 인스턴스 정보에 사용되며 시작 시 한 번만 관측하면 되고, Prometheus에서 인스턴스 간 비교를 가능하게 합니다.
이 개념을 vllm:cache_config_info 메트릭에 사용합니다:
# HELP vllm:cache_config_info Information of the LLMEngine CacheConfig
# TYPE vllm:cache_config_info gauge
vllm:cache_config_info{block_size="16",cache_dtype="auto",cpu_offload_gb="0",enable_prefix_caching="False",gpu_memory_utilization="0.9",...} 1.0
그러나 prometheus_client는 multiprocessing 모드에서 Info 메트릭을 지원한 적이 없으며, 그 이유는 불명확합니다. 대신 값이 1로 설정되고 multiprocess_mode="mostrecent"인 Gauge 메트릭을 사용합니다.
LoRA 메트릭
vllm:lora_requests_info Gauge는 값이 현재 벽시계 시간이고 매 반복마다 갱신된다는 점만 제외하면 다소 비슷합니다.
사용되는 레이블 이름:
running_lora_adapters: 해당 어댑터를 사용해 실행 중인 요청 수의 어댑터별 개수. 콤마로 구분된 문자열로 형식화.waiting_lora_adapters: 유사하지만 스케줄링을 기다리는 요청을 셉니다.max_lora— "단일 배치의 최대 LoRA 수" 정적 구성.
여러 어댑터의 running/waiting 개수를 콤마로 구분된 문자열로 인코딩하는 것은 잘못된 것 같습니다. 어댑터별 개수를 레이블로 구분할 수 있기 때문입니다. 이것을 재검토해야 합니다.
multiprocess_mode="livemostrecent"이 사용됩니다 — 가장 최근 메트릭이 사용되지만 현재 실행 중인 프로세스에서만 사용됩니다.
이것은 Pull Request #9477에서 추가됐고 최소 한 명의 알려진 사용자가 있습니다. 이 설계를 재검토하고 옛 메트릭을 폐기한다면, 제거 전에 다운스트림 사용자와 조율해 마이그레이션할 수 있게 해야 합니다.
프리픽스 캐시 메트릭 (Prefix Cache metrics)
프리픽스 캐시 메트릭 추가에 관한 Issue #10582의 논의는 향후 메트릭 접근 방식에 관련된 흥미로운 점을 제시했습니다.
프리픽스 캐시가 쿼리될 때마다 쿼리된 토큰 수와 캐시에 존재하는(즉 히트한) 쿼리 토큰 수를 기록합니다.
그러나 관심 메트릭은 히트율 — 즉 쿼리당 히트 수입니다.
로깅의 경우, 가장 최근 고정 수의 쿼리(구간은 현재 가장 최근 1k 쿼리로 고정)에 대해 히트율을 계산하는 것이 사용자에게 가장 좋습니다.
Prometheus의 경우에는 Prometheus의 시계열 특성을 활용해 사용자가 원하는 구간에 대해 히트율을 계산하게 해야 합니다. 예: 지난 5분의 히트율을 계산하는 PromQL 쿼리:
rate(cache_query_hit[5m]) / rate(cache_query_total[5m])
이를 위해 히트율을 gauge로 기록하는 대신 쿼리와 히트를 Prometheus에서 카운터로 기록해야 합니다.
폐기된 메트릭 (Deprecated Metrics)
폐기 방법 (How To Deprecate)
메트릭 폐기는 가볍게 여겨선 안 됩니다. 사용자는 메트릭이 폐기됐다는 것을 알아차리지 못할 수 있고, 사용할 수 있는 동등한 메트릭이 있더라도 (사용자 관점에서) 갑자기 제거되면 상당히 불편할 수 있습니다.
예를 들어 vllm:avg_prompt_throughput_toks_per_s가 어떻게 폐기되고(코드 주석 포함), 제거됐다가 사용자에게 발견됐는지 확인하세요.
일반적으로:
- 메트릭 폐기에는 신중해야 합니다. 특히 사용자 영향을 예측하기 어려울 수 있기 때문입니다.
/metrics출력에 포함되는 help 문자열에 눈에 띄는 폐기 공지를 포함해야 합니다.- 사용자 대상 문서와 릴리스 노트에 폐기된 메트릭을 나열해야 합니다.
- 관리자에게 삭제 전에 탈출구(escape hatch)를 주기 위해 폐기된 메트릭을 CLI 인자 뒤에 숨기는 것을 고려해야 합니다.
프로젝트 전반의 폐기 정책은 폐기 정책을 참고하세요.
미구현 — vllm:tokens_total (Unimplemented)
Pull Request #4464가 추가했지만 분명히 구현된 적이 없습니다. 그냥 제거할 수 있습니다.
중복 — 큐 시간 (Duplicated - Queue Time)
vllm:time_in_queue_requests Histogram 메트릭은 Pull Request #9659가 추가했고 계산은:
self.metrics.first_scheduled_time = now
self.metrics.time_in_queue = now - self.metrics.arrival_time
2주 뒤 Pull Request #4464가 vllm:request_queue_time_seconds를 추가해 다음이 남았습니다:
if seq_group.is_finished():
if (seq_group.metrics.first_scheduled_time is not None and
seq_group.metrics.first_token_time is not None):
time_queue_requests.append(
seq_group.metrics.first_scheduled_time -
seq_group.metrics.arrival_time)
...
if seq_group.metrics.time_in_queue is not None:
time_in_queue_requests.append(
seq_group.metrics.time_in_queue)
이것은 중복으로 보이며 둘 중 하나를 제거해야 합니다. 후자는 Grafana 대시보드에서 사용되므로 전자를 폐기하거나 제거해야 합니다.
프리픽스 캐시 히트율 (Prefix Cache Hit Rate)
위 참고 — 이제 'queries'와 'hits' 카운터를 노출하며 'hit rate' gauge는 더 이상 아닙니다.
KV 캐시 오프로딩 (KV Cache Offloading)
두 레거시 메트릭은 v1에서 더 이상 관련 없는 "swapped" 선점 모드와 관련됩니다:
vllm:num_requests_swappedvllm:cpu_cache_usage_perc
이 모드에서 요청이 선점될 때(예: 다른 요청을 완료하기 위해 KV 캐시 공간을 만들기), kv cache 블록이 CPU 메모리로 스왑됐습니다. 이 기능이 V1에서 더 이상 사용되지 않아 --swap-space 플래그가 제거됐습니다.
역사적으로 vLLM은 오랫동안 beam search를 지원했습니다. SequenceGroup은 같은 프롬프트 kv 블록을 공유하는 N개의 시퀀스 개념을 캡슐화했습니다. 이는 요청 간 KV 캐시 블록 공유와 분기를 위한 copy-on-write를 가능하게 했습니다. CPU 스와핑은 이런 beam search 같은 사례를 위해 의도됐습니다.
나중에 KV 캐시 블록을 암시적으로 공유할 수 있는 프리픽스 캐싱 개념이 도입됐습니다. 블록을 필요에 따라 천천히 제거하고, 제거된 프롬프트 부분을 재계산할 수 있으므로 CPU 스와핑보다 더 나은 선택임이 입증됐습니다.
SequenceGroup은 V1에서 제거됐지만 "병렬 샘플링"(n>1)을 위해서는 대체가 필요할 것입니다. Beam search는 코어 밖으로 이동됐습니다. 매우 드문 기능을 위한 복잡한 코드가 많았습니다.
V1에서는 프리픽스 캐싱이 더 낫고(오버헤드 0) 기본으로 켜져 있으므로 선점·재계산 전략이 더 잘 작동해야 합니다.
향후 작업 (Future Work)
병렬 샘플링 (Parallel Sampling)
일부 레거시 메트릭은 "병렬 샘플링" 맥락에서만 관련됩니다. 이는 요청의 n 파라미터를 사용해 같은 프롬프트에서 여러 완성을 요청하는 것입니다.
Pull Request #10980에서 병렬 샘플링 지원을 추가하는 과정에서 이 메트릭도 추가해야 합니다.
vllm:request_params_n(Histogram)
종료된 모든 요청의 'n' 파라미터 값을 관측합니다.
vllm:request_max_num_generation_tokens(Histogram)
종료된 모든 시퀀스 그룹의 모든 시퀀스의 최대 출력 길이를 관측합니다. 병렬 샘플링이 없는 경우 vllm:request_generation_tokens와 동등합니다.
추측 디코딩 (Speculative Decoding)
일부 레거시 메트릭은 "추측 디코딩"에 특화돼 있습니다. 더 빠른 근사 방법이나 모델로 후보 토큰을 생성한 뒤 더 큰 모델로 그 토큰을 검증하는 것입니다.
vllm:spec_decode_draft_acceptance_rate(Gauge)vllm:spec_decode_efficiency(Gauge)vllm:spec_decode_num_accepted_tokens(Counter)vllm:spec_decode_num_draft_tokens(Counter)vllm:spec_decode_num_emitted_tokens(Counter)
v1에 "prompt lookup (ngram)" 추측 디코딩을 추가하는 PR이 검토 중입니다. 다른 기법도 그 뒤를 따를 것입니다. 이 맥락에서 이 메트릭을 재검토해야 합니다.
참고: 프리픽스 캐싱 히트율처럼 acceptance rate를 별도의 accepted·draft 카운터로 노출해야 할 것입니다. 효율성도 비슷한 처리가 필요할 것입니다.
자동 확장과 로드밸런싱 (Autoscaling and Load-balancing)
메트릭의 일반적인 사용 사례 중 하나는 vLLM 인스턴스의 자동 확장을 지원하는 것입니다.
Kubernetes Serving Working Group의 관련 논의:
- Kubernetes에서 대형 모델 서버 메트릭 표준화하기
- Kubernetes에서 성능 평가·자동 확장을 위한 LLM 워크로드 벤치마킹
- Inference Perf
- Issue #5041 및 Pull Request #12726.
이것은 사소하지 않은 주제입니다. Rob의 다음 코멘트를 고려하세요:
이 메트릭은 평균 요청 길이가 초당 쿼리 수보다 커지게 하는 최대 동시성을 추정하는 데 초점을 맞춰야 한다고 생각합니다 ... 실제로 서버를 "포화"시키는 것이 바로 이것이기 때문입니다.
명확한 목표는 이 포화 지점을 감지하는 데 필요한 메트릭을 노출해 관리자가 이를 기반으로 자동 확장 규칙을 구현할 수 있게 하는 것입니다. 그러나 그러기 위해서는 관리자(및 자동 모니터링 시스템)가 인스턴스가 포화에 접근하고 있다고 어떻게 판단해야 하는지에 대한 명확한 관점이 필요합니다:
모델 서버 컴퓨팅의 포화 지점(더 높은 요청률로 더 많은 처리량을 얻을 수 없고 추가 지연 시간이 발생하기 시작하는 변곡점)을 식별해 효과적으로 자동 확장할 수 있는 방법은 무엇인가?
메트릭 명명 (Metric Naming)
메트릭 명명에 대한 접근 방식은 재검토할 가치가 있습니다:
- 메트릭 이름에 콜론을 사용하는 것은 "콜론은 사용자 정의 기록 규칙을 위해 예약됐다"는 것과 상충되는 것 같습니다.
- 대부분의 메트릭은 단위로 끝나는 관례를 따르지만 전부는 아닙니다.
- 일부 메트릭 이름은
_total로 끝납니다:
메트릭 이름에 _total 접미사가 있으면 제거됩니다. 카운터에 대한 시계열을 노출할 때는 _total 접미사가 추가됩니다. 이는 OpenMetrics와 Prometheus 텍스트 형식 간의 호환성을 위한 것입니다(OpenMetrics는 _total 접미사를 요구).
메트릭 더 추가하기 (Adding More Metrics)
새 메트릭에 대한 아이디어는 부족하지 않습니다:
- TGI 같은 다른 프로젝트의 예
- 위의 Kubernetes 자동 확장 같은 특정 사용 사례에서 발생하는 제안
- OpenTelemetry Semantic Conventions for Gen AI 같은 표준화 노력에서 발생할 수 있는 제안
새 메트릭 추가에는 신중해야 합니다. 메트릭은 종종 비교적 추가하기 쉽지만:
- 제거하기 어렵습니다 — 위 폐기 절 참고.
- 활성화되면 의미 있는 성능 영향을 줄 수 있습니다. 그리고 메트릭은 기본·프로덕션에서 활성화할 수 없으면 사용 가치가 매우 제한적입니다.
- 프로젝트 개발·유지보수에 영향을 줍니다. 시간이 지나며 추가된 모든 메트릭이 이 노력을 더 시간 소모적으로 만들었으며, 모든 메트릭이 이 지속적인 유지보수 투자를 정당화하지는 않을 수 있습니다.
추적 — OpenTelemetry (Tracing - OpenTelemetry)
메트릭은 시간이 지남에 따른 시스템 성능·건강의 집계된 뷰를 제공합니다. 반면 추적은 개별 요청이 여러 서비스와 컴포넌트를 이동하며 추적합니다. 둘 다 더 일반적인 "관찰 가능성(Observability)" 범주에 속합니다.
vLLM은 OpenTelemetry 추적을 지원합니다:
- Pull Request #4687이 추가, Pull Request #20372이 복원
--otlp-traces-endpoint,--collect-detailed-traces로 구성- OpenTelemetry 블로그 포스트
- 사용자 문서
- 블로그 포스트
- IBM 제품 문서
OpenTelemetry에는 Gen AI Working Group이 있습니다.
메트릭은 그 자체로 충분히 큰 주제이므로 추적 주제는 메트릭과 상당히 별개로 간주합니다.
OpenTelemetry 모델 Forward vs Execute 시간 (Model Forward vs Execute Time)
현재 구현은 다음 두 메트릭을 노출합니다:
vllm:model_forward_time_milliseconds(Histogram) — 이 요청이 배치에 있을 때 모델 forward pass에 소요된 시간.vllm:model_execute_time_milliseconds(Histogram) — 모델 execute 함수에 소요된 시간. 모델 forward, 워커 간 블록/동기화, cpu-gpu 동기화 시간, 샘플링 시간을 포함합니다.
이 메트릭은 OpenTelemetry 추적이 활성화되고 --collect-detailed-traces=all/model/worker를 사용할 때만 활성화됩니다. 이 옵션의 문서는:
지정된 모듈에 대한 상세 추적을 수집합니다. 이는 비용이 들 수 있고/또는 블로킹 연산을 포함할 수 있어 성능에 영향을 줄 수 있습니다.
이 메트릭은 Pull Request #7089이 추가했고 OpenTelemetry 추적에 다음과 같이 표시됩니다:
-> gen_ai.latency.time_in_scheduler: Double(0.017550230026245117)
-> gen_ai.latency.time_in_model_forward: Double(3.151565277099609)
-> gen_ai.latency.time_in_model_execute: Double(3.6468167304992676)
이미 inference_time와 decode_time 메트릭이 있으므로, 더 높은 해상도 타이밍에 대한 충분히 공통적인 사용 사례가 있는지가 오버헤드를 정당화하는지의 문제입니다.
OpenTelemetry 지원 문제를 별도로 다룰 것이므로 이 특정 메트릭을 그 주제 아래에 포함하겠습니다.
더 알아보기 (Learn more)
- 프로덕션 메트릭 — 사용자 대상 메트릭 문서
- 구성 엔진 인자 — 메트릭 관련 인자
- Opentelemetry 예제 — 추적 사용법