vLLM의 Kubernetes 오토스케일링(Autoscaling)이 실패하는 이유 (그리고 대안)
요약
Kubernetes 환경에서 vLLM 배포 시 CPU/메모리 기반 HPA가 추론 부하를 제대로 반영하지 못하는 문제를 분석합니다. KEDA를 활용해 vLLM 전용 지표로 오토스케일링을 구현하고, 콜드 스타트 및 KV 캐시 관리 전략을 제시합니다.
핵심 포인트
- CPU/메모리 대신 vLLM의 대기 요청 수와 KV 캐시 사용률을 스케일링 지표로 사용해야 함
- KEDA를 활용하여 vLLM Prometheus 지표 기반의 오토스케일링 구현 권장
- 모델 가중치 사전 캐싱(PVC)과 startupProbe 설정을 통해 콜드 스타트 시간 단축
- OOM 방지를 위해 gpu-memory-utilization을 0.85 수준으로 여유 있게 설정
- 지연 시간 급증을 막기 위해 선점(Preemption) 발생률 모니터링 필수
Kubernetes에 vLLM을 배포하고 표준적인 CPU 기반 HPA(Horizontal Pod Autoscaler) 오토스케일링을 사용한다면, 테스트 시에는 괜찮아 보이지만 실제 트래픽이 발생하면 무너지는 시스템을 구축하게 될 것입니다.
그 이유와 대신 무엇을 해야 하는지 설명하겠습니다.
문제점: Kubernetes는 추론 부하(Inference Load)를 볼 수 없습니다
HPA는 기본적으로 CPU와 메모리를 기준으로 스케일링합니다. 하지만 이 두 가지는 LLM 추론(Inference)에 있어 무용지물인 신호입니다.
GPU가 작업을 수행하기 때문에 CPU 사용량은 낮게 유지됩니다. 요청을 전혀 처리하지 않는 vLLM Pod와 100개의 요청을 처리하는 Pod의 CPU 사용량은 거의 동일하게 나타납니다.
GPU 메모리는 vLLM이 시작 시 KV 캐시(KV cache)를 위해 미리 할당하기 때문에 일정하게 유지됩니다. 메모리 사용량이 변하지 않으므로 스케일링 결정을 트리거하지 못합니다.
따라서 과부하 상태에서도 Kubernetes가 보기에 vLLM 배포 상태는 유휴(Idle) 상태로 보이지만, 엔진 내부에는 요청이 쌓이고 지연 시간(Latency)은 치솟게 됩니다. 스케줄러는 아무런 문제가 발생했다는 사실을 전혀 알지 못합니다.
해결책: 실제 부하를 반영하는 지표로 오토스케일링하기
중요한 신호들은 vLLM 내부에 존재하며, vLLM의 Prometheus 엔드포인트를 통해 내보내집니다:
vllm:num_requests_waiting : 대기 중인 요청의 수.
vllm:gpu_cache_usage_perc : KV 캐시(KV cache)가 얼마나 차 있는지의 비율.
HPA가 아닌 KEDA에 이 지표들을 연결하세요:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
...
cooldownPeriod: 300 설정은 선택 사항이 아닙니다. vLLM의 콜드 스타트(Cold start)는 몇 분이 소요됩니다. 쿨다운(Cooldown) 기간이 짧으면 스케일 다운(Scale down) 후 즉시 다시 스케일 업(Scale up)이 반복되는 스래싱(Thrashing) 현상이 발생하여, 콜드 스타트 비용을 반복해서 지불하게 됩니다.
운영 환경에서 문제를 일으키는 세 가지 추가 사항
콜드 스타트 (Cold start). ReadOnlyMany PVC에 모델 가중치(Model weights)를 사전 캐싱(Pre-cache)하세요. 스케일 업(Scale-up) 시마다 HuggingFace에서 7B 모델을 다운로드하면 25분이 추가됩니다. 미리 채워진 PVC에서 마운트하면 이를 3060초로 단축할 수 있습니다. Kubernetes가 로딩 도중에 포드(Pod)를 종료하지 않도록 failureThreshold를 높게 설정한 startupProbe를 설정하세요.
KV 캐시 OOM (KV cache OOM). 가장 흔한 vLLM 충돌 원인입니다. 크기를 정할 때는 FP16 가중치를 위해 params_B × 2를 계산하고, 캐시를 위해 25%를 추가한 뒤, 여유 공간(Headroom)을 위해 --gpu-memory-utilization 0.85로 설정하세요. 0.95로 설정하면 동시 부하(Concurrent load) 상황에서 OOM이 발생할 것입니다.
선점 (Preemption). KV 캐시가 가득 차면, vLLM은 공간을 확보하기 위해 오래된 요청을 조용히 선점(Preempt)합니다. 이로 인해 P99 지연 시간(Latency)이 8배까지 급증할 수 있습니다. rate(vllm:num_preemptions_total[1m]) > 0.05가 30초 동안 지속되면 알람을 설정하세요. 이것이 얻을 수 있는 가장 빠른 경고입니다.
가장 중요한 지표
선점률(Preemption rate)을 모니터링하세요. 표준 Kubernetes 모니터링(CPU, 메모리, 재시작)은 추론(Inference) 상태에 대해 아무것도 알려주지 않습니다. 선점률은 사용자가 실제로 느끼는 지연 시간 급증보다 먼저 나타납니다. 단 하나의 vLLM 지표에 대해 알람을 설정해야 한다면, 바로 이 지표를 설정하세요.
GPU 메모리 크기 산정 규칙, 스팟(Spot) vs 온디맨드(On-Demand) 노드 전략, NVIDIA Dynamo Snapshot을 포함한 콜드 스타트 완화 방법, 모니터링해야 할 4가지 지표, 그리고 운영 체크리스트를 포함한 전체 버전은 저희 블로그에서 확인하실 수 있습니다: [https://thegoodshell.com/vllm-kubernetes/]
여러분은 어떤 신호를 기준으로 LLM 추론을 오토스케일링(Autoscaling)하고 계신가요? 큐 깊이(Queue depth) + KV 캐시보다 더 나은 방법을 찾으신 분이 있는지 궁금합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기