vLLM 완벽 가이드 — PagedAttention으로 LLM 추론 처리량 24배, GPU 비용 절감
이 글의 핵심
vLLM은 UC Berkeley에서 시작된 오픈소스 LLM 추론 서버로, PagedAttention·Continuous Batching 기술로 동일 GPU에서 HuggingFace TGI 대비 최대 24배 높은 처리량을 보여줍니다. OpenAI 호환 API를 제공해 ChatGPT API 클라이언트를 그대로 self-hosting으로 전환할 수 있고 Llama·Qwen·DeepSeek·Mistral·Gemma 등 거의 모든 주요 오픈 모델을 지원합니다. 이 글은 배포·성능 튜닝·양자화·K8s 프로덕션 운영까지 체계적으로 다룹니다.
이 글의 핵심
vLLM은 2023년 UC Berkeley 연구에서 출발해 현재 Linux Foundation AI & Data의 사실상 표준 LLM 추론 서버입니다. 핵심 기여:
- PagedAttention: KV cache를 OS 페이지처럼 블록 단위로 관리 → 메모리 단편화 제거
- Continuous Batching: 요청 단위가 아닌 토큰 단위로 배치 → GPU utilization 극대화
- Prefix Caching: 동일한 시스템 프롬프트를 여러 요청이 공유
- Speculative Decoding: 작은 draft 모델로 후보 생성 후 대형 모델이 검증
- Chunked Prefill: 긴 프롬프트를 토큰 단위로 쪼개 latency/throughput 균형
결과: 동일 GPU에서 HuggingFace TGI 대비 2-24배 처리량, OpenAI API 호환성까지.
설치
Docker 한 줄
docker run --runtime nvidia --gpus all \
-p 8000:8000 --ipc=host \
-v $HOME/.cache/huggingface:/root/.cache/huggingface \
-e HUGGING_FACE_HUB_TOKEN=hf_xxx \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct \
--max-model-len 8192
pip 설치
# CUDA 12.1 환경
pip install vllm
# OpenAI 호환 서버 실행
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--max-model-len 8192 \
--gpu-memory-utilization 0.9
클라이언트 호출 (OpenAI SDK)
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="none", # vLLM은 기본 auth 없음, 필요하면 --api-key 플래그
)
resp = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "vLLM을 한 문장으로 설명해줘"},
],
temperature=0.7,
max_tokens=256,
)
print(resp.choices[0].message.content)
LangChain·LlamaIndex·LiteLLM 클라이언트 모두 base URL만 바꾸면 동작합니다.
처리량을 좌우하는 핵심 옵션
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--max-model-len 16384 \
--max-num-seqs 256 \
--gpu-memory-utilization 0.92 \
--enable-prefix-caching \
--enable-chunked-prefill \
--quantization awq \
--dtype half
| 옵션 | 의미 | 권장 |
|---|---|---|
--tensor-parallel-size | 모델을 N GPU에 분할 | 2의 거듭제곱 (2, 4, 8) |
--max-model-len | 최대 context 길이 | 실제 필요한 만큼 (크면 KV cache 소모) |
--max-num-seqs | 동시 처리 sequence 수 | 64-256 (KV cache 여유에 따라) |
--gpu-memory-utilization | 단일 GPU 메모리 사용 비율 | 0.9 (위험하면 0.85) |
--enable-prefix-caching | 시스템 프롬프트 재사용 | 대화형 API에 필수 |
--enable-chunked-prefill | 긴 prompt를 조각 처리 | 긴 컨텍스트 워크로드 |
--quantization | awq/gptq/fp8 | 메모리 1/2-1/4 |
--dtype | half(fp16)/bfloat16 | Ampere+ 는 bfloat16 |
PagedAttention 간단 이해
전통 KV cache는 “한 요청당 연속 메모리”를 잡아 단편화로 GPU가 놀게 됩니다.
[seq1 길이 500][ 빈 공간 3500 ][seq2 1800][ 빈 공간 2200 ]
PagedAttention은 block(기본 16 토큰) 단위로 매핑해:
[B1][B2][B3] ←→ [B8][B9]
[B4][B5] [B10][B11][B12]
...
KV 블록 풀에서 필요한 만큼만 할당 → 단편화 거의 0, 메모리 활용 2-3배 증가.
Continuous Batching
기존 static batching은 배치 내 모든 요청이 같은 토큰 수를 생성할 때까지 기다립니다. Continuous batching은 매 step마다 배치를 재구성해 짧은 요청이 끝나면 즉시 새 요청을 투입합니다. GPU가 놀지 않게 되어 처리량이 극적으로 오릅니다.
양자화 비교
| 형식 | 메모리 | 품질 손실 | 추천 시나리오 |
|---|---|---|---|
| FP16/BF16 | 100% | 없음 | 정확도가 최우선 |
| AWQ 4-bit | ~28% | 매우 작음(1-3%) | 프로덕션 기본 선택 |
| GPTQ 4-bit | ~28% | 작음(2-4%) | AWQ 없을 때 |
| FP8 (H100+) | ~50% | 거의 없음 | Hopper GPU |
| GGUF (llama.cpp) | ~25-50% | 모델별 | CPU+GPU 혼합 |
AWQ 모델은 HuggingFace의 TheBloke/*-AWQ, casperhansen/llama-3-8b-awq 등으로 쉽게 구할 수 있습니다.
Tool Calling (함수 호출)
tools = [{
"type": "function",
"function": {
"name": "search_issues",
"description": "GitHub 이슈 검색",
"parameters": {
"type": "object",
"properties": {"q": {"type": "string"}},
"required": ["q"]
}
}
}]
resp = client.chat.completions.create(
model="meta-llama/Llama-3.1-70B-Instruct",
messages=[{"role": "user", "content": "latest vllm issues"}],
tools=tools,
tool_choice="auto",
)
print(resp.choices[0].message.tool_calls)
vLLM은 모델별 native tool calling 파서를 내장해 Hermes/Llama-3.1/Mistral/Qwen 형식을 자동 인식합니다.
# 서버 시작 시
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--enable-auto-tool-choice \
--tool-call-parser llama3_json
Structured Output (JSON Schema 강제)
schema = {
"type": "object",
"properties": {
"answer": {"type": "string"},
"confidence": {"type": "number"}
},
"required": ["answer", "confidence"]
}
resp = client.chat.completions.create(
model="...",
messages=[...],
extra_body={"guided_json": schema},
)
vLLM은 outlines/xgrammar 기반 제약 디코딩으로 항상 유효한 JSON을 반환하도록 보장합니다. 프로덕션에서 후처리 파싱 실패를 거의 제거합니다.
Kubernetes 프로덕션 배포
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama70b
spec:
replicas: 2
selector: {matchLabels: {app: vllm-llama70b}}
template:
metadata:
labels: {app: vllm-llama70b}
spec:
nodeSelector:
accelerator: nvidia-h100
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- "--model=meta-llama/Llama-3.1-70B-Instruct"
- "--tensor-parallel-size=4"
- "--max-model-len=16384"
- "--max-num-seqs=128"
- "--enable-prefix-caching"
- "--gpu-memory-utilization=0.92"
- "--quantization=fp8"
ports: [{containerPort: 8000}]
env:
- name: HUGGING_FACE_HUB_TOKEN
valueFrom: {secretKeyRef: {name: hf, key: token}}
resources:
limits:
nvidia.com/gpu: 4
memory: 128Gi
volumeMounts:
- {name: hf-cache, mountPath: /root/.cache/huggingface}
- {name: shm, mountPath: /dev/shm}
readinessProbe:
httpGet: {path: /health, port: 8000}
initialDelaySeconds: 300
periodSeconds: 10
livenessProbe:
httpGet: {path: /health, port: 8000}
initialDelaySeconds: 600
periodSeconds: 30
volumes:
- name: hf-cache
persistentVolumeClaim: {claimName: hf-cache}
- name: shm
emptyDir: {medium: Memory, sizeLimit: 32Gi}
포인트
initialDelaySeconds크게 — 70B 모델 로딩 5-10분/dev/shm을 emptyDir memory로 32GB — Tensor Parallel용 NCCL 통신- HF 캐시는 PVC로 공유해 재시작 시 다운로드 생략
- 2 replica로 롤링 업데이트/장애 대응
Service / HPA
apiVersion: v1
kind: Service
metadata: {name: vllm-llama70b}
spec:
selector: {app: vllm-llama70b}
ports: [{port: 80, targetPort: 8000}]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: vllm-llama70b}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-llama70b
minReplicas: 2
maxReplicas: 8
metrics:
- type: Pods
pods:
metric:
name: vllm_num_requests_waiting
target: {type: AverageValue, averageValue: "10"}
vLLM의 커스텀 메트릭(vllm_num_requests_waiting)을 Prometheus Adapter로 연결하면 대기 큐 깊이 기반 오토스케일링이 가능합니다.
관측: Prometheus 메트릭
vLLM은 :8000/metrics에 풍부한 메트릭을 노출합니다.
| 메트릭 | 의미 |
|---|---|
vllm:num_requests_running | 처리 중 요청 수 |
vllm:num_requests_waiting | 대기 큐 |
vllm:gpu_cache_usage_perc | KV cache 사용률 |
vllm:time_to_first_token_seconds | TTFT p50/p95 |
vllm:time_per_output_token_seconds | TPOT |
vllm:e2e_request_latency_seconds | 전체 지연 |
vllm:prompt_tokens_total / vllm:generation_tokens_total | 토큰 카운터 |
Grafana 공식 대시보드로 바로 시각화 가능합니다.
프리픽스 캐시 활용
동일한 system prompt를 쓰는 챗봇/agent는 매 요청마다 시스템 프롬프트를 다시 prefill하는 낭비가 큽니다. --enable-prefix-caching은 KV cache 블록을 hash로 식별해 재사용합니다.
예: 1000 토큰 시스템 프롬프트를 100명이 동시 사용 시
- 캐시 없음: 100,000 토큰 prefill
- 프리픽스 캐시: 1,000 토큰 prefill + 99회 재사용 → 99% 절감
프롬프트 일부만 동일해도 토큰 접두사가 일치하는 구간까지 캐시됩니다.
Speculative Decoding
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--speculative-model meta-llama/Llama-3.2-1B \
--num-speculative-tokens 5 \
--tensor-parallel-size 4
작은 draft 모델이 5 토큰 후보를 생성하면 큰 모델이 한 forward pass로 검증·재사용해 단일 사용자 TPS 1.5-3배 향상.
비용 최적화 사례
시나리오: Llama 3.1 70B를 하루 100만 요청 서빙
- FP16: A100 80GB × 4 Pod × 3 replicas = 12 GPU, 월 ~$20,000
- FP8 + prefix cache + speculative: H100 × 4 Pod × 2 replicas = 8 GPU, 처리량 2.5배 ↑
- AWQ 4-bit: L40S × 2 Pod × 3 replicas = 6 GPU, 품질 99%, 비용 40% ↓
동일 트래픽에서 월 $8,000까지 절감 가능 — 프로덕션 경험 범위의 실제 수치입니다.
문제 해결
OOM: KV cache out of memory
--max-num-seqs낮추기--max-model-len줄이기 (실제 사용 길이에 맞게)--gpu-memory-utilization0.85 정도로- Chunked prefill 활성화
낮은 TPS
--enable-prefix-caching확인- Tensor parallel이 비대칭 — 2의 거듭제곱만
- 양자화 고려
Tool call 파싱 실패
- 모델별 파서: llama3_json, hermes, mistral, pythonic 등 선택
guided_json으로 schema 강제
느린 시작
- HF 캐시 PVC 준비 (콜드스타트 10분 → 1분)
- 모델을 S3·GCS에 mirror해 download 가속
NCCL 통신 오류 (TP > 1)
/dev/shm크기 32GB+- NCCL_DEBUG=INFO로 로그 확인
- GPU 간 P2P/NVLink 지원 확인
언제 vLLM이 아닌 다른 선택지
- CPU 서빙/엣지: llama.cpp, Ollama
- 매우 작은 모델 & 초저지연 요구: TensorRT-LLM
- 엔드투엔드 파인튜닝+서빙: Modal, Together, Anyscale
- Windows 네이티브: LM Studio, Ollama
- 관리형 서버리스: OpenRouter, Together AI API
체크리스트
- 모델 크기별 최소 GPU 요건 계산
-
--tensor-parallel-size는 2의 거듭제곱 -
--enable-prefix-caching항상 on -
--max-num-seqs·--max-model-len워크로드 맞춤 튜닝 - AWQ/FP8 양자화 검토
- HF 캐시를 PVC/공유 스토리지로
-
/dev/shmtmpfs 충분히 - Prometheus 메트릭 대시보드·알림
- 2+ replica + HPA로 HA 구성
- OpenAI SDK 호환 테스트 (base_url만 교체)
마무리
vLLM은 2026년 현재 오픈소스 LLM 서빙의 사실상 표준입니다. PagedAttention·Continuous Batching이라는 연구 혁신이 프로덕션 추론 비용을 몇 배 절감해주고, OpenAI API 호환으로 기존 클라이언트 마이그레이션이 거의 공짜입니다. Llama·Qwen·DeepSeek 같은 최신 오픈 모델을 self-hosting해 데이터 주권과 비용을 잡고 싶다면 vLLM이 현재 최선의 선택이며, 이 가이드의 튜닝 옵션·K8s 패턴만 적용해도 프로덕션 수준의 서빙이 가능합니다.
관련 글
- LLM 로컬 배포 완벽 가이드
- Ollama 완벽 가이드
- LiteLLM 완벽 가이드
- GPU Kubernetes 배포 가이드