vLLM vs Ollama: chọn inference server nào cho production?
Dự án bắt đầu bằng một câu hỏi tưởng chừng đơn giản: "Chạy model local thì dùng gì?"
Team BKGlobal chúng tôi đã gặp đúng cái bẫy này khi triển khai một tính năng hỗ trợ tài liệu nội bộ dùng LLM. Giai đoạn demo thì mọi thứ chạy ngon với Ollama - cài xong là có ngay, gọi API thoải mái, mọi người đều thích. Nhưng khi đẩy lên staging với 20 user đồng thời thì latency nhảy từ 2 giây lên 45 giây. Một số request timeout hẳn.
Đó là lúc chúng tôi bắt đầu đào sâu vào sự khác biệt thực sự giữa vLLM và Ollama - không phải từ marketing, mà từ benchmark và kinh nghiệm xây hệ thống.
Bài này chúng tôi sẽ chia sẻ những gì team đã học được, bao gồm số liệu thực, khi nào nên dùng cái nào, và một bảng quyết định để anh em dev không phải đi lại vết xe đổ.
Ollama và vLLM - hai công cụ cho hai mục tiêu khác nhau
Trước khi so sánh, cần hiểu rõ triết lý thiết kế của từng tool.
Ollama được xây dựng với mục tiêu: "developer nào cũng có thể chạy LLM trong 5 phút". Nó đóng gói model management, runtime và API server vào một binary duy nhất, chạy tốt trên cả Apple Silicon, consumer GPU lẫn CPU-only. Cài xong gõ ollama run llama3 là có ngay. Không cần cấu hình CUDA, không cần setup Python environment.
vLLM được xây dựng với mục tiêu ngược lại: "phục vụ hàng trăm request đồng thời với latency thấp nhất có thể". Đây là project từ UC Berkeley, được thiết kế để chạy trên data center GPU (A100, H100) và đã được triển khai ở scale của các công ty lớn. Nó không đơn giản để setup, nhưng khi cần performance thật sự - không có đối thủ trong phân khúc open-source.
Cách dễ nhớ nhất: Ollama là "Docker for LLMs" - đơn giản, portable, developer-first. vLLM là "Nginx for LLMs" - production-grade, high-performance, cần configuration.
Kiến trúc bên dưới: tại sao vLLM nhanh hơn nhiều?
PagedAttention - bộ nhớ GPU được dùng như RAM ảo
Vấn đề cốt lõi của LLM inference là KV cache (key-value cache) - mỗi token được sinh ra cần lưu lại attention values của tất cả token trước đó. Với cách truyền thống, mỗi request được cấp phát một vùng memory liên tục và cố định, dẫn đến hai vấn đề:
- Memory bị reserved nhưng không dùng hết (internal fragmentation)
- Không thể chia sẻ KV cache giữa các request có prefix giống nhau
vLLM giải quyết bằng PagedAttention - chia KV cache thành các block nhỏ không liên tục, giống cách OS quản lý virtual memory. Kết quả: GPU memory utilization tăng 2–4x, có thể phục vụ 2–4 lần nhiều request hơn với cùng phần cứng.
Truyền thống:
Request A: [████████████░░░░░░░░░░░] ← 30% bị lãng phí
Request B: [██████░░░░░░░░░░░░░░░░░] ← 50% bị lãng phí
PagedAttention (vLLM):
Request A: [Block1][Block2][Block3] ← chỉ dùng đúng phần cần
Request B: [Block4][Block5] ← block được tái sử dụng
Continuous batching - không để GPU ngồi chờ
Với batch processing truyền thống, server chờ hết batch hiện tại mới nhận batch mới. Nếu một request cần 1000 token mà request khác chỉ cần 10, GPU phải ngồi không trong khi chờ request dài kết thúc.
vLLM dùng continuous batching: ngay khi một request kết thúc, slot đó được điền bằng request mới - không phải đợi hết batch. Scheduler hoạt động ở token level, không phải request level. Điều này giúp GPU luôn được tận dụng tối đa.
Ollama không có continuous batching. Các request được xử lý qua FIFO queue, với tối đa ~4 request đồng thời theo mặc định. Với 10+ concurrent users, GPU sẽ xử lý lần lượt - throughput tổng hầu như không tăng dù thêm user.
Benchmark thực tế: số liệu không nói dối
Dưới đây là dữ liệu từ các benchmark công khai (Red Hat, SitePoint, Spheron) với Llama 3.1 8B trên NVIDIA GPU:
Throughput (tokens/giây) theo số user đồng thời
| Concurrent users | vLLM (tok/s) | Ollama (tok/s) | Chênh lệch |
|---|---|---|---|
| 1 | ~85 | ~90 | Ollama nhỉnh hơn |
| 4 | ~320 | ~180 | vLLM ~1.8x |
| 10 | ~485 | ~148 | vLLM ~3.3x |
| 50 | ~920 | ~155 | vLLM ~6x |
| 128 | ~8,033 | ~484 | vLLM ~16.6x |
Insight quan trọng: Ở 1 user, Ollama thậm chí nhanh hơn một chút nhờ overhead Python thấp hơn. Sự khác biệt bắt đầu rõ từ 4+ concurrent users và trở nên cực kỳ lớn ở scale cao.
Latency (time-to-first-token)
| Scenario | vLLM | Ollama |
|---|---|---|
| 1 user (single request) | ~82ms | ~45ms |
| 50 concurrent users | ~145ms | ~3,200ms |
| 128 concurrent users | ~10.7ms (TTFT) | ~65ms (TTFT) |
Điều đáng chú ý: ở single user, Ollama thực sự có latency thấp hơn vì không có Python scheduling overhead. Nhưng khi concurrency tăng, Ollama sụp đổ hoàn toàn.
Memory footprint
| Metric | vLLM | Ollama |
|---|---|---|
| System RAM | ~4.6 GB | ~1.8 GB |
| CPU utilization under load | ~15% | ~8% |
| GPU memory efficiency | Cao (PagedAttention) | Thấp (static allocation) |
Khi Ollama lên production: những vấn đề mà ít ai nói
Qua kinh nghiệm thực tế và đọc case study từ cộng đồng, chúng tôi tổng hợp các vấn đề phổ biến nhất khi đẩy Ollama lên production:
1. Model unload sau 5 phút idle - Ollama tự unload model khỏi GPU memory sau 5 phút không có request. Request đầu tiên sau đó phải reload, mất 30 giây đến vài phút tùy model size. Với production API, đây là unacceptable cold start.
2. Concurrency cap mặc định = 4 - Mặc định Ollama xử lý tối đa 4 request song song. Có thể tăng qua env var OLLAMA_NUM_PARALLEL, nhưng memory tăng tuyến tính và không có cơ chế dynamic batching.
3. Không có tensor parallelism - Ollama không hỗ trợ chia model ra nhiều GPU. Với model 70B+ cần multi-GPU, Ollama không phải lựa chọn phù hợp.
4. Observability hạn chế - Ollama không expose Prometheus metrics out of the box. Để monitor production cần wrap thêm.
5. Quantization hạn chế - Chỉ hỗ trợ weight-only quantization (GGUF). vLLM hỗ trợ INT4, INT8, FP8, FP4 với nhiều backend như AWQ, GPTQ, bitsandbytes.
Setup so sánh: từ 0 đến inference API
Ollama - 3 bước, 5 phút
# Bước 1: Cài Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# Bước 2: Pull và chạy model
ollama pull llama3.1:8b
ollama serve
# Bước 3: Gọi API (OpenAI-compatible)
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.1:8b",
"messages": [{"role": "user", "content": "Hello!"}]
}'
Thế là xong. Không cần GPU bắt buộc, chạy được trên Mac M1, Windows, Linux.
vLLM - Docker production setup
# Chạy vLLM với Docker (yêu cầu NVIDIA GPU + nvidia-container-toolkit)
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HF_TOKEN=$HF_TOKEN" \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct \
--tensor-parallel-size 1 \
--max-model-len 4096 \
--gpu-memory-utilization 0.90
Gọi API - hoàn toàn tương thích OpenAI:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed" # vLLM không cần auth mặc định
)
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": "Bạn là trợ lý hữu ích."},
{"role": "user", "content": "Giải thích PagedAttention là gì?"}
],
temperature=0.7,
max_tokens=512
)
print(response.choices[0].message.content)
Load test đơn giản để kiểm tra system
import asyncio
import time
from openai import AsyncOpenAI
async def benchmark(client, model, num_requests=50):
"""Đo throughput với N concurrent requests."""
async def single_request():
start = time.time()
response = await client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Viết 100 từ về Python."}],
max_tokens=200
)
elapsed = time.time() - start
tokens = response.usage.completion_tokens
return tokens, elapsed
start_total = time.time()
tasks = [single_request() for _ in range(num_requests)]
results = await asyncio.gather(*tasks)
total_time = time.time() - start_total
total_tokens = sum(r[0] for r in results)
avg_latency = sum(r[1] for r in results) / len(results)
print(f"Throughput: {total_tokens / total_time:.1f} tok/s")
print(f"Avg latency: {avg_latency:.2f}s")
print(f"Total time: {total_time:.2f}s")
# Test với vLLM
asyncio.run(benchmark(
AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="x"),
"meta-llama/Llama-3.1-8B-Instruct",
num_requests=50
))
Bảng quyết định: chọn gì cho dự án của bạn?
| Tiêu chí | Ollama | vLLM |
|---|---|---|
| Setup time | 5 phút | 30–60 phút |
| Yêu cầu GPU | Không bắt buộc | Bắt buộc (NVIDIA) |
| Chạy trên Mac/CPU | Có | Không |
| Concurrent users < 5 | Tốt | Over-engineered |
| Concurrent users 5–20 | Chậm | Tốt |
| Concurrent users 20+ | Không phù hợp | Lựa chọn duy nhất |
| Model 70B+ / multi-GPU | Không hỗ trợ | Hỗ trợ tốt |
| Quantization nâng cao | Giới hạn (GGUF) | Đầy đủ (AWQ, GPTQ, FP8) |
| OpenAI API compatibility | Có | Có |
| Prometheus metrics | Cần tự thêm | Built-in |
| Model management UI | Có | Không |
| Speculative decoding | Không | Có |
| Chi phí infra/complexity | Thấp | Cao |
Khuyến nghị cụ thể theo giai đoạn:
Prototype / demo / personal project → Ollama
Khi bạn cần chạy nhanh để validate idea, demo cho stakeholder, hoặc build tool nội bộ cho 1–3 người dùng. Setup nhanh, không cần GPU đắt tiền, model library phong phú.
Staging / MVP với vài chục user → Ollama (có tune) hoặc vLLM tùy budget
Có thể tăng OLLAMA_NUM_PARALLEL và tắt auto-unload (OLLAMA_KEEP_ALIVE=-1). Nếu budget cho phép server GPU, hãy dùng vLLM từ đầu để không phải migrate sau.
Production với traffic thật (50+ concurrent) → vLLM, không có lựa chọn nào khác
Benchmark không biết nói dối: ở 128 concurrent users, vLLM cho throughput 16.6x so với Ollama. Nếu bạn đang xây AI feature cho end-user product, hãy đầu tư vào vLLM từ đầu.
Team nhỏ, không có DevOps chuyên dụng → Bắt đầu với Ollama, có migration path sang vLLM
Vì cả hai đều dùng OpenAI-compatible API, việc migrate code gần như zero-effort - chỉ đổi base_url. Nhưng hãy chuẩn bị mental cho việc phải optimize vLLM config khi scale.
Kinh nghiệm thực chiến từ team BKGlobal
Khi triển khai hệ thống RAG nội bộ, chúng tôi đã dùng Ollama trong 2 tháng đầu và thấy hoạt động tốt với team ~8 người. Vấn đề thực sự bắt đầu khi mở rộng cho toàn bộ công ty (~60 người) - concurrent request tăng đột biến vào giờ cao điểm, Ollama bắt đầu timeout.
Sau khi migrate sang vLLM (mất 2 ngày setup + tuning), throughput tăng hơn 5x, latency ổn định ngay cả khi 30+ người dùng cùng lúc. Quan trọng hơn, không còn cảnh "system đang chậm, anh em chờ xíu" trong Slack nữa.
Một điều chúng tôi học được: đừng nhìn vào single-user benchmark để đánh giá inference server cho production. Con số thực sự quan trọng là throughput ở 50–100 concurrent users - đó mới là môi trường production thật.
Kết luận
vLLM và Ollama không phải đối thủ - chúng phục vụ hai use case khác nhau.
- Ollama = công cụ hoàn hảo cho developer local, prototype, và team nhỏ dưới 10 người dùng đồng thời
- vLLM = lựa chọn bắt buộc khi cần production-grade serving với nhiều user, SLA latency, và GPU utilization cao
Nếu bạn đang xây AI feature cho sản phẩm thật, hãy prototype với Ollama nhưng plan production với vLLM. API interface giống nhau nên migration dễ - điều quan trọng là không bị surprise về performance khi lên production.
Anh em đang dùng inference server nào trong dự án? Nếu có kinh nghiệm thú vị với Triton, TGI, hay LMDeploy, hãy chia sẻ trong phần comment - team BKGlobal luôn muốn nghe về thực chiến từ cộng đồng.
BKGlobal Tech Team
Bài viết dựa trên kinh nghiệm thực tế và benchmark từ Red Hat Developer, Northflank, Spheron, và vLLM documentation.