vLLM — produkciona inferencija sa više paralelnih zahteva

Ollama i llama.cpp su pisani za scenario u kome jedan korisnik čeka jedan odgovor. Kada na isti server krene deset ljudi ili skripta koja obrađuje hiljade zapisa, ta pretpostavka pada — i pada skupo, jer kartica najveći deo vremena stoji.

vLLM je pisan za suprotan slučaj. Ovaj tekst pokazuje kako se instalira, po čemu se razlikuje i kada prelazak zaista ima smisla.

Odakle razlika

Dva mehanizma nose gotovo sve.

Continuous batching. Umesto da čeka da se jedan odgovor završi pa uzme sledeći zahtev, vLLM ubacuje nove zahteve u obradu čim se oslobodi mesto. Pošto je inferencija ograničena propusnošću memorije — kako pokazuje tekst o CPU i GPU inferenciji — težine se ionako čitaju za svaki token. Obraditi osam zahteva u istom prolazu košta jedva više nego obraditi jedan.

PagedAttention. KV keš se ne rezerviše unapred u komadu, nego u blokovima, kao stranice u virtuelnoj memoriji. Nema praznog prostora rezervisanog za kontekst koji možda nikad neće biti iskorišćen, a zajednički deo prompta — recimo isti sistemski prompt kod svih korisnika — čuva se jednom umesto po korisniku.

Ollama, llama.cpp vLLM
Jedan korisnik Odlično Isto ili nešto slabije
Deset paralelnih Red čekanja Višestruko veća propusnost
Format modela GGUF safetensors, AWQ, GPTQ, FP8
Rad na procesoru Podržan Praktično ne
Model mora ceo u VRAM Ne, preliva se Da
Više modela odjednom Da Jedan po procesu
Vreme do prvog modela Minut Pola sata sa preuzimanjem

Red o memoriji je onaj koji odlučuje. vLLM nema prelivanje na procesor — ako model ne staje u VRAM, servis se ne pokreće. Nema tihe degradacije kao kod Ollame, ali nema ni popuštanja.

Kada ostati na Ollami

  • Jedan korisnik ili nekoliko upita dnevno.
  • Model ne staje ceo u karticu.
  • Menjaš modele često, ili ih držiš više odjednom.
  • Nemaš NVIDIA karticu.

Instalacija

vLLM je Python paket i traži virtuelno okruženje. Sistemski Python nemoj dirati.

$ python3 -m venv ~/.venv/vllm
$ source ~/.venv/vllm/bin/activate
$ pip install -U vllm

Paket povlači PyTorch i CUDA biblioteke — računaj na nekoliko gigabajta i podosta čekanja. Za brže rešavanje zavisnosti:

$ pip install uv
$ uv venv ~/.venv/vllm --python 3.12
$ source ~/.venv/vllm/bin/activate
$ uv pip install vllm

Kao i kod Ollame, potreban je samo drajver, ne i CUDA toolkit — biblioteke stižu uz paket.

$ nvidia-smi
$ python -c "import torch; print(torch.cuda.is_available())"
True

Ako je odgovor False, drajver nije ispravan i dalje nema smisla — pogledaj tekst o drajverima.

$ vllm --version

Prvo pokretanje

$ vllm serve Qwen/Qwen3-8B

Model se preuzima sa Hugging Face-a pri prvom pokretanju, u keš opisan u tekstu o Hugging Face CLI alatu. Za zatvorene modele postavi HF_TOKEN pre pokretanja.

Podizanje traje minut do dva, uz obiman ispis. Dva reda su bitna:

INFO ... Loading model weights took 15.2088 GB
INFO ... # GPU blocks: 4823, # CPU blocks: 2048
INFO ... Starting vLLM API server on http://0.0.0.0:8000

Broj GPU blokova govori koliko je memorije ostalo za KV keš. Ako je taj broj mali, model jedva staje i propusnost će biti loša uprkos tome što servis radi.

$ curl http://localhost:8000/v1/models

Podrazumevano sluša na 0.0.0.0, za razliku od Ollame. To znači da je od prvog trenutka dostupan svima na mreži, bez ikakve autentifikacije. Prvo što treba da uradiš:

$ vllm serve Qwen/Qwen3-8B --host 127.0.0.1

Parametri

Parametar Šta radi
--host, --port Adresa i port; podrazumevano 0.0.0.0:8000
--gpu-memory-utilization Udeo VRAM-a koji sme da zauzme; podrazumevano 0.9
--max-model-len Najveći kontekst; ograniči ga ako memorija ne dozvoljava
--max-num-seqs Najviše zahteva u jednom paketu
--tensor-parallel-size Broj kartica preko kojih se model deli
--quantization Metoda; obično se prepoznaje sama
--kv-cache-dtype Kvantizacija KV keša, npr. fp8
--api-key Obavezan ključ u zaglavlju
--served-model-name Kraći naziv za API
--enable-prefix-caching Deljenje zajedničkog dela prompta
--download-dir Gde se model preuzima

gpu-memory-utilization

Ovo je parametar oko koga se najviše vrti. Vrednost 0.9 znači da vLLM uzima devedeset posto memorije kartice, bez obzira na to koliko model stvarno traži — ostatak ide u KV keš i to je namerno.

$ vllm serve Qwen/Qwen3-8B --gpu-memory-utilization 0.85

Spusti je ako kartica gura i ekran ili neki drugi proces. Podizanje iznad 0.95 vodi u pucanje pri opterećenju, jer nema rezerve za skokove u zauzeću.

Nemoj se iznenaditi kad nvidia-smi pokaže punu karticu odmah po pokretanju — memorija se rezerviše unapred, i to je razlika u odnosu na Ollamu, koja je uzima kako treba.

Postavka za karticu od 24 GB

$ vllm serve Qwen/Qwen3-8B-AWQ \
    --host 127.0.0.1 --port 8000 \
    --served-model-name qwen3 \
    --gpu-memory-utilization 0.90 \
    --max-model-len 16384 \
    --max-num-seqs 16 \
    --enable-prefix-caching \
    --api-key tajni-kljuc-ovde

Za AWQ i ostale formate pogledaj tekst o kvantizaciji. Ukratko: GGUF ovde ne ide, traži repozitorijum sa -AWQ ili -FP8 u nazivu, ili model u punoj preciznosti ako staje.

Više kartica

$ vllm serve Qwen/Qwen3-32B --tensor-parallel-size 2

Model se deli preko dve kartice, sa svakim slojem rasečenim na pola. Broj mora deliti broj glava pažnje u modelu, pa vrednosti koje nisu stepen dvojke često ne rade.

API

Isključivo OpenAI-kompatibilan, bez sopstvenog puta:

$ curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer tajni-kljuc-ovde" \
  -d '{
    "model": "qwen3",
    "messages": [{"role": "user", "content": "Šta radi komanda ss -tulpn?"}],
    "temperature": 0.2,
    "max_tokens": 256
  }'

Naziv modela mora odgovarati onome pod kojim je servis podignut — otud korist od --served-model-name, jer je puna oznaka sa Hugging Face-a nezgodna za kucanje.

Detaljno o oblicima zahteva ima u tekstu o OpenAI-kompatibilnom API-ju.

Korisne adrese: /health stanje, /v1/models spisak, /metrics metrike u Prometheus formatu — ove poslednje su uključene same od sebe i tema su teksta o monitoringu.

$ curl -s http://localhost:8000/metrics | grep -E 'num_requests|gpu_cache_usage'
vllm:num_requests_running 7.0
vllm:num_requests_waiting 2.0
vllm:gpu_cache_usage_perc 0.63

Ova tri broja su najkorisniji pokazatelj stanja. Ako num_requests_waiting stalno raste, kapacitet je premašen. Ako gpu_cache_usage_perc stalno stoji blizu jedinice, zahtevi se odbacuju i vraćaju u red.

Systemd jedinica

$ sudo useradd -r -s /bin/false -m -d /var/lib/vllm vllm
$ sudo tee /etc/systemd/system/vllm.service > /dev/null <<'EOF'
[Unit]
Description=vLLM inference server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=vllm
Group=vllm
Environment="HF_HOME=/var/lib/vllm/huggingface"
EnvironmentFile=-/etc/vllm/env
ExecStart=/var/lib/vllm/.venv/bin/vllm serve Qwen/Qwen3-8B-AWQ \
    --host 127.0.0.1 --port 8000 \
    --served-model-name qwen3 \
    --gpu-memory-utilization 0.90 \
    --max-model-len 16384 \
    --enable-prefix-caching
Restart=always
RestartSec=10
TimeoutStartSec=600

[Install]
WantedBy=multi-user.target
EOF

TimeoutStartSec je ovde bitan. Podizanje sa preuzimanjem modela ume da premaši podrazumevani rok, pa systemd ubije proces usred posla i onda se sve vrti u krug.

API ključ i HF token idu u zaseban fajl, ne u jedinicu:

$ sudo mkdir -p /etc/vllm
$ sudo tee /etc/vllm/env > /dev/null <<'EOF'
VLLM_API_KEY=tajni-kljuc-ovde
HF_TOKEN=hf_xxxxxxxxxxxxx
EOF
$ sudo chmod 600 /etc/vllm/env
$ sudo chown vllm:vllm /etc/vllm/env
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now vllm
$ sudo journalctl -u vllm -f

Šta stvarno dobijaš

Poređenje ima smisla samo pod opterećenjem. Sa jednim korisnikom razlika je zanemarljiva, ponekad i u korist llama.cpp.

Model 8B, kartica od 24 GB, kratki odgovori

Paralelnih zahteva     Ollama       vLLM
1                      58 t/s       54 t/s
4                      61 t/s      190 t/s
16                     63 t/s      520 t/s

Brojevi su ilustrativni i zavise od hardvera, ali odnos je onaj koji viđaš u praksi. Ollamin ukupni učinak jedva raste sa brojem korisnika; vLLM raste gotovo linearno dok se kartica ne zasiti.

Pojedinačni korisnik pritom dobija odgovor sporije nego kad je sam — to je cena. Ako ti je bitna latencija jednog korisnika, a ne ukupna propusnost, vLLM ti ne donosi ništa. Kako se ovo meri, pokazuje tekst o benchmarku.

Praktični scenariji

Scenario 1: nema dovoljno memorije pri pokretanju

ValueError: The model's max seq len (40960) is larger than the maximum
number of tokens that can be stored in KV cache

Model staje, ali za deklarisani kontekst nema mesta. Spusti --max-model-len na realnu vrednost — retko ti stvarno treba maksimum koji model podržava.

Scenario 2: servis se ne podiže, a model bi trebalo da stane

Proveri da nije GGUF. vLLM traži safetensors, AWQ, GPTQ ili FP8. Proveri i da nešto drugo ne drži karticu:

$ nvidia-smi

Scenario 3: propusnost ne raste sa brojem korisnika

Pogledaj gpu_cache_usage_perc. Ako je blizu jedinice, KV keš je usko grlo — spusti --max-model-len ili podigni --gpu-memory-utilization.

Scenario 4: servis puca posle nekog vremena rada

Vrednost --gpu-memory-utilization je previsoka i nema rezerve za skokove. Spusti na 0.85 i posmatraj.

Scenario 5: podizanje traje predugo i systemd ga ubija

Podigni TimeoutStartSec ili preuzmi model unapred kroz hf download, pa neka servis kreće od gotovog fajla.

Scenario 6: treba ti drugi model

vLLM drži jedan model po procesu. Ili restartuj servis sa drugim modelom, ili podigni drugu jedinicu na drugom portu — uz uslov da oba stanu u memoriju.

Kratka referenca

  • uv pip install vllm — instalacija u virtuelno okruženje
  • vllm serve organizacija/model — pokretanje
  • --host 127.0.0.1obavezno, podrazumevano je otvoreno prema mreži
  • --gpu-memory-utilization 0.90 — udeo VRAM-a; ispod 0.95
  • --max-model-len — kontekst; prvo mesto za smanjenje
  • --max-num-seqs — zahteva u paketu
  • --tensor-parallel-size 2 — deljenje preko kartica
  • --enable-prefix-caching — zajednički deo prompta jednom
  • --served-model-name — kraći naziv za API
  • --api-key — obavezan ključ
  • /v1/chat/completions — jedini API put
  • /metricsnum_requests_waiting, gpu_cache_usage_perc
  • TimeoutStartSec=600 — inače systemd prekine podizanje
  • Nema prelivanja na procesor — model staje ili servis ne radi
  • Nema GGUF — AWQ, GPTQ, FP8 ili puna preciznost

Vežba

  1. Instaliraj vLLM u virtuelno okruženje i potvrdi da PyTorch vidi karticu.
  2. Pokreni model koji staje u tvoju karticu i u ispisu pronađi broj GPU blokova.
  3. Pošalji isti upit kroz curl i uporedi oblik odgovora sa Ollaminim.
  4. Pusti osam paralelnih zahteva odjednom i prati /metrics tokom obrade.
  5. Izmeri ukupnu propusnost istog modela kroz Ollamu i kroz vLLM pri jednom, pa pri osam paralelnih zahteva.
  6. Namerno zadaj --max-model-len veći nego što memorija dozvoljava i pročitaj grešku do kraja.

Sledeći tekst u serijalu: Ollama u Dockeru sa GPU passthrough-om

Povezano:

Comments

Popular posts from this blog

Početak u Linuxu — šta je i zašto se uči

Konverzija tipova podataka u Pythonu

groupadd