vLLM 서빙에서 Throughput(전체 처리량)과 TTFT(첫 토큰 지연 시간)는 트레이드오프 관계를 가집니다. 대규모 배치 처리는 Throughput을 극대화하지만 긴 Prefill 연산으로 인해 기존 요청의 ITL과 신규 요청의 TTFT를 저하시킵니다.
워크로드 특성(RAG 기반 무거운 프롬프트 vs 실시간 대화형 서비스)에 맞춘 핵심 엔진 파라미터 조합과 튜닝 전략을 정리했습니다.
--enable-chunked-prefill)--enable-chunked-prefill
--max-num-batched-tokens)--max-num-seqs)--gpu-memory-utilization)0.90 (90%).0.92 ~ 0.95까지 상향하여 KV Cache 블록 수를 최대로 확보하는 것이 Throughput에 유리합니다.> 0.96) CUDA Graph 캡처 메모리나 임시 활성화 텐서(Activation) 공간 부족으로 OOM이 발생할 수 있습니다.--max-model-len)max_position_embeddings)를 기준으로 KV Cache 슬롯 공간을 계산합니다.--max-model-len 8192 또는 16384로 제약해야 합니다.--enable-prefix-caching)python3 -m vllm.entrypoints.openai.api_server \
--model /models/Llama-3.1-70B-Instruct \
--tensor-parallel-size 8 \
--gpu-memory-utilization 0.92 \
--max-model-len 8192 \
--enable-chunked-prefill \
--max-num-batched-tokens 512 \
--max-num-seqs 128 \
--enable-prefix-caching
python3 -m vllm.entrypoints.openai.api_server \
--model /models/Llama-3.1-70B-Instruct \
--tensor-parallel-size 8 \
--gpu-memory-utilization 0.95 \
--max-model-len 16384 \
--enable-chunked-prefill \
--max-num-batched-tokens 2048 \
--max-num-seqs 256 \
--enable-prefix-caching
파라미터 변경 후 Phase 5 부하 테스트를 수행하며 /metrics 엔드포인트에서 아래 3가지 메트릭 추이를 확인해야 합니다.
vllm:num_requests_waiting (대기 큐 지표)--max-num-seqs를 늘리기보다는 클러스터 차원의 Rate Limiting 또는 노드 스케일아웃이 필요합니다.vllm:gpu_cache_usage_factor (KV Cache 포화도)vllm:time_to_first_token_seconds (TTFT 분포)--enable-chunked-prefill 적용 전후의 P95/P99 구간을 비교하여 긴 프롬프트 유입 시 TTFT 스파이크가 해소되었는지 검증합니다.===
vLLM 서빙 엔진과 GPU 하드웨어(DCGM Exporter)의 상태를 실시간 수집하고 이상 징후를 감지하기 위한 Prometheus 설정 및 Alertmanager 경보 규칙입니다.
scrape_configs)vLLM의 메트릭 엔드포인트(기본 포트 8000, 경로 /metrics)와 NVIDIA DCGM Exporter(기본 포트 9400)를 정기적으로 폴링하도록 설정합니다.
# prometheus.yml
scrape_configs:
# ----------------------------------------------------
# 1. vLLM Serving Engine Metrics
# ----------------------------------------------------
- job_name: 'vllm-serving'
scrape_interval: 5s # 큐 상태 및 실시간 캐시 변동 추적을 위해 짧은 주기 권장
scrape_timeout: 4s
metrics_path: /metrics
static_configs:
- targets: ['<GPU_NODE_IP>:8000']
labels:
cluster: 'gpu-platform'
role: 'llm-inference'
model: 'llama-3.1-70b'
# ----------------------------------------------------
# 2. NVIDIA DCGM Exporter (Hardware & GPU Metrics)
# ----------------------------------------------------
- job_name: 'dcgm-exporter'
scrape_interval: 10s # 하드웨어 센서/전력 모니터링 주기
scrape_timeout: 8s
metrics_path: /metrics
static_configs:
- targets: ['<GPU_NODE_IP>:9400']
labels:
cluster: 'gpu-platform'
role: 'gpu-telemetry'
alert_rules.yml)엔진 레벨의 성능 병목(큐잉, KV Cache 고갈, TTFT 지연)과 하드웨어 레벨의 치명적 결함(ECC 에러, 과열, XID 오류)을 분리하여 감시합니다.
groups:
# ====================================================
# Group 1: vLLM Inference Engine Alerts
# ====================================================
- name: vllm_serving_alerts
rules:
- alert: VLLMInstanceDown
expr: up{job="vllm-serving"} == 0
for: 30s
labels:
severity: critical
annotations:
summary: "vLLM serving instance is down"
description: "Target {{ $labels.instance }} has been unreachable for more than 30 seconds."
- alert: VLLMKVCacheSaturation
expr: vllm:gpu_cache_usage_factor > 0.95
for: 1m
labels:
severity: warning
annotations:
summary: "vLLM KV Cache is nearly saturated (>95%)"
description: "Instance {{ $labels.instance }} GPU cache usage is at {{ $value | humanizePercentage }}. Risk of request eviction or queuing."
- alert: VLLMHighRequestQueuing
expr: vllm:num_requests_waiting > 10
for: 1m
labels:
severity: warning
annotations:
summary: "vLLM request queue backlog detected"
description: "Instance {{ $labels.instance }} has {{ $value }} requests queued for over 1 minute. Serving capacity is saturated."
- alert: VLLMHighTTFTLatency
expr: |
histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le, instance, model_name)) > 2.5
for: 3m
labels:
severity: warning
annotations:
summary: "P95 TTFT latency exceeds 2.5s"
description: "Model {{ $labels.model_name }} on {{ $labels.instance }} P95 TTFT is {{ $value }}s for the last 5 minutes."
- alert: VLLMRequestPreemptionDetected
expr: rate(vllm:num_preemptions_total[2m]) > 0
for: 30s
labels:
severity: critical
annotations:
summary: "vLLM request preemptions occurring"
description: "Instance {{ $labels.instance }} is preempting/recomputing requests due to strict memory limits."
# ====================================================
# Group 2: GPU Hardware & DCGM Telemetry Alerts
# ====================================================
- name: gpu_hardware_alerts
rules:
- alert: DCGMExporterDown
expr: up{job="dcgm-exporter"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "DCGM Exporter is down"
description: "Hardware telemetry on {{ $labels.instance }} is unavailable."
- alert: GPUCriticalTemperature
expr: DCGM_FI_DEV_GPU_TEMP > 83
for: 30s
labels:
severity: critical
annotations:
summary: "GPU temperature is critical (>83°C)"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} has reached {{ $value }}°C. Thermal throttling imminent."
- alert: GPUClockThrottled
expr: DCGM_FI_DEV_CLOCK_THROTTLE_REASONS > 0
for: 1m
labels:
severity: warning
annotations:
summary: "GPU clock throttling active"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} is throttled (Reason bitmask: {{ $value }})."
- alert: GPUEccDoubleBitError
expr: increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[5m]) > 0
labels:
severity: critical
annotations:
summary: "Uncorrectable ECC double-bit error detected"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} detected double-bit ECC memory corruption. Immediate hardware check required."
- alert: GPUXidErrorOccurred
expr: DCGM_FI_DEV_XID_ERRORS > 0
labels:
severity: critical
annotations:
summary: "NVIDIA driver XID error detected"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} logged XID error code: {{ $value }}."
- alert: GPUNVLinkErrorDetected
expr: increase(DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL[5m]) > 0
labels:
severity: warning
annotations:
summary: "NVLink CRC error count increasing"
description: "NVLink on GPU {{ $labels.gpu }} ({{ $labels.instance }}) is reporting transmission CRC errors."
| 모니터링 영역 | PromQL 표현식 | 이상 기준 / 해석 |
|---|---|---|
| 토큰 생성 처리량 | sum(rate(vllm:request_generation_tokens_total[1m])) by (instance) | 초당 생성 토큰 수(TPS) 측정 |
| KV Cache 여유량 | (1 - vllm:gpu_cache_usage_factor) * 100 | 10% 미만으로 떨어질 경우 대기열 적체 임박 |
| 실행 중인 동시 요청 | vllm:num_requests_running | 현재 GPU에서 동시 Decoding 중인 시퀀스 수 |
| 대기 큐 요청 수 | vllm:num_requests_waiting | 지속적으로 0보다 크면 서빙 노드 증설 필요 |
| GPU 전력 사용량 | DCGM_FI_DEV_POWER_USAGE | 스펙상 정격 TDP 대비 피크 도달 여부 점검 |
| GPU SM 연산 점유율 | DCGM_FI_DEV_GPU_UTIL | Prefill 구간에서 100% 도달, Decode 구간에서는 통상 HBM 대역폭(DCGM_FI_DEV_MEM_COPY_UTIL)과 함께 확인 |
===
K8s 조인 전 GPU 노드 단독 환경에서는 RHEL 10.2의 Podman과 systemd(또는 Podman Pod)를 활용해 Prometheus, Grafana, DCGM Exporter를 간략하게 묶어 올리는 구성이 가장 깔끔합니다.
호스트 네트워크 모드(--net=host)를 사용하면 복잡한 포트 포워딩이나 브리지 인터페이스 설정 없이, 호스트에 떠 있는 vLLM(:8000)과 DCGM Exporter(:9400)의 메트릭을 즉시 스크랩하고 Grafana(:3000) 웹 UI로 모니터링할 수 있습니다.
GPU 노드의 호스트 디렉터리에 Prometheus 설정과 Grafana 데이터 경로를 생성합니다.
# 1. 설정 및 데이터 디렉터리 생성
mkdir -p /opt/monitoring/{prometheus,grafana_data}
chmod 777 /opt/monitoring/grafana_data # Grafana 컨테이너 UID(472) 쓰기 권한
# 2. Prometheus 수집 설정 파일 작성 (/opt/monitoring/prometheus/prometheus.yml)
cat <<'EOF' > /opt/monitoring/prometheus/prometheus.yml
global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
# 1. GPU 하드웨어 메트릭 (DCGM Exporter)
- job_name: 'dcgm'
static_configs:
- targets: ['127.0.0.1:9400']
# 2. vLLM 추론 엔진 서빙 메트릭
- job_name: 'vllm'
metrics_path: /metrics
static_configs:
- targets: ['127.0.0.1:8000']
EOF
호스트의 GPU와 네트워크를 직접 공유하도록 실행합니다.
RHEL 10의 CDI(Container Device Interface)를 통해 GPU 장치 접근 권한을 넘겨줍니다.
podman run -d --name dcgm-exporter \
--restart unless-stopped \
--device nvidia.com/gpu=all \
--net=host \
nexus.internal:8082/nvidia/k8s-device-plugin/dcgm-exporter:latest
동작 확인:
curl -s http://127.0.0.1:9400/metrics | grep DCGM_FI_DEV_GPU_TEMP
podman run -d --name prometheus \
--restart unless-stopped \
--net=host \
-v /opt/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro,Z \
nexus.internal:8082/prom/prometheus:latest \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.retention.time=7d
동작 확인:
curl -s http://127.0.0.1:9090/-/ready
# "Prometheus is Ready." 출력 확인
podman run -d --name grafana \
--restart unless-stopped \
--net=host \
-v /opt/monitoring/grafana_data:/var/lib/grafana:Z \
nexus.internal:8082/grafana/grafana:latest
http://<GPU_NODE_IP>:3000 (외부망 ConnectX-6 IP)admin / admin (첫 로그인 시 변경)[http://127.0.0.1:9090](http://127.0.0.1:9090) (호스트 네트워크 모드이므로 로컬호스트 지정)examples/vllm_prometheus_grafana/)에 포함된 기본 대시보드 JSON을 Import하여 TTFT, TPS, KV Cache 추이를 시각화.성능 테스트가 끝나고 추후 정식 K8s Worker 노드로 조인할 때는 아래 명령으로 깔끔하게 제거할 수 있습니다.
# 모니터링 컨테이너 일괄 중지 및 삭제
podman rm -f dcgm-exporter prometheus grafana
# (선택) 임시 수집 데이터 정리
rm -rf /opt/monitoring