vLLM 프로덕션 모니터링 & 스케일링¶
| 환경 | EKS + vLLM + Grafana/Prometheus + DCGM Exporter |
|---|---|
| 기반 문서 | AWS EKS 공식: Identify scaling metric thresholds |
1. 전통 ML vs LLM 모니터링 — 왜 다른가?¶
기존 ML(분류/회귀/탐지 모델) 운영 경험이 있다면, 아래 비교표를 기준으로 LLM 모니터링의 차이점을 이해하면 됩니다.
| 구분 | 전통 ML (분류/회귀/탐지) | LLM 추론 (vLLM) |
|---|---|---|
| 핵심 지표 | Latency, Throughput, Error Rate, Accuracy | + TTFT, TPOT, KV Cache 사용율, Queue Depth |
| 병목 자원 | CPU / Memory | GPU VRAM + Memory Bandwidth (CPU 거의 무의미) |
| 스케일 시그널 | CPU > 70% 또는 Latency 증가 | Queue Depth 급증 (CPU/Memory는 참고 불가) |
| 응답 구조 | 요청 → 즉시 응답 (수ms) | 요청 → 스트리밍 토큰 생성 (수초~수십초) |
| 비용 단위 | 요청당 (request) | 토큰당 (input + output tokens) |
| 메모리 특성 | 고정 (모델 로드 후 일정) | 가변 (KV Cache가 요청마다 동적 할당/해제) |
| 배치 처리 | 정적 배치 (batch size 고정) | Continuous Batching (동적 합류/이탈) |
| Cold Start | 모델 로드 수초 | 모델 로드 2~10분 + CUDA 워밍업 필요 |
| OOM 패턴 | 드묾 (메모리 예측 가능) | 빈번 (긴 프롬프트/동시 요청 시 KV Cache 폭증) |
| GPU 사용율 해석 | 높을수록 좋음 | 30~50%도 정상 (Memory-bound 워크로드) |
왜 다른가?¶
[전통 ML]
입력 → [모델(고정)] → 출력
- 연산량 일정, 메모리 일정, 응답시간 예측 가능
[LLM 추론]
입력(가변 길이) → [Prefill: 전체 입력 처리] → 첫 토큰 (TTFT)
→ [Decode: 토큰 하나씩 생성] → ... → 완료
- 연산량 = 입력 길이 × 출력 길이에 비례
- KV Cache = 매 요청마다 동적 할당 (긴 입력 = 메모리 급증)
- 동시 요청 = KV Cache 경합 → OOM 또는 Preemption
대시보드 운영 관점 요약¶
| 전통 ML에서 보던 것 | LLM에서는 이것으로 대체 |
|---|---|
| CPU 사용율 → 스케일 판단 | ❌ 의미없음 → **Queue Depth**로 판단 |
| Response Time (단일 값) | TTFT + TPOT 분리 필요 (UX 영향 상이) |
| Memory 사용율 | KV Cache 사용율 (일반 Memory는 참고만) |
| Error Rate | 동일하게 유효 + Preemption Count 추가 |
| Model Accuracy | 프로덕션에서는 별도 평가 파이프라인 필요 (offline) |
2. 모니터링 아키텍처¶
Prometheus (kube-prometheus-stack)
├── vLLM /metrics (ServiceMonitor)
├── DCGM Exporter (GPU 메트릭)
└── Node Exporter (CPU/Mem)
↓ Remote Write (프로덕션 규모 시)
Amazon Managed Prometheus (AMP)
↓
Grafana (or Amazon Managed Grafana)
├── vLLM 대시보드
├── GPU 대시보드 (DCGM)
└── Kubernetes 대시보드
↓
KEDA ScaledObject
├── Queue depth trigger
└── P95 latency trigger (SLO guardrail)
↓
Karpenter NodePool (GPU 노드 자동 프로비저닝)
3. 핵심 모니터링 지표 — 3-Tier 체계¶
🔴 Tier 1 — LLM 서비스 품질 (User-facing SLI)¶
| 지표 (Prometheus metric) | 의미 | Grafana 패널 | 정상 범위 |
|---|---|---|---|
vllm:e2e_request_latency_seconds |
E2E 요청 레이턴시 (P50/P95/P99) | Time series | < SLO (모델/용도별 상이) |
vllm:time_to_first_token_seconds |
TTFT — 첫 토큰까지의 시간. UX에 직결 | Time series | < 1~3초 (입력 길이 의존) |
vllm:time_per_output_token_seconds |
TPOT — 토큰 생성 속도 | Gauge | 30~100ms (모델 크기/TP 의존) |
vllm:num_requests_waiting |
대기 큐 깊이 — 스케일 아웃의 1차 신호 | Stat + Alert | 0~5 |
vllm:num_requests_running |
현재 처리 중인 요청 수 | Stat | 모델/GPU 의존 |
vllm:request_success_total |
성공 요청 수 (rate 계산용) | Time series | 에러율 < 1% |
🟡 Tier 2 — vLLM 내부 상태 (성능 튜닝)¶
| 지표 | 의미 | 주의 기준 |
|---|---|---|
vllm:gpu_cache_usage_perc |
KV Cache 점유율 | > 80% 알람, > 95% 즉시 대응 |
vllm:num_preemptions_total |
선점(preemption) 발생 횟수 | 급증 시 GPU 메모리 부족 신호 |
vllm:prompt_tokens_total / generation_tokens_total |
입출력 토큰 처리량 | 용량 계획용 |
vllm:iteration_tokens_total |
배치 처리 반복당 토큰 수 | 배치 효율 분석 |
🟢 Tier 3 — GPU / 인프라 (노드 레벨)¶
| 지표 (DCGM) | 의미 |
|---|---|
DCGM_FI_DEV_GPU_UTIL |
GPU Compute 사용율 |
DCGM_FI_DEV_MEM_COPY_UTIL |
GPU Memory 대역폭 |
DCGM_FI_DEV_FB_USED / FB_FREE |
GPU VRAM 사용/잔여 |
DCGM_FI_DEV_POWER_USAGE |
전력 사용 (비용 추적) |
DCGM_FI_DEV_ECC_SBE_VOL_TOTAL |
GPU ECC 오류 (하드웨어 이상 감지) |
container_cpu_usage_seconds_total |
Pod CPU 사용율 |
container_memory_working_set_bytes |
Pod 메모리 |
!!! info "GPU Util 해석 주의" vLLM continuous batching은 메모리 바운드 워크로드 — GPU Compute 사용율이 30~50%여도 정상. CPU처럼 "사용율 높아야 좋다"는 해석은 적용되지 않음.
4. 스케일 아웃 임계값 — AWS 공식 방법론¶
📎 출처: AWS EKS 공식 문서 — Identify scaling metric thresholds for AI inference
4-1. 임계값 도출 방법 (k6 부하 테스트)¶
1. 워밍업: 100회 sequential 요청 (CUDA graph 캡처, 커널 컴파일 완료)
2. 10 → 20 → 30 → 40 → 50 → 60 → 70 RPS 구간 점진적 부하 투입
- 각 구간 60초 유지, 구간 간 30초 휴식
3. Grafana에서 동시 관찰:
- vllm:num_requests_waiting (큐 깊이)
- vllm:e2e_request_latency_seconds (레이턴시)
4. 큐 깊이가 0 근처에서 갑자기 급증하는 RPS 지점 = saturation point
5. saturation point 직전 값을 스케일 아웃 임계값으로 설정
4-2. 스케일 아웃 트리거¶
| 트리거 | 임계값 (예시) | 의미 | 비고 |
|---|---|---|---|
| Queue depth (1차) | vllm:num_requests_waiting > N (per pod) |
대기 요청 초과 시 | demand signal — k6로 N값 실측 |
| P95 latency (2차, SLO guardrail) | p95(e2e_latency) > SLO |
SLO 위반 직전에 선제 스케일 | 긴 프롬프트/이미지 입력 시 유효 |
!!! warning "임계값은 반드시 자체 워크로드로 실측" 위 예시 값은 AWS 문서의 소형 모델(Ministral-3B + g6e.4xlarge) 기준입니다. 모델 크기, GPU 유형, 입력 패턴에 따라 saturation point가 크게 달라지므로 반드시 자체 k6 부하테스트로 확인 필요.
4-3. 스케일 인 조건¶
| 조건 | 설정 |
|---|---|
| Queue == 0 + Latency < SLO/2 | 5분 이상 지속 시 Scale In |
stabilizationWindowSeconds |
300초 이상 (GPU Pod 기동 느림 — 모델 로딩 2~10분) |
| 한번에 제거하는 Pod 수 | 최대 1개/2분 (보수적) |
5. KEDA ScaledObject 설정 (예시)¶
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: vllm-inference-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-inference-app
minReplicaCount: 1
maxReplicaCount: 5
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleUp:
stabilizationWindowSeconds: 30 # 빠른 스케일 업
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 보수적 스케일 다운
policies:
- type: Pods
value: 1
periodSeconds: 120
triggers:
# 1차: 큐 깊이 (demand signal)
- type: prometheus
metricType: AverageValue
metadata:
serverAddress: http://kube-prometheus-stack-prometheus.monitoring.svc:9090
query: sum(vllm:num_requests_waiting) or vector(0)
threshold: "25" # ← k6 실측 후 조정
activationThreshold: "1"
# 2차: P95 latency (SLO guardrail)
- type: prometheus
metricType: AverageValue
metadata:
serverAddress: http://kube-prometheus-stack-prometheus.monitoring.svc:9090
query: histogram_quantile(0.95, sum(rate(vllm:e2e_request_latency_seconds_bucket[1m])) by (le)) or vector(0)
threshold: "5" # ← SLO 기준으로 조정
KEDA를 쓰는 이유: HPA 대비 Prometheus 쿼리 직접 연동, activation threshold(스파이크 무시) 지원.
노드 스케일: Karpenter가 Pending pod 발생 시 자동으로 GPU 노드 프로비저닝 → Pod 스케일 아웃 + GPU 노드 자동 증설의 2-tier 구조.
6. 알림 규칙 (Alert Rules)¶
| 알림 | 조건 | Severity | 의미 |
|---|---|---|---|
| VLLMHighQueueDepth | num_requests_waiting > 50 for 2분 |
⚠️ warning | 스케일링 지연 또는 부하 초과 |
| VLLMHighLatency | p95(e2e_latency) > SLO for 2분 |
🔴 critical | SLO 위반 |
| VLLMGPUMemoryCritical | FB_USED/FB_TOTAL > 95% for 1분 |
🔴 critical | OOM 임박 |
| VLLMKVCacheFull | gpu_cache_usage_perc > 95% for 30초 |
🔴 critical | Preemption/OOM 임박 |
| VLLMPreemptionSpike | rate(preemptions[5m]) > 5 |
⚠️ warning | 메모리 부족으로 요청 중단 |
7. 프로덕션 더블체크 포인트¶
배포 후 시스템 오픈 전 점검:
| # | 항목 | 확인 |
|---|---|---|
| 1 | vLLM /metrics 엔드포인트 노출 확인 |
|
| 2 | ServiceMonitor → Prometheus 스크래핑 확인 | |
| 3 | DCGM Exporter DaemonSet 동작 확인 | |
| 4 | Grafana 대시보드 구성 (Tier ½/3 지표) | |
| 5 | Alert Rule 등록 (5개) | |
| 6 | KEDA 설치 & ScaledObject 적용 | |
| 7 | Karpenter do-not-disrupt annotation 적용 |
|
| 8 | k6 부하테스트 — saturation point 확인 | |
| 9 | 워밍업 스크립트 (100회) 배포 후 자동 실행 | |
| 10 | gpu_memory_utilization / max_model_len 최적화 |
|
| 11 | Scale Out → Scale In 사이클 동작 검증 |
자주 놓치는 항목¶
!!! tip "KV Cache 설정" gpu_memory_utilization=0.85 권장 (기본 0.9 — 너무 높으면 OOM). vllm:gpu_cache_usage_perc > 80% 알람 설정 필수.
!!! tip "Karpenter Consolidation 보호" yaml # vLLM Pod에 annotation 추가 (Deployment template) metadata: annotations: karpenter.sh/do-not-disrupt: "true"없으면 Karpenter가 node consolidation 하면서 서빙 중단 가능.
!!! tip "GPU 워밍업" 첫 요청은 CUDA graph 캡처, 커널 컴파일로 수배 느림. 배포 후 헬스체크 전 워밍업 요청 100회 선처리 권장. ReadinessProbe를 워밍업 완료 후에 통과하도록 설정.
!!! tip "max_model_len 검토" 기본값(모델 지원 최대) → KV Cache 메모리 과다 점유 가능. 실제 사용하는 최대 컨텍스트 길이로 조정 (예: 65,536) → 동시 처리량 증가.
8. 참고 리소스¶
| 리소스 | 설명 |
|---|---|
| AWS EKS: Identify scaling metric thresholds | k6 부하테스트 → 임계값 도출 → KEDA 설정 (end-to-end) |
| AWS EKS: Autoscale AI inference with HPA and KEDA | KEDA ScaledObject 상세 설정 |
| EKS Best Practices: AI/ML | 전체 아키텍처 베스트 프랙티스 |
| Workshop: Generative AI on EKS (NVIDIA GPU) | vLLM 배포 + ServiceMonitor + Grafana 대시보드 실습 |
| Workshop: Scaling LLM Inference with vLLM | vLLM 대시보드 JSON 직접 제공, Prometheus 쿼리 포함 |
| awslabs/ai-ml-observability-reference-architecture | Prometheus + Grafana + OpenSearch 통합 RA |
| awslabs/ai-on-eks | AI on EKS 전체 리소스 |