Ollama vs vLLM 완전 비교 - 기능, 성능, 활용 가이드
Ollama와 vLLM의 아키텍처, 설치, 성능, API, 운영 환경별 활용법을 비교 정리합니다. 로컬 개발부터 프로덕션 서빙까지, 상황에 맞는 LLM 추론 엔진 선택 가이드를 제공합니다.
LLM을 자체 인프라에서 실행하려면 추론 엔진이 필요합니다. Ollama와 vLLM은 가장 대표적인 두 가지 선택지로, 각각 로컬 개발과 프로덕션 서빙에 최적화되어 있습니다. 이 글에서는 두 도구의 아키텍처, 성능, 사용법을 체계적으로 비교합니다.
1. Ollama와 vLLM 개요
Ollama란 무엇인가
Ollama는 로컬 환경에서 LLM을 간편하게 실행할 수 있도록 설계된 도구입니다. Docker처럼 ollama run llama3 한 줄이면 모델을 다운로드하고 즉시 대화할 수 있습니다.
핵심 철학: 복잡한 설정 없이, 누구나 로컬에서 LLM을 실행할 수 있어야 한다.
- 개발사: Ollama Inc.
- 라이선스: MIT
- 기반 기술: llama.cpp (GGML/GGUF)
- 지원 플랫폼: macOS, Linux, Windows
vLLM이란 무엇인가
vLLM은 대규모 프로덕션 환경에서 LLM을 고성능으로 서빙하기 위해 설계된 추론 엔진입니다. UC Berkeley에서 시작되어, PagedAttention이라는 혁신적인 메모리 관리 기법으로 높은 처리량(Throughput)을 달성합니다.
핵심 철학: 최소한의 GPU 자원으로, 최대한의 요청을 처리한다.
- 개발: UC Berkeley (현재 vLLM 프로젝트)
- 라이선스: Apache 2.0
- 기반 기술: PagedAttention, Continuous Batching
- 지원 플랫폼: Linux (CUDA GPU 필수)
한눈에 보는 비교 요약
| 비교 항목 | Ollama | vLLM |
|---|---|---|
| 주요 목적 | 로컬 개발, 프로토타입 | 프로덕션 서빙, 대규모 추론 |
| 설치 난이도 | 매우 쉬움 (원클릭) | 중간 (Python/CUDA 환경 필요) |
| 모델 포맷 | GGUF (양자화) | Hugging Face (FP16/BF16) |
| CPU 추론 | O (기본 지원) | X (GPU 필수) |
| GPU 활용 | 선택적 (CPU/GPU 혼합) | 필수 (CUDA 최적화) |
| 동시 요청 처리 | 제한적 | 매우 우수 (Continuous Batching) |
| 처리량 (Throughput) | 중간 | 매우 높음 |
| 메모리 효율 | 양자화로 절약 | PagedAttention으로 최적화 |
| OpenAI 호환 API | O | O |
| LoRA 핫스왑 | X | O |
| 텐서 병렬 (Multi-GPU) | 제한적 | O (완전 지원) |
| 멀티모달 | O (Vision 모델) | O (Vision 모델) |
| 적합 사용자 | 개발자, 연구자, 개인 | ML 엔지니어, 인프라 팀 |
2. 아키텍처와 핵심 기술
Ollama: llama.cpp 기반 로컬 추론 엔진
Ollama는 내부적으로 llama.cpp를 추론 백엔드로 사용합니다. llama.cpp는 C/C++로 작성된 경량 LLM 추론 라이브러리로, 양자화된 모델을 CPU에서도 효율적으로 실행할 수 있습니다.
[Ollama 아키텍처]
사용자 (CLI / API)
↓
┌─────────────────┐
│ Ollama 서버 │ ← Go로 작성된 서비스 레이어
│ (REST API) │
└────────┬────────┘
↓
┌─────────────────┐
│ llama.cpp │ ← C/C++ 추론 엔진
│ (GGUF 모델) │
└────────┬────────┘
↓
CPU / GPU (Metal, CUDA, ROCm)
주요 특징:
- GGUF 포맷: 양자화된 모델 파일 (Q4_K_M, Q5_K_M, Q8_0 등)
- CPU 최적화: AVX2, AVX-512, ARM NEON 활용
- GPU 오프로딩: 일부 레이어를 GPU에 올려 가속
- 메모리 매핑: mmap으로 디스크에서 직접 로드, RAM 절약
- 모델 관리: Docker Hub처럼 모델 풀(pull), 리스트(list), 삭제(rm)
vLLM: PagedAttention과 고성능 서빙 엔진
vLLM의 핵심 혁신은 PagedAttention입니다. 운영체제의 가상 메모리 페이징에서 영감을 받아, KV 캐시를 고정 크기 블록으로 나누어 관리합니다.
[vLLM 아키텍처]
사용자 (HTTP API)
↓
┌──────────────────────┐
│ OpenAI 호환 API 서버 │ ← FastAPI 기반
└──────────┬───────────┘
↓
┌──────────────────────┐
│ vLLM Engine │
│ ┌────────────────┐ │
│ │ Scheduler │ │ ← Continuous Batching
│ │ (스케줄러) │ │
│ └───────┬────────┘ │
│ ┌───────┴────────┐ │
│ │ PagedAttention │ │ ← KV Cache 블록 관리
│ │ (블록 매니저) │ │
│ └───────┬────────┘ │
│ ┌───────┴────────┐ │
│ │ Model Executor │ │ ← GPU 커널 실행
│ └────────────────┘ │
└──────────────────────┘
↓
CUDA GPU (필수)
PagedAttention의 핵심:
기존 LLM 서빙에서는 각 요청마다 최대 시퀀스 길이만큼 KV 캐시 메모리를 사전 할당합니다. 이로 인해 실제 사용량과 관계없이 GPU 메모리가 낭비됩니다.
[기존 방식: 연속 메모리 할당]
요청 1: [████████░░░░░░░░] ← 절반만 사용, 나머지 낭비
요청 2: [██████░░░░░░░░░░] ← 1/3만 사용, 나머지 낭비
요청 3: [메모리 부족 — 대기]
[vLLM PagedAttention: 블록 단위 할당]
요청 1: [██][██][██][██] ← 필요한 만큼만 블록 할당
요청 2: [██][██][██] ← 필요한 만큼만 블록 할당
요청 3: [██][██] ← 남은 블록 활용 가능!
- 메모리 낭비 감소: 기존 대비 60~80% 메모리 절약
- 동시 처리량 증가: 같은 GPU로 2~4배 더 많은 요청 처리
- Continuous Batching: 요청이 완료되면 즉시 새 요청을 배치에 추가
내부 동작 방식 비교
| 구분 | Ollama (llama.cpp) | vLLM |
|---|---|---|
| 언어 | C/C++ + Go (서비스) | Python + C++/CUDA (커널) |
| 메모리 관리 | mmap + 양자화 | PagedAttention |
| 배칭 | 단순 (요청 순차 처리 중심) | Continuous Batching |
| KV Cache | 정적 할당 | 동적 블록 할당 |
| 양자화 | GGUF (Q4, Q5, Q8 등) | AWQ, GPTQ, FP8 |
| 컴퓨트 백엔드 | CPU (기본) + GPU 오프로딩 | CUDA GPU (필수) |
3. 설치와 기본 사용법
Ollama 설치 및 모델 실행
설치 (원클릭):
# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh
# Windows
# ollama.com에서 설치 파일 다운로드
# 설치 확인
ollama --version모델 실행:
# 모델 다운로드 및 실행 (자동으로 pull)
ollama run llama3.1
# 특정 크기의 모델 실행
ollama run llama3.1:8b
ollama run llama3.1:70b
# 대화 시작
>>> 안녕하세요, Spark에서 데이터 스큐를 해결하는 방법을 알려주세요.모델 관리:
# 사용 가능한 모델 목록
ollama list
# 모델 다운로드만 (실행 없이)
ollama pull llama3.1:8b
# 모델 삭제
ollama rm llama3.1:8b
# 모델 정보 확인
ollama show llama3.1:8bAPI 서버 (자동 시작됨):
# Ollama 설치 시 백그라운드 서버 자동 실행 (포트 11434)
curl http://localhost:11434/api/generate -d '{
"model": "llama3.1",
"prompt": "Kubernetes의 장점을 3가지 알려주세요.",
"stream": false
}'vLLM 설치 및 API 서버 실행
설치 (Python + CUDA 필요):
# 기본 설치
pip install vllm
# 특정 CUDA 버전에 맞춰 설치
pip install vllm --extra-index-url https://download.pytorch.org/whl/cu121API 서버 실행:
# OpenAI 호환 API 서버 시작
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 4096 \
--gpu-memory-utilization 0.9API 호출:
# OpenAI 호환 API로 호출
curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "Kubernetes의 장점을 3가지 알려주세요."}
],
"temperature": 0.7,
"max_tokens": 512
}'Python에서 직접 사용:
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
gpu_memory_utilization=0.9,
max_model_len=4096
)
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512
)
prompts = [
"Spark에서 데이터 스큐를 해결하는 방법은?",
"Kafka 컨슈머 랙이 증가하는 원인은?",
"Airflow DAG 설계 모범 사례를 알려주세요."
]
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(f"Prompt: {output.prompt[:50]}...")
print(f"Response: {output.outputs[0].text[:200]}...")
print("---")Docker 기반 배포
Ollama Docker:
# GPU 사용
docker run -d --gpus all \
-v ollama:/root/.ollama \
-p 11434:11434 \
--name ollama \
ollama/ollama
# 모델 실행
docker exec -it ollama ollama run llama3.1:8b
# CPU 전용 (GPU 없이)
docker run -d \
-v ollama:/root/.ollama \
-p 11434:11434 \
--name ollama \
ollama/ollamavLLM Docker:
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct \
--max-model-len 4096비교:
| 항목 | Ollama Docker | vLLM Docker |
|---|---|---|
| GPU 필수 | X (CPU 가능) | O |
| 이미지 크기 | ~1.5 GB | ~8 GB |
| 모델 관리 | 내장 (ollama pull) | 외부 (Hugging Face 캐시) |
| 설정 복잡도 | 낮음 | 중간 |
4. 지원 모델과 포맷
Ollama: GGUF 포맷과 Modelfile
Ollama는 GGUF(GPT-Generated Unified Format) 포맷의 양자화 모델을 사용합니다.
사용 가능한 양자화 레벨:
| 양자화 | 비트 | 모델 크기 (7B) | 품질 | 속도 |
|---|---|---|---|---|
| Q2_K | 2비트 | ~2.7 GB | 낮음 | 매우 빠름 |
| Q4_K_M | 4비트 | ~4.1 GB | 좋음 | 빠름 |
| Q5_K_M | 5비트 | ~4.8 GB | 매우 좋음 | 보통 |
| Q6_K | 6비트 | ~5.5 GB | 우수 | 보통 |
| Q8_0 | 8비트 | ~7.2 GB | 최우수 | 느림 |
| FP16 | 16비트 | ~14.0 GB | 원본 | 가장 느림 |
참고: 일반적으로 Q4_K_M이 품질과 크기의 최적 균형점으로 권장됩니다. Q5_K_M 이상은 품질 차이가 미미한 반면 메모리 사용량이 크게 증가합니다.
Modelfile (커스텀 모델 정의):
# Modelfile: 커스텀 모델 생성
FROM llama3.1:8b
# 시스템 프롬프트 설정
SYSTEM """당신은 Data Dynamics의 빅데이터 엔지니어링 전문가입니다.
Apache Spark, Kafka, NiFi, Kudu 등의 기술에 대해 정확하고 실용적인 답변을 제공합니다.
코드 예시를 포함해주세요."""
# 파라미터 설정
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
PARAMETER stop "<|eot_id|>"
# 템플릿 (선택)
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}
<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>
<|start_header_id|>assistant<|end_header_id|>
"""# 커스텀 모델 생성 및 실행
ollama create dd-engineer -f Modelfile
ollama run dd-engineervLLM: Hugging Face 모델과 LoRA 어댑터
vLLM은 Hugging Face 포맷 (safetensors, PyTorch)의 모델을 직접 로드합니다.
# Hugging Face 모델을 직접 지정하여 실행
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct
# 양자화 모델 사용 (AWQ)
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Llama-3.1-8B-Instruct-AWQ \
--quantization awq
# GPTQ 양자화 모델
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Llama-3.1-8B-Instruct-GPTQ \
--quantization gptqLoRA 어댑터 로드:
# LoRA 어댑터를 동적으로 로드
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--lora-modules \
finance-adapter=./adapters/finance \
legal-adapter=./adapters/legal \
--max-lora-rank 16# API에서 LoRA 어댑터 지정하여 호출
import openai
client = openai.OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
# 금융 도메인 어댑터 사용
response = client.chat.completions.create(
model="finance-adapter", # LoRA 어댑터 이름
messages=[{"role": "user", "content": "PER 밸류에이션 분석 방법은?"}]
)
# 법률 도메인 어댑터 사용
response = client.chat.completions.create(
model="legal-adapter", # 다른 LoRA 어댑터
messages=[{"role": "user", "content": "계약 해지 조건 검토해주세요."}]
)지원 모델 범위 비교
| 모델 | Ollama | vLLM |
|---|---|---|
| LLaMA 3 / 3.1 | O | O |
| Mistral / Mixtral | O | O |
| Gemma / Gemma 2 | O | O |
| Qwen 2 / 2.5 | O | O |
| Phi-3 / Phi-4 | O | O |
| CodeLlama | O | O |
| DeepSeek-V2 | O | O |
| Command R+ | O | O |
| 커스텀 Fine-Tuned | GGUF 변환 필요 | Hugging Face 포맷 직접 로드 |
| LoRA 어댑터 핫스왑 | X | O |
| Vision 모델 (멀티모달) | O (LLaVA 등) | O (LLaVA 등) |
5. 성능 비교
추론 속도 (Throughput, Latency)
단일 요청 성능 (LLaMA 3.1 8B 기준):
| 지표 | Ollama (Q4_K_M, GPU) | Ollama (Q4_K_M, CPU) | vLLM (FP16, GPU) |
|---|---|---|---|
| TTFT (첫 토큰 시간) | ~100ms | ~500ms | ~80ms |
| 토큰 생성 속도 | ~60 tok/s | ~15 tok/s | ~80 tok/s |
| 메모리 사용 | ~5 GB | ~5 GB (RAM) | ~17 GB |
참고: 단일 요청에서 Ollama와 vLLM의 속도 차이는 크지 않습니다. vLLM의 진정한 강점은 동시 요청 처리에서 나타납니다.
동시 요청 처리 (Concurrency)
vLLM의 Continuous Batching이 동시 요청 처리에서 압도적인 성능 차이를 만듭니다.
[동시 요청 수에 따른 처리량 비교 (LLaMA 3.1 8B, A100 80GB)]
동시 요청 Ollama vLLM
1 ~60 tok/s ~80 tok/s
4 ~70 tok/s ~280 tok/s
8 ~75 tok/s ~500 tok/s
16 ~78 tok/s ~850 tok/s
32 ~80 tok/s ~1,200 tok/s
64 요청 대기 발생 ~1,500 tok/s
왜 이런 차이가 나는가?
| 구분 | Ollama | vLLM |
|---|---|---|
| 배칭 방식 | 요청별 순차 처리 (또는 제한적 병렬) | Continuous Batching (동적 배치) |
| KV Cache | 요청별 독립 할당 | PagedAttention으로 공유/재사용 |
| GPU 활용 | 단일 요청에 GPU 집중 | 여러 요청이 GPU 연산을 공유 |
GPU 메모리 활용 효율
| 시나리오 | Ollama | vLLM |
|---|---|---|
| 7B 모델 로드 | ~5 GB (Q4) | ~17 GB (FP16) |
| 7B 모델 로드 (양자화) | ~5 GB (Q4) | ~5 GB (AWQ) |
| 동시 10 요청 KV Cache | ~2 GB 추가 | ~1 GB 추가 (PagedAttention) |
| 동시 100 요청 KV Cache | 지원 어려움 | ~8 GB 추가 |
| 총 메모리 효율 | 모델 크기 절약에 강점 | 동시 처리 메모리 효율에 강점 |
배치 처리 성능
대량의 프롬프트를 한꺼번에 처리하는 오프라인 배치 추론 시나리오:
# vLLM 오프라인 배치 추론
from vllm import LLM, SamplingParams
llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct")
sampling_params = SamplingParams(temperature=0.0, max_tokens=256)
# 1,000개 프롬프트 배치 처리
prompts = [f"질문 {i}: ..." for i in range(1000)]
outputs = llm.generate(prompts, sampling_params)
# vLLM: ~2분 (A100), Ollama: ~15분 (같은 GPU)| 배치 크기 | Ollama (A100) | vLLM (A100) | vLLM 속도 향상 |
|---|---|---|---|
| 100건 | ~90초 | ~12초 | 7.5x |
| 1,000건 | ~900초 | ~120초 | 7.5x |
| 10,000건 | ~9,000초 | ~1,100초 | 8.2x |
6. API와 통합
OpenAI 호환 API
두 도구 모두 OpenAI API와 호환되는 엔드포인트를 제공하여, 기존 OpenAI SDK 코드를 수정 없이 사용할 수 있습니다.
Ollama OpenAI 호환 API:
from openai import OpenAI
# Ollama (포트 11434)
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # 아무 값이나 가능
)
response = client.chat.completions.create(
model="llama3.1",
messages=[{"role": "user", "content": "안녕하세요"}],
temperature=0.7
)
print(response.choices[0].message.content)vLLM OpenAI 호환 API:
from openai import OpenAI
# vLLM (포트 8000)
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="vllm" # 아무 값이나 가능
)
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "안녕하세요"}],
temperature=0.7
)
print(response.choices[0].message.content)API 엔드포인트 비교:
| 엔드포인트 | Ollama | vLLM |
|---|---|---|
/v1/chat/completions | O | O |
/v1/completions | O | O |
/v1/embeddings | O | O |
/v1/models | O | O |
/api/generate (네이티브) | O | X |
/api/chat (네이티브) | O | X |
| 스트리밍 (SSE) | O | O |
LangChain / LlamaIndex 연동
LangChain + Ollama:
from langchain_ollama import ChatOllama
llm = ChatOllama(
model="llama3.1",
base_url="http://localhost:11434",
temperature=0.7
)
response = llm.invoke("Spark에서 데이터 스큐를 해결하는 방법은?")
print(response.content)LangChain + vLLM:
from langchain_openai import ChatOpenAI
# vLLM은 OpenAI 호환이므로 ChatOpenAI 사용
llm = ChatOpenAI(
model="meta-llama/Llama-3.1-8B-Instruct",
base_url="http://localhost:8000/v1",
api_key="vllm",
temperature=0.7
)
response = llm.invoke("Spark에서 데이터 스큐를 해결하는 방법은?")
print(response.content)LlamaIndex 연동:
# Ollama
from llama_index.llms.ollama import Ollama
llm = Ollama(model="llama3.1", base_url="http://localhost:11434")
# vLLM (OpenAI 호환)
from llama_index.llms.openai_like import OpenAILike
llm = OpenAILike(
model="meta-llama/Llama-3.1-8B-Instruct",
api_base="http://localhost:8000/v1",
api_key="vllm"
)커스텀 클라이언트 구현
import requests
def query_ollama(prompt: str, model: str = "llama3.1"):
"""Ollama 네이티브 API 호출"""
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": model,
"prompt": prompt,
"stream": False,
"options": {
"temperature": 0.7,
"num_predict": 512
}
}
)
return response.json()["response"]
def query_vllm(prompt: str, model: str = "meta-llama/Llama-3.1-8B-Instruct"):
"""vLLM OpenAI 호환 API 호출"""
response = requests.post(
"http://localhost:8000/v1/chat/completions",
json={
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.7,
"max_tokens": 512
}
)
return response.json()["choices"][0]["message"]["content"]7. 운영 환경별 활용 가이드
로컬 개발 / 프로토타입 → Ollama
개발자가 로컬 머신에서 LLM을 실험하거나, 프로토타입을 빠르게 구축할 때 Ollama가 최적입니다.
적합한 시나리오:
- RAG 파이프라인 프로토타입 개발
- 프롬프트 엔지니어링 실험
- CI/CD 파이프라인에서 LLM 기반 테스트
- 오프라인 환경에서의 LLM 활용
- GPU 없는 머신에서 LLM 실행
# 개발 환경 세팅 (5분 이내)
ollama pull llama3.1:8b
ollama pull nomic-embed-text # 임베딩 모델
# RAG 프로토타입 개발 시작
python rag_prototype.py프로덕션 서빙 → vLLM
실제 서비스에서 다수의 사용자에게 LLM 응답을 제공할 때 vLLM이 최적입니다.
적합한 시나리오:
- 사내 AI 챗봇 서비스
- API 기반 LLM 서비스 제공
- 대량 배치 추론 (문서 요약, 분류 등)
- Fine-Tuned 모델 서빙
- 멀티 GPU 환경에서의 대규모 모델 서빙
# 프로덕션 배포 (텐서 병렬 + 자동 스케일링)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--max-num-seqs 256 \
--host 0.0.0.0 \
--port 8000엣지 / 온프레미스 배포
외부 네트워크 없이, 사내 인프라에서만 LLM을 운영해야 하는 경우:
| 환경 | 추천 도구 | 이유 |
|---|---|---|
| GPU 없는 서버 | Ollama | CPU 추론 지원, 양자화로 경량 실행 |
| 단일 GPU 서버 | Ollama 또는 vLLM | 동시 요청 적으면 Ollama, 많으면 vLLM |
| 멀티 GPU 서버 | vLLM | 텐서 병렬로 대형 모델 실행 |
| 데스크톱 / 노트북 | Ollama | 간편한 설치, macOS Metal 가속 |
| Kubernetes 클러스터 | vLLM | Horizontal scaling, 로드밸런싱 |
하이브리드 구성
Ollama와 vLLM을 함께 사용하는 전략:
[하이브리드 아키텍처]
개발자 로컬 머신 프로덕션 서버
┌──────────────────┐ ┌──────────────────────┐
│ Ollama │ │ vLLM │
│ (프로토타입) │ │ (프로덕션 서빙) │
│ llama3.1:8b Q4 │ │ LLaMA 3.1 70B FP16 │
│ 포트: 11434 │ │ 포트: 8000 │
└──────────────────┘ └──────────────────────┘
↓ ↓
개발/테스트 프로덕션 트래픽
프롬프트 실험 사용자 서비스
RAG 프로토타입 배치 추론
동일 코드, 다른 백엔드:
import os
from openai import OpenAI
# 환경 변수로 백엔드 전환
if os.getenv("ENV") == "production":
client = OpenAI(base_url="http://vllm-server:8000/v1", api_key="vllm")
model = "meta-llama/Llama-3.1-70B-Instruct"
else:
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
model = "llama3.1:8b"
# 동일한 API 코드
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "안녕하세요"}]
)8. 고급 기능
Ollama: 멀티모달, 커스텀 모델, 임베딩
멀티모달 (Vision):
# Vision 모델 실행
ollama run llava
# 이미지와 함께 질의
>>> [이미지 경로] 이 이미지에 무엇이 보이나요?# API로 멀티모달 호출
import ollama
import base64
with open("chart.png", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
response = ollama.chat(
model="llava",
messages=[{
"role": "user",
"content": "이 차트를 분석해주세요.",
"images": [image_data]
}]
)임베딩 생성:
import ollama
# 임베딩 생성 (RAG용)
response = ollama.embeddings(
model="nomic-embed-text",
prompt="Apache Spark는 분산 데이터 처리 프레임워크입니다."
)
embedding = response["embedding"] # 768차원 벡터커스텀 GGUF 모델 가져오기:
# Modelfile: 외부 GGUF 파일 사용
FROM ./my-custom-model-Q4_K_M.gguf
SYSTEM "당신은 전문 어시스턴트입니다."
PARAMETER temperature 0.7ollama create my-model -f Modelfile
ollama run my-modelvLLM: 텐서 병렬, 추측적 디코딩, 구조화 출력
텐서 병렬 (Multi-GPU):
# 4개 GPU로 70B 모델 실행
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4
# 8개 GPU로 405B 모델 실행
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-405B-Instruct \
--tensor-parallel-size 8 \
--pipeline-parallel-size 1추측적 디코딩 (Speculative Decoding):
작은 Draft 모델로 여러 토큰을 미리 생성하고, 큰 모델로 검증하여 추론 속도를 높이는 기법입니다.
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-70B-Instruct \
--speculative-model meta-llama/Llama-3.1-8B-Instruct \
--num-speculative-tokens 5[일반 추론]
토큰 1 → 토큰 2 → 토큰 3 → 토큰 4 → 토큰 5 (5 스텝)
[추측적 디코딩]
Draft(8B): 토큰 1,2,3,4,5 한꺼번에 생성
Target(70B): 5개 토큰을 한 번에 검증 → 3개 수락 (1~2 스텝)
→ 약 2~3배 속도 향상
구조화 출력 (Structured Output):
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="vllm")
# JSON Schema를 강제하여 구조화된 응답 생성
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "서울의 날씨를 알려주세요."}],
extra_body={
"guided_json": {
"type": "object",
"properties": {
"city": {"type": "string"},
"temperature": {"type": "number"},
"condition": {"type": "string", "enum": ["맑음", "흐림", "비", "눈"]},
"humidity": {"type": "number"}
},
"required": ["city", "temperature", "condition"]
}
}
)
# 반드시 지정된 JSON 스키마 형태로 응답LoRA 어댑터 핫스왑
vLLM의 LoRA 핫스왑은 하나의 기반 모델 위에 여러 Fine-Tuned 어댑터를 동시에 서빙할 수 있게 합니다.
# 여러 LoRA 어댑터를 동시에 로드
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--lora-modules \
finance=./lora/finance \
legal=./lora/legal \
medical=./lora/medical \
--max-lora-rank 32 \
--max-num-seqs 256[LoRA 핫스왑 아키텍처]
┌─ finance 어댑터 ─→ 금융 도메인 응답
기반 모델 (8B) ────┼─ legal 어댑터 ───→ 법률 도메인 응답
└─ medical 어댑터 ─→ 의료 도메인 응답
→ 기반 모델 1개 + LoRA 어댑터 N개 = GPU 메모리 절약
→ 요청별로 어댑터 선택 가능 (model 파라미터로 지정)
Ollama vs vLLM 고급 기능 비교:
| 기능 | Ollama | vLLM |
|---|---|---|
| 멀티모달 (Vision) | O | O |
| 임베딩 생성 | O | O |
| 텐서 병렬 (Multi-GPU) | 제한적 | O (완전 지원) |
| 파이프라인 병렬 | X | O |
| 추측적 디코딩 | X | O |
| 구조화 출력 (JSON) | X | O (guided_json) |
| LoRA 핫스왑 | X | O |
| 커스텀 모델 (Modelfile) | O | X (Hugging Face 포맷) |
| Prefix Caching | X | O |
| 양자화 방식 | GGUF (Q4~Q8) | AWQ, GPTQ, FP8, BitsAndBytes |
9. 선택 가이드: 어떤 상황에서 무엇을 쓸 것인가
의사결정 플로차트
[GPU가 있는가?]
├─ No → Ollama (CPU 추론)
└─ Yes
↓
[동시 요청이 10개 이상인가?]
├─ Yes → vLLM (Continuous Batching)
└─ No
↓
[프로덕션 서비스인가?]
├─ Yes
│ ↓
│ [LoRA 핫스왑 또는 구조화 출력이 필요한가?]
│ ├─ Yes → vLLM
│ └─ No
│ ↓
│ [SLA (응답 시간 보장)가 중요한가?]
│ ├─ Yes → vLLM
│ └─ No → Ollama도 가능
└─ No
↓
[빠른 실험 / 프로토타입인가?]
├─ Yes → Ollama
└─ No → 요구사항에 따라 선택
시나리오별 추천
| 시나리오 | 추천 | 이유 |
|---|---|---|
| "내 노트북에서 LLM 돌려보고 싶어" | Ollama | 원클릭 설치, CPU 가능 |
| "RAG 프로토타입 빠르게 만들고 싶어" | Ollama | 5분 내 세팅, LangChain 연동 간편 |
| "사내 AI 챗봇 서비스를 오픈할 거야" | vLLM | 동시 처리, 안정적 서빙 |
| "1만 건 문서를 배치로 요약할 거야" | vLLM | 배치 처리 성능 7~8배 빠름 |
| "Fine-Tuned 모델 3개를 동시에 서빙할 거야" | vLLM | LoRA 핫스왑으로 효율적 서빙 |
| "GPU 없이 온프레미스에서 LLM 운영할 거야" | Ollama | CPU 추론 지원 |
| "70B 모델을 멀티 GPU로 실행할 거야" | vLLM | 텐서 병렬 완전 지원 |
| "CI/CD에서 LLM 기반 테스트를 돌릴 거야" | Ollama | Docker 간편, 빠른 시작/종료 |
| "JSON 스키마에 맞는 응답이 필요해" | vLLM | guided_json으로 구조화 출력 보장 |
| "macOS에서 Apple Silicon으로 실행할 거야" | Ollama | Metal 가속 기본 지원 |
함께 사용하는 전략
실무에서는 Ollama와 vLLM을 단계별로 함께 사용하는 것이 가장 효과적입니다.
[개발 라이프사이클에서의 활용]
1단계: 실험 (Ollama)
- 모델 탐색: ollama run llama3.1, mistral, gemma2 비교
- 프롬프트 테스트: 최적 프롬프트 찾기
- RAG 프로토타입: 로컬에서 벡터 DB + LLM 연동
2단계: 개발 (Ollama)
- 애플리케이션 개발: OpenAI 호환 API로 통합
- 단위 테스트: CI/CD에서 Ollama Docker 활용
- Fine-Tuning 테스트: 결과 확인
3단계: 스테이징 (vLLM)
- 성능 테스트: 동시 요청 부하 테스트
- 모델 최적화: 양자화, 배치 크기 튜닝
- LoRA 어댑터 검증
4단계: 프로덕션 (vLLM)
- 서비스 배포: Kubernetes + vLLM
- 모니터링: Prometheus + Grafana
- 오토스케일링: 트래픽에 따른 자동 확장
참고: OpenAI 호환 API를 사용하면 Ollama에서 개발한 코드를 vLLM으로 전환할 때
base_url과model이름만 변경하면 됩니다. 코드 수정 없이 개발 환경과 프로덕션 환경을 전환할 수 있는 것이 두 도구를 함께 사용하는 가장 큰 이점입니다.
References
- Kwon, W. et al. (2023). "Efficient Memory Management for Large Language Model Serving with PagedAttention." SOSP
- Ollama Documentation — https://ollama.com
- vLLM Documentation — https://docs.vllm.ai
- llama.cpp GitHub — https://github.com/ggerganov/llama.cpp
- vLLM GitHub — https://github.com/vllm-project/vllm
- Leviathan, Y. et al. (2023). "Fast Inference from Transformers via Speculative Decoding." ICML
— Data Dynamics 엔지니어링 팀