Ollama u Dockeru sa GPU passthrough-om

Kontejner rešava dva problema koja se javljaju čim server prestane da bude samo tvoj: verzija alata više ne zavisi od distribucije, a ceo servis se seli na drugu mašinu jednom komandom. Cena je jedan dodatni sloj koji mora da propusti karticu.

Taj sloj se zove nvidia-container-toolkit i on je sve o čemu ovaj tekst zapravo govori. Bez njega kontejner vidi procesor i ništa više.

Kada u kontejner

Za Protiv
Verzija alata nezavisna od distribucije Još jedan sloj koji može da zakaže
Ista postavka na svakoj mašini Slika sa CUDA bibliotekama ide preko gigabajta
Više servisa jedan pored drugog, bez sukoba Drajver i dalje mora biti na domaćinu
Vraćanje na staru verziju je trivijalno Modeli se lako izgube ako volumen nije podešen
Prirodan korak ka Kubernetesu Nešto komplikovaniji nadzor

Ako imaš jedan server i jedan model, systemd jedinica iz prethodnog teksta je jednostavnija i sasvim dovoljna. Kontejner uzmi kad ti trebaju dva servisa uporedo, kad postavku seliš između mašina, ili kad ideš prema Kubernetesu.

Šta se deli, a šta ne

Ovo je jedina stvar koju treba razumeti pre nego što bilo šta pokreneš.

Domaćin:     kernel drajver (nvidia.ko, nvidia_uvm)
             ↓ propušta se u kontejner
Kontejner:   CUDA biblioteke, alat, model

Drajver ostaje na domaćinu i ne instalira se u kontejner. Slika sadrži CUDA biblioteke, a toolkit se stara da uređaj i odgovarajuće biblioteke drajvera budu vidljivi unutra. Zato ista slika radi na mašinama sa različitim verzijama drajvera.

Provera pre svega ostalog:

$ nvidia-smi
$ lsmod | grep nvidia_uvm
$ docker --version

Ako Docker nemaš, instalacija je opisana u tekstu o instalaciji Dockera.

nvidia-container-toolkit

Debian i Ubuntu:

$ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
    | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

$ curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
    | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
    | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

$ sudo apt update
$ sudo apt install -y nvidia-container-toolkit

RHEL, Rocky, Fedora:

$ curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo \
    | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo

$ sudo dnf install -y nvidia-container-toolkit

Povezivanje sa Dockerom je zaseban korak — instalacija paketa nije dovoljna:

$ sudo nvidia-ctk runtime configure --runtime=docker
$ sudo systemctl restart docker

Komanda upisuje NVIDIA izvršno okruženje u /etc/docker/daemon.json:

$ cat /etc/docker/daemon.json
{
    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }
}

Provera

$ docker run --rm --gpus all ubuntu:24.04 nvidia-smi

Ako se ispis pojavi isti kao na domaćinu, sve radi. Ovo je test koji vredi zapamtiti — njime razdvajaš problem sa Dockerom od problema sa Ollamom.

Ollama u kontejneru

$ docker run -d \
    --name ollama \
    --gpus all \
    -v ollama:/root/.ollama \
    -p 127.0.0.1:11434:11434 \
    --restart unless-stopped \
    ollama/ollama

Tri stvari u ovoj komandi nose sav teret.

--gpus all propušta sve kartice. Za tačno određenu:

$ docker run --gpus '"device=0"' ...
$ docker run --gpus '"device=GPU-a1b2..."' ...

-v ollama:/root/.ollama čuva modele van kontejnera. Bez ovoga svaki restart znači ponovno preuzimanje svega — a to je desetine gigabajta.

-p 127.0.0.1:11434:11434 vezuje port samo za lokalnu adresu. Zapis -p 11434:11434, koji ćeš viđati po uputstvima, otvara port prema celoj mreži i zaobilazi pravila firewalla, jer Docker piše sopstvena iptables pravila ispred tvojih. To je najčešći način da nezaštićen model završi na internetu.

$ docker logs -f ollama
$ docker exec ollama ollama pull qwen3:8b
$ docker exec -it ollama ollama run qwen3:8b

Provera koja je i ovde jedina bitna:

$ docker exec ollama ollama ps
NAME       ID            SIZE     PROCESSOR    UNTIL
qwen3:8b   a1b2c3d4e5f6  6.2 GB   100% GPU     4 minutes from now

Ako piše bilo šta osim sto posto GPU, karticu kontejner ne koristi kako treba — vrati se na test sa nvidia-smi iz prethodnog odeljka.

Promenljive okruženja

Iste kao kod systemd jedinice, samo kroz -e:

$ docker run -d \
    --name ollama \
    --gpus all \
    -v ollama:/root/.ollama \
    -p 127.0.0.1:11434:11434 \
    -e OLLAMA_KEEP_ALIVE=-1 \
    -e OLLAMA_CONTEXT_LENGTH=16384 \
    -e OLLAMA_KV_CACHE_TYPE=q8_0 \
    -e OLLAMA_FLASH_ATTENTION=1 \
    -e OLLAMA_NUM_PARALLEL=2 \
    --restart unless-stopped \
    ollama/ollama

Značenje svake stoji u tekstu o systemd jedinici. Vrednost OLLAMA_HOST unutar kontejnera ostavi na podrazumevanoj — izlaganje se rešava mapiranjem porta, ne promenljivom.

Gde su modeli

$ docker volume inspect ollama
[
    {
        "Mountpoint": "/var/lib/docker/volumes/ollama/_data",
        "Name": "ollama"
    }
]
$ sudo du -sh /var/lib/docker/volumes/ollama/_data

Ako je /var/lib/docker na maloj particiji, koristi direktorijum umesto imenovanog volumena:

$ sudo mkdir -p /data/ollama
$ docker run -d --gpus all -v /data/ollama:/root/.ollama ...

Docker Compose

Za sve osim najkraćeg testa, compose je čitljiviji od duge komande:

$ nano compose.yaml
services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    restart: unless-stopped
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - /data/ollama:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=-1
      - OLLAMA_CONTEXT_LENGTH=16384
      - OLLAMA_KV_CACHE_TYPE=q8_0
      - OLLAMA_FLASH_ATTENTION=1
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
$ docker compose up -d
$ docker compose logs -f
$ docker compose exec ollama ollama pull qwen3:8b

Odeljak deploy.resources je compose ekvivalent za --gpus all. Zapis je nezgrapan, ali je to jedini ispravan način. Za određenu karticu zameni count: all sa device_ids: ['0'].

Više o samom alatu ima u tekstu o Docker Compose.

Ollama i Open WebUI zajedno

Ovde se prednost kontejnera prvi put stvarno vidi — dva servisa u istoj mreži, bez ijedne izmene na domaćinu:

services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    restart: unless-stopped
    volumes:
      - /data/ollama:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=-1
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

  webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: unless-stopped
    depends_on:
      - ollama
    ports:
      - "127.0.0.1:3000:8080"
    volumes:
      - /data/open-webui:/app/backend/data
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434

Ollama ovde uopšte nema mapiran port prema domaćinu. Dostupna je samo interno, po imenu servisa, a napolju stoji jedino veb interfejs. To je bolja postavka od one u kojoj su oba porta otvorena.

Sam interfejs je tema sledećeg teksta.

vLLM u kontejneru

Zvanična slika povlači sve zavisnosti, pa nema instalacije u virtuelno okruženje:

$ docker run -d \
    --name vllm \
    --gpus all \
    --ipc=host \
    -v /data/huggingface:/root/.cache/huggingface \
    -p 127.0.0.1:8000:8000 \
    --env HF_TOKEN \
    vllm/vllm-openai:latest \
    --model Qwen/Qwen3-8B-AWQ \
    --served-model-name qwen3 \
    --gpu-memory-utilization 0.90 \
    --max-model-len 16384

Oznaka --ipc=host je obavezna. vLLM koristi deljenu memoriju između procesa, a podrazumevanih 64 MB u kontejneru mu nije dovoljno — bez nje servis puca pri učitavanju, sa greškom koja ni na šta ne upućuje. Alternativa je --shm-size=8g.

Zapis --env HF_TOKEN bez vrednosti prosleđuje promenljivu iz okruženja domaćina, pa token ne završava u istoriji ljuske ni u ispisu docker inspect.

Ostali parametri su isti kao u tekstu o vLLM-u.

Održavanje

$ docker pull ollama/ollama
$ docker compose up -d

Compose sam prepoznaje noviju sliku i podiže kontejner ponovo. Modeli ostaju, jer su na volumenu.

Vraćanje na staru verziju, ako nova pravi problem:

$ docker run -d --gpus all ... ollama/ollama:0.11.4

Ovo je prednost koju systemd postavka nema — verziju vraćaš u sekundi, bez ponovne instalacije.

Čišćenje starih slika, koje se nagomilavaju brzo jer su velike:

$ docker image prune -a
$ docker system df

Komanda docker system prune --volumes briše i volumene sa modelima. Nju izbegavaj.

Praktični scenariji

Scenario 1: kontejner ne vidi karticu

$ docker run --rm --gpus all ubuntu:24.04 nvidia-smi

Ako ni ovo ne radi, problem je u toolkit-u, ne u Ollami. Proveri da li je pokrenuta nvidia-ctk runtime configure i da li je Docker restartovan posle toga.

Scenario 2: posle restarta nema modela

Volumen nije bio podešen, pa je sve ostalo u sloju kontejnera koji je nestao. Dodaj -v i preuzmi ponovo.

Scenario 3: port je otvoren iako firewall to zabranjuje

Docker upisuje sopstvena pravila ispred tvojih. Rešenje je vezivanje za adresu pri mapiranju:

-p 127.0.0.1:11434:11434

Scenario 4: vLLM puca odmah po pokretanju u kontejneru

Nedostaje --ipc=host. Greška govori o deljenoj memoriji i ničemu drugom ne liči.

Scenario 5: nema mesta na disku, a modeli su obrisani

$ docker system df

Stare slike sa CUDA bibliotekama zauzimaju po nekoliko gigabajta svaka. Očisti ih sa docker image prune -a.

Scenario 6: dva kontejnera na istoj kartici, oba spora

Ne postoji podela memorije među kontejnerima — svaki uzima koliko stigne i oba se preliju. Na jednoj kartici drži jedan servis, ili im izričito dodeli različite kartice.

Kratka referenca

  • nvidia-container-toolkit — paket koji propušta karticu
  • nvidia-ctk runtime configure --runtime=docker — obavezan korak posle instalacije
  • docker run --rm --gpus all ubuntu:24.04 nvidia-smi — test propuštanja
  • --gpus all — sve kartice
  • --gpus '"device=0"' — određena kartica
  • -v ollama:/root/.ollama — modeli preživljavaju restart
  • -p 127.0.0.1:11434:11434 — vezivanje za lokalnu adresu
  • --ipc=host — obavezno za vLLM
  • --env HF_TOKEN — prosleđivanje bez upisivanja vrednosti
  • deploy.resources.reservations.devices — GPU u compose fajlu
  • docker exec ollama ollama ps — provera da radi na kartici
  • docker system df — zauzeće slikama i volumenima
  • Drajver ostaje na domaćinu, u kontejner ide samo CUDA
  • Docker zaobilazi firewall — port uvek vezuj za adresu

Vežba

  1. Instaliraj toolkit i potvrdi da kontejner vidi karticu kroz test sa nvidia-smi.
  2. Pokreni Ollamu u kontejneru sa volumenom, preuzmi model, restartuj kontejner i potvrdi da je model ostao.
  3. Namerno izostavi -v, uporedi šta se dešava posle restarta.
  4. Napiši compose fajl sa Ollamom i Open WebUI, tako da Ollama nema mapiran port prema domaćinu.
  5. Proveri sa ss -tulpn koji su portovi stvarno otvoreni posle pokretanja compose postavke.
  6. Uporedi brzinu istog modela u kontejneru i kroz systemd jedinicu na domaćinu.

Sledeći tekst u serijalu: Open WebUI — web interfejs za tvoje lokalne modele

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