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 vLLMOllama - 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 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
Prometheus metrics Cần tự thêm Built-in
Model management UI Không
Speculative decoding Không
Chi phí infra/complexity Thấp Cao

Khuyến nghị cụ thể theo giai đoạn:

Prototype / demo / personal projectOllama

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 userOllama (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ụngBắ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.

Ứng dụng & Xu hướng AI

Xem tất cả