vLLM
vLLM은 대규모 언어 모델(LLM) 추론/서빙을 위한 고성능 엔진입니다. 특히 PagedAttention과 Continuous Batching을 통해, 동일한 GPU 자원에서 더 높은 처리량을 제공합니다.
빠른 추론 속도
OpenAI 호환 API(
/v1/chat/completions) 지원멀티 GPU 텐서 병렬화 지원
Qwen 계열 모델 서빙에 적합
1왜 vLLM을 사용하는가?¶
LLM 서빙 성능은 크게 아래 요소에 의해 결정됩니다.
: 처리량
: 동시 요청 수
: 요청당 생성 토큰 수
: 평균 요청 지연 시간
즉, 처리량은 동시 요청과 생성 토큰 수가 커질수록 증가하고, 요청 지연 시간이 커질수록 감소합니다.
vLLM은 KV Cache를 효율적으로 관리해 요청 간 메모리 낭비를 줄이고, 배치 스케줄링을 최적화해 실제 서비스 처리량을 높입니다.
2Ollama vs vLLM¶
2.1주요 특성 비교¶
| 항목 | Ollama | vLLM |
|---|---|---|
| 설치 복잡도 | 매우 간단 (바이너리 설치) | 중간 (Python 환경 필요) |
| 사용 난이도 | 매우 쉬움 (ollama run) | 보통 (CLI/API 옵션 많음) |
| 동시 요청 처리 | 기본적 (병렬화 제한) | 우수 (최적화된 배칭) |
| 처리량 (대량 요청) | 낮음 | 높음 |
| 지연시간 | 저부하 낮음, 고부하 급증 | 부하 전반 안정 |
| GPU 최적화 | 제한적 | 우수 (PagedAttention) |
| OpenAI 호환 | 제한적 | 완전함 |
| 메모리 효율성 | 기본적 | 우수 |
| 프로덕션 준비도 | 낮음 | 높음 |
2.2활용 포인트¶
2.2.1Ollama 권장 사용 사례¶
빠른 프로토타이핑: 모델을 빠르게 로드하고 테스트
로컬 개인 사용: 단건 요청이 주요 패턴
낮은 러닝 커브: 설정 없이 바로 실행 가능
소규모 배포: 1~2개 요청/초 이하
예: 개인 개발, 중소 기업의 간단한 챗봇
2.2.2vLLM 권장 사용 사례¶
고성능 서빙: 많은 동시 사용자 처리
API 서버: OpenAI 호환 REST API 필요
배치 처리: 여러 요청을 효율적으로 처리
프로덕션 환경: 안정성과 성능이 중요
예: SaaS 서비스, 기업 내부 LLM 인프라, 고트래픽 서비스
2.3전환 가이드¶
Ollama에서 vLLM으로 전환하는 신호:
동시 사용자 수가 10명 이상
평균 응답 시간 개선 필요
OpenAI API 호환성 필수
GPU 메모리 최적화 필요
3환경 설정¶
Docker 기반 vLLM 환경을 설정합니다. CUDA 드라이버와 컨테이너 런타임만 갖춰져 있으면 됩니다.
최신 vLLM 이미지를 가져옵니다.
docker pull vllm/vllm-openai:latest4모델 서버¶
Docker를 이용해 Qwen3 모델을 vLLM으로 서빙합니다. 단일 GPU 기준 설정입니다.
아래 예시는 Qwen/Qwen3-8B 모델을 활용합니다.
# 단일 GPU 기준 Qwen3 모델을 vLLM 서버로 실행합니다.
docker run --gpus all \
-p 8000:8000 \
--ipc=host \
-v ~/.cache/huggingface:/root/.cache/huggingface \
vllm/vllm-openai:latest \
Qwen/Qwen3-8B \
--host 0.0.0.0 \
--port 8000 \
--dtype bfloat16 \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--seed 42--gpus all: 모든 GPU를 컨테이너에 노출합니다.--ipc=host: 공유 메모리를 호스트와 공유해 텐서 병렬화 성능을 유지합니다.-v ~/.cache/huggingface:/root/.cache/huggingface: 모델 캐시를 호스트에 유지해 재다운로드를 방지합니다.
위 명령을 compose.yaml로 관리하면 옵션을 파일로 유지하고 반복 실행이 편해집니다.
# compose.yaml
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus: all
command: >
Qwen/Qwen3-8B
--host 0.0.0.0
--port 8000
--dtype bfloat16
--max-model-len 8192
--gpu-memory-utilization 0.90
--seed 42# 서버 시작
docker compose up -d
# 로그 확인
docker compose logs -f vllm
# 서버 종료
docker compose down4.1실행 옵션¶
| 옵션 | 설명 |
|---|---|
| 모델 ID(위치 인자) | 모델 이름 또는 경로를 지정합니다. |
--host | API 서버 바인딩 주소입니다. |
--port | API 서버 포트입니다. 기본값은 8000입니다. |
--dtype | 가중치/활성화 정밀도를 지정합니다(auto, float16, bfloat16 등). |
--max-model-len | 최대 컨텍스트 길이(입력+출력)를 지정합니다. |
--gpu-memory-utilization | GPU 메모리 사용 비율(기본 0.9)입니다. |
--seed | 재현성을 위한 시드입니다(기본 0). |
--max-num-seqs | 한 iteration에서 처리할 최대 시퀀스 수입니다. |
--max-num-batched-tokens | 한 iteration에서 처리할 최대 토큰 수입니다. |
--swap-space | GPU당 CPU swap 공간(GiB)입니다. |
--tensor-parallel-size (-tp) | 텐서 병렬 그룹 수(멀티 GPU 핵심)입니다. |
4.2주요 응용 사례에 자주 쓰는 추가 옵션¶
응용 사례에 직접적인 옵션만 추렸습니다.
| 옵션 | 권장 시작값 | 주로 쓰는 상황 | 메모 |
|---|---|---|---|
--enable-chunked-prefill | 활성화 | 긴 프롬프트/RAG 입력이 많은 경우 | prefill을 나눠 지연시간 급증을 완화합니다. |
--async-scheduling | 활성화 | 동시 요청이 많은 온라인 서비스 | GPU 유휴 구간을 줄여 처리량 개선에 도움됩니다. |
--performance-mode | throughput 또는 balanced | 처리량 우선 vs 지연시간 우선 | interactivity는 저지연 중심입니다. |
--pipeline-parallel-size (-pp) | 1~2 | 단일 GPU 메모리로 큰 모델이 어려울 때 | TP와 함께 병렬 전략을 조합합니다. |
--data-parallel-size (-dp) | 2+ | 고QPS 수평 확장 | 복제된 엔진으로 요청 처리량을 높입니다. |
--api-server-count (-asc) | 1~4 | 프론트엔드 병목 완화 | 미지정 시 data_parallel_size를 따릅니다. |
--kv-cache-memory-bytes | 예: 10G | KV 캐시 메모리를 정밀 제어하고 싶을 때 | 설정 시 --gpu-memory-utilization보다 우선합니다. |
--cpu-offload-gb | 8~16 | GPU 메모리가 부족한 환경 | CPU-GPU 연결 대역폭이 중요합니다. |
--quantization (-q) | 모델/하드웨어별 상이 | 메모리 절감 및 더 큰 모델 서빙 | 정확도/속도 트레이드오프를 확인해야 합니다. |
--api-key | 운영 정책에 맞게 | 외부 노출 API 운영 | OpenAI 호환 API 보호에 필수적입니다. |
4.3옵션 조합 예시¶
다양한 배포 시나리오에 대한 실전 명령어들입니다.
4.3.1단일 GPU 동시 요청 최적화¶
동시 요청을 효율적으로 처리하기 위해 배치 크기와 메모리 활용 비율을 조정합니다.
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus: all
command: >
Qwen/Qwen3-8B
--host 0.0.0.0
--port 8000
--dtype bfloat16
--max-model-len 8192
--gpu-memory-utilization 0.92
--max-num-seqs 32
--max-num-batched-tokens 4096
--enable-chunked-prefill
--async-scheduling
--performance-mode throughput
--seed 424.3.22-GPU 텐서 병렬¶
두 개 GPU를 활용해 더 큰 모델이나 긴 컨텍스트를 처리합니다.
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus:
- count: 2
command: >
Qwen/Qwen3-8B
--tensor-parallel-size 2
--host 0.0.0.0
--port 8000
--dtype bfloat16
--max-model-len 8192
--gpu-memory-utilization 0.90
--max-num-seqs 64
--max-num-batched-tokens 8192
--performance-mode throughput
--seed 424.3.34-GPU 고동시성¶
텐서 병렬화와 데이터 병렬화를 결합하여 높은 처리량을 달성합니다.
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus: all
command: >
Qwen/Qwen3-8B
--tensor-parallel-size 2
--data-parallel-size 2
--api-server-count 2
--host 0.0.0.0
--port 8000
--dtype bfloat16
--max-model-len 8192
--gpu-memory-utilization 0.90
--max-num-seqs 128
--max-num-batched-tokens 16384
--enable-chunked-prefill
--async-scheduling
--performance-mode throughput4.3.4메모리 제약 환경¶
제한된 GPU 메모리에서 모델을 서빙하기 위해 CPU 오프로드와 캐시 최적화를 활용합니다.
services:
vllm:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus: all
command: >
Qwen/Qwen3-8B
--host 0.0.0.0
--port 8000
--dtype float16
--max-model-len 4096
--kv-cache-memory-bytes 10G
--cpu-offload-gb 8
--swap-space 165API 호출 테스트¶
실행 중인 vLLM 서버에 프롬프트를 보내고 응답을 받습니다. curl과 Python의 두 가지 방법을 소개합니다.
5.1curl로 호출¶
# OpenAI 호환 Chat Completions API 호출
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-8B",
"messages": [
{"role": "system", "content": "당신은 딥러닝 튜터입니다."},
{"role": "user", "content": "vLLM의 장점을 3가지로 설명해줘."}
],
"temperature": 0.0,
"max_tokens": 256
}'5.2Python으로 호출¶
OpenAI SDK를 이용해 vLLM과 상호작용합니다.
# OpenAI SDK를 vLLM 엔드포인트에 연결
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8000/v1",
api_key="EMPTY", # vLLM 로컬 서버는 임의 문자열 사용 가능
)
# 재현 가능성을 위해 temperature를 0으로 설정
response = client.chat.completions.create(
model="Qwen/Qwen3-8B",
messages=[
{"role": "system", "content": "당신은 딥러닝 튜터입니다."},
{"role": "user", "content": "Qwen3를 서비스할 때 주의점을 알려줘."},
],
temperature=0.0,
max_tokens=256,
)
print(response.choices[0].message.content)6성능 튜닝 포인트¶
GPU 메모리, 배치 크기, 정밀도 등을 최적화하여 처리량과 지연시간 사이의 균형을 맞웁니다.
| 항목 | 옵션 | 설명 |
|---|---|---|
| 최대 컨텍스트 길이 | --max-model-len | 길이를 줄이면 메모리 사용량 감소 |
| GPU 사용 비율 | --gpu-memory-utilization | OOM 방지를 위해 0.85~0.95 범위에서 조정 |
| 정밀도 | --dtype | bfloat16/float16로 메모리 절감 |
| 병렬화 | --tensor-parallel-size | 멀티 GPU 사용 시 처리량 향상 |
| 재현성 | --seed, temperature=0 | 결과 일관성 확보 |
멀티 GPU 예시:
# 2개 GPU를 사용해 Qwen3 대형 모델 서빙
docker run --gpus '"device=0,1"' \
-p 8000:8000 \
--ipc=host \
-v ~/.cache/huggingface:/root/.cache/huggingface \
vllm/vllm-openai:latest \
Qwen/Qwen3-32B \
--tensor-parallel-size 2 \
--host 0.0.0.0 \
--port 80007프롬프트 캐싱 (Prompt Caching)¶
vLLM은 자주 반복되는 대용량의 시스템 프롬프트, 도구 지시문, RAG 문서 블록을 매번 처음부터 다시 계산하지 않고 VRAM에 키-값 상태(KV Cache)로 캐싱하여 재사용하는 자동 접두사 캐싱(Automatic Prefix Caching, APC) 기술을 지원합니다.
7.1자동 접두사 캐싱의 작동 원리¶
블록 단위 캐싱: vLLM은 프롬프트를 일정한 크기의 블록(일반적으로 16 토큰 단위)으로 분할합니다.
접두사 매칭: 요청된 새 프롬프트의 시작 부분이 이전 요청들과 동일한 토큰 블록 구조를 가지고 있다면, 서빙 엔진은 이 블록의 KV 상태를 VRAM에서 직접 가져와 재사용합니다.
성능 효과: 긴 문서에 대한 연속적인 질문이나 멀티턴 대화 시, 프롬프트를 읽고 분석하는 프리필(Prefill) 단계를 생략하게 되므로 첫 토큰 생성 지연 시간(TTFT)이 비약적으로(최대 10배 이상) 단축되고 시스템의 전체 토큰 처리량(Throughput)이 대폭 상승합니다.
같은 ~1,000토큰 접두사를 반복 요청해 직접 재면, 첫 요청 845ms(프리필 포함)가 캐시 적중 후 123ms로 줄어 약 6.9배 빨라집니다(접두사가 길수록 효과가 커집니다).
7.2실행 방법¶
vLLM 구동 시 --enable-prefix-caching 옵션을 추가하면 이 기능이 즉시 활성화됩니다.
# 자동 접두사 캐싱 옵션을 활성화하여 vLLM 서버 시작
docker run --gpus all \
-p 8000:8000 \
--ipc=host \
-v ~/.cache/huggingface:/root/.cache/huggingface \
vllm/vllm-openai:latest \
Qwen/Qwen3-8B \
--host 0.0.0.0 \
--port 8000 \
--dtype bfloat16 \
--max-model-len 8192 \
--enable-prefix-caching또한, compose.yaml로 관리하는 경우 다음과 같이 명령어 필드(command) 마지막에 추가하여 간편하게 활성화할 수 있습니다.
command: >
Qwen/Qwen3-8B
--host 0.0.0.0
--port 8000
--dtype bfloat16
--max-model-len 8192
--gpu-memory-utilization 0.90
--enable-prefix-caching8서빙 병목 해소를 위한 KV Cache 및 디코딩 가속 혁신¶
대규모 온라인 서비스 서빙 환경에서 LLM 추론은 엄청난 메모리(VRAM) 병목과 토큰 생성 시간 지연에 직면합니다. vLLM 엔진과 최신 언어 모델들은 프롬프트 캐싱을 넘어 아키텍처 수준의 가속 기법들을 통합 적용하여 이를 극복하고 있습니다.
8.1MLA (Multi-Head Latent Attention): KV Cache 압축의 혁명¶
최신 고성능 모델(예: DeepSeek-V3 MoE)의 서빙 속도를 극적으로 배가시킨 핵심 기술이 바로 MLA(Multi-Head Latent Attention)입니다.
[일반 MHA (VRAM에 막대한 KV Cache 상주)]
Key Cache : ──────────────────────────► (Batch x Seq x Heads x Dim)
Value Cache : ──────────────────────────► (Batch x Seq x Heads x Dim)
[DeepSeek MLA (저차원 잠재 공간으로 압축하여 VRAM 절약)]
KV Latent Cache : ──► [Low-Rank 압축 (d_c)] ──► VRAM 상주 (메모리 85% 이상 절약)
│
[Up-Projection 복원] ──► Query와의 어텐션 연산 전개동작 원리: 기존의 Multi-Head Attention(MHA) 기법은 입력 문맥이 길어질수록 VRAM 내부에 상주해야 하는 Key-Value Cache 크기가 선형적으로 늘어나, 동시 배치 크기를 제약하는 치명적인 병목이 되었습니다. MLA는 이 문제를 해결하기 위해 Key와 Value 벡터들을 매우 작은 차원의 저랭크 잠재 공간(Latent Space) 로 압축하여 VRAM에 저장합니다. 이후 실제 Attention 연산을 전개하는 시점에만 빠르게 원래 차원으로 복원(Up-Projection)하여 내적을 수행합니다.
공학적 효과: vLLM의 서빙 메모리 상에서 KV Cache 점유 공간을 85% 이상 획기적으로 줄여주어, 동일 GPU 환경에서 동시 처리 가능한 최대 배치 크기(Throughput)를 수 배 이상 확장시키는 성과를 보여줍니다. 단, 이 효과는 MLA를 채택한 모델(DeepSeek 계열)에 한정되며, 이 장의 비교에 쓴 Qwen3처럼 일반적인 MHA/GQA를 쓰는 모델에는 해당하지 않습니다.
8.2추측성 디코딩 (Speculative Decoding)¶
토큰을 하나씩 자동회귀적으로 생성하는 LLM 디코딩 단계는 GPU 연산 능력 자체보다는 메모리 대역폭(Memory Bandwidth)에 병목이 걸려 속도가 느립니다. 추측성 디코딩(Speculative Decoding)은 이 병목을 풀기 위한 영리한 협업 디코딩 전략입니다.
┌───────────────────────────────────────┐
│ 1) Draft Model (경량 1B 모델) │
│ - 매우 빠르게 여러 토큰 후보군 생성 │
└──────────────────┬────────────────────┘
│
▼ [y_1, y_2, y_3, y_4 생성]
┌───────────────────────────────────────┐
│ 2) Target Model (대형 8B/70B 모델) │
│ - 한 번의 병렬 연산으로 후보군 검증 │
└──────────────────┬────────────────────┘
│
├─► y_1, y_2 수락! (속도 가속)
└─► y_3 거절! ──► Target 모델이 직접 올바른 토큰 생성드래프트 모델(Draft Model)의 고속 생성: 매우 가볍고 빠른 소형 언어 모델(예: 1B 규모)을 드래프트 모델로 삼아 다음 토큰 후보군 4~5개를 매우 빠르게 먼저 생성합니다.
타겟 모델(Target Model)의 병렬 검증: 훈련된 거대 타겟 모델(예: 70B 규모)은 한 번의 순방향 연산(Forward Pass)을 통해 드래프트 모델이 제안한 여러 토큰의 확률 분포를 **동시에 일괄 검증(Parallel Verification)**합니다.
결과 결합: 타겟 모델이 허용한 토큰들은 즉시 채택되고, 거부된 오답 토큰 시점부터는 타겟 모델이 올바른 확률로 다시 덮어씁니다.
이 기법을 적용하면, 성능은 100% 거대 타겟 모델 수준을 고스란히 유지하면서도 생성 속도(Tokens per second)를 최소 2배에서 3배 이상 폭증시키는 공학적 가치를 선사합니다.
다만 기본으로 켜져 있는 기능은 아닙니다 — vLLM에서는 드래프트 모델이나 n-gram 등을 지정하는 별도 설정(--speculative-config)으로 활성화하며, 적합한 작업/드래프트 조합에서 가속 효과가 큽니다.
9Ollama vs vLLM 동시 요청 처리 속도 비교¶
Ollama와 vLLM의 동시 요청 처리 성능을 실제로 측정하고 비교합니다. 동일한 Qwen3 4B 모델로 테스트합니다.
9.1비교 조건¶
| 항목 | 값 |
|---|---|
| 프롬프트 | 동일 문장 1개 |
| 생성 길이 | max_tokens=128 (num_predict=128) |
| 샘플링 | temperature=0.0 |
| 동시 요청 수 | 1, 4, 8, 16, 32, 64 |
| 측정 지표 | req/s, p95 latency(ms) |
9.2서버 실행¶
같은 GPU를 공유하므로 한 번에 하나씩 띄웁니다(동시 적재 시 VRAM 부족).
# Ollama — 컨텍스트를 제한해야 모델 전체가 GPU에 올라갑니다.
docker run -d --gpus all -p 11434:11434 \
-e OLLAMA_NUM_PARALLEL=8 -e OLLAMA_CONTEXT_LENGTH=2048 \
ollama/ollama:latest
docker exec <컨테이너> ollama pull qwen3:4b# vLLM — 네이티브 리눅스에서는 최신 이미지를 그대로 사용합니다(아래 측정도 이 구성).
# WSL2라면 UVA 오류 회피를 위해 latest 대신 v0.8.5로 고정하세요.
docker run --gpus all --ipc=host -p 8000:8000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
vllm/vllm-openai:latest \
Qwen/Qwen3-4B \
--dtype bfloat16 \
--max-model-len 2048 \
--gpu-memory-utilization 0.90 \
--seed 429.3벤치마크 스크립트¶
표준 라이브러리만으로 동작하는(추가 설치 불필요) 벤치마크 스크립트를 scripts/benchmark-serving.py로 제공합니다.
두 서버를 같은 채팅 형식으로 호출해 생성 경로를 정렬하고, 동시성을 올려가며 req/s·p50/p95·정규화 처리량을 출력합니다.
또한 평균 생성 토큰이 목표치에 못 미치면(조기 종료) 경고해, 길이 차이로 인한 가짜 처리량 차이를 거릅니다.
핵심은 동시성만큼의 스레드로 같은 요청을 한꺼번에 보내고, 배치가 끝나는 데 걸린 벽시계 시간으로 처리량을 재는 것입니다.
from concurrent.futures import ThreadPoolExecutor
def 스윕(설정, 동시성, 총요청):
with ThreadPoolExecutor(max_workers=동시성) as 풀:
시작 = time.perf_counter()
결과 = list(풀.map(한_요청, [설정] * 총요청)) # 동시성만큼 동시 실행, 나머지는 큐
경과 = time.perf_counter() - 시작
return 총요청 / 경과 # req/s실행(서버가 떠 있는 상태에서):
python3 scripts/benchmark-serving.py --target vllm --model Qwen/Qwen3-4B
python3 scripts/benchmark-serving.py --target ollama --model qwen3:4b9.4결과 기록 템플릿¶
server,concurrency,req_per_sec,p95_ms
Ollama,1,,
Ollama,4,,
Ollama,8,,
Ollama,16,,
Ollama,32,,
vLLM,1,,
vLLM,4,,
vLLM,8,,
vLLM,16,,
vLLM,32,,9.5측정 결과 (실측 예시)¶
같은 RTX 3090(네이티브 우분투 리눅스)에서 Qwen3-4B로 직접 측정한 결과입니다.
Ollama는 qwen3:4b(Q4_K_M, OLLAMA_NUM_PARALLEL=8, 컨텍스트 2048, 전 레이어 GPU 적재), vLLM은 Qwen/Qwen3-4B(bfloat16, 최신 이미지 v0.23.0)이며, 양쪽 모두 채팅 형식·max_tokens=128·temperature=0으로 64개 요청을 보냈습니다.
정밀도가 다르므로(Ollama Q4 vs vLLM bf16) 절대 수치보다 동시성에 따른 처리량 증가 곡선을 봅니다.
| 동시 요청 | Ollama req/s (정규화) | vLLM req/s (정규화) | Ollama p95 | vLLM p95 |
|---|---|---|---|---|
| 1 | 1.28 (1.0×) | 0.67 (1.0×) | 0.8s | 1.5s |
| 4 | 2.96 (2.3×) | 2.61 (3.9×) | 1.4s | 1.5s |
| 8 | 3.91 (3.1×) | 5.13 (7.7×) | 2.2s | 1.6s |
| 16 | 4.24 (3.3×) | 9.25 (13.8×) | 3.9s | 1.8s |
| 32 | 4.15 (3.2×) | 17.83 (26.6×) | 8.0s | 1.8s |
| 64 | 4.06 (3.2×) | 30.21 (45×) | 15.7s | 2.1s |
(정규화 = 각 엔진의 동시성 1 대비 처리량 배수. 절대 req/s는 정밀도가 달라 직접 비교하지 않습니다.)
세 가지가 드러납니다.
첫째, vLLM의 처리량은 동시성에 거의 선형으로 증가합니다(1 → 45배).
Continuous Batching이 동시 요청을 한 배치로 묶어 GPU를 가득 채우기 때문입니다.
반면 Ollama는 약 3배에서 포화합니다 — OLLAMA_NUM_PARALLEL=8이 동시 처리 상한이라 그 이상은 큐에 쌓입니다.
둘째, 지연시간의 성격이 다릅니다. 동시성 1에서는 오히려 Ollama가 더 빠른데(0.8s vs 1.5s), Q4 양자화가 가중치를 4분의 1로 줄여 메모리 대역폭 부담이 작기 때문입니다. 그러나 부하가 커지면 Ollama의 p95는 0.8초 → 15.7초로 폭증하는 반면, vLLM은 부하와 무관하게 약 2초로 일정합니다. '단일 요청이 빠른 것’과 '많은 요청에서도 안정적인 것’은 다른 문제이며, 후자가 vLLM의 강점입니다.
셋째, 부하가 커지면 절대 처리량도 역전됩니다. 동시성 4까지는 Ollama가 근소하게 앞서지만 동시성 8부터 vLLM이 추월하며, 동시성 32에서 vLLM은 2,283 tok/s로 Ollama(531 tok/s)의 약 4.3배인데, vLLM이 더 무거운 bfloat16임에도 그렇습니다.
이 확장은 공짜가 아니라 동시 처리 상한에 좌우됩니다.
같은 vLLM을 --max-num-seqs 4로 묶으면(한 번에 4개 시퀀스만), 처리량이 동시성 4 부근에서 약 4배에 포화하고 그 이상은 큐에 쌓여 p95가 1.6초 → 12.5초로 치솟습니다 — 기본값의 27배 확장과 대조적입니다.
즉 Ollama의 약 3배 포화도 본질적으로 OLLAMA_NUM_PARALLEL=8이라는 같은 종류의 상한 때문이며, vLLM의 우위는 상한을 크게 두고도 메모리·배칭을 견디는 능력에서 옵니다.
| 동시성 | vLLM 기본값 | vLLM --max-num-seqs 4 |
|---|---|---|
| 8 | 7.7× | 3.9× (p95 3.1s) |
| 32 | 26.6× | 3.9× (p95 12.5s) |
10자주 발생하는 문제¶
배포 과정에서 마주할 수 있는 주요 이슈와 해결 방법을 정리합니다.
CUDA/드라이버 오류
NVIDIA 드라이버, CUDA, PyTorch/vLLM 버전 호환성을 먼저 확인합니다.
OOM(Out of Memory)
--max-model-len축소작은 모델 사용
동시 요청 수 제한
응답 지연 증가
긴 입력 프롬프트 축소
배치 전략 및 GPU 수 조정
11다음 단계¶
vLLM 배포 이후 실전에서 활용할 수 있는 방향들을 소개합니다.
11.1배포 전 체크리스트¶
vLLM 서버 운영을 위한 사전 확인 사항입니다.
GPU 메모리 충분성 검증: 모델 용량과 최대 컨텍스트 길이(
--max-model-len)를 고려동시 요청 목표 설정: 예상 QPS에 따라
--max-num-seqs와--max-num-batched-tokens결정프롬프트 길이 정의: 평균 입력 길이와 생성 길이(
max_tokens) 기록CPU 및 호스트 메모리 여유: CPU 오프로드 옵션 필요 시 확보
API 보안 설정: 외부 노출 시
--api-key지정은 필수
11.2다중 모델 서빙¶
여러 버전의 Qwen3 모델을 동시에 서빙할 수 있습니다.
services:
vllm-8b:
image: vllm/vllm-openai:latest
ports:
- "8000:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus:
- count: 1
command: >
Qwen/Qwen3-8B
--host 0.0.0.0
--port 8000
vllm-32b:
image: vllm/vllm-openai:latest
ports:
- "8001:8000"
ipc: host
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
gpus:
- count: 2
command: >
Qwen/Qwen3-32B
--tensor-parallel-size 2
--host 0.0.0.0
--port 8000프론트엔드는 요청 특성에 따라 8B(빠름)와 32B(정확함) 중 선택합니다.
11.3RAG 통합¶
RAG 파이프라인과 연결하여 검색 증강 생성(RAG) 파이프라인을 구성합니다.
파이프라인 구조:
문서 저장소 → 벡터 임베딩 → 벡터 DB → 검색기 → 프롬프트 구성 → vLLM API → 최종 응답핵심 단계:
임베딩: 문서를 벡터로 변환 (예:
sentence-transformers)검색: 사용자 쿼리 관련 문서 상위 K개 반환
컨텍스트: 검색된 문서를 프롬프트에 포함
생성: vLLM 서버로 최종 응답 생성
11.4프로덕션 배포 팁¶
안정적인 운영을 위한 실전 노하우입니다.
로그 관리:
docker compose logs --follow또는 ELK 스택 연동헬스 체크: 별도 스크립트에서
/health엔드포인트 주기적 호출자동 재시작:
compose.yaml의 서비스에restart: always설정모니터링: Prometheus/Grafana로 GPU 메모리, 처리량, p95 지연시간 추적
버전 관리: 모델 변경 시 Docker 이미지
tag로 기록