Files
nanobot-runtime/projects/devops/artifacts/2026-09-13_ollama-nvidia-negative-findings.md
2026-09-14 07:24:46 +02:00

12 KiB
Raw Blame History

Negativní zjištění — nvidia.hell / ollama

Co selhalo, co nefunguje a co jsme zkusili marně. Sesbíráno z incidentů 12.13. 9. 2026. Plné záznamy jsou v history.md, ověřená pozitivní fakta v knowledge.md.

Rozsah: jen stroj nvidia.hell a ollama. Slepé uličky z PVE migrace tu nejsou — ty jsou v knowledge.md.


1. Pojistky, které selhaly

earlyoom jako ochrana ollamy — zavrženo, odinstalováno

Nasazen 13. 9. ve 14:20, do večera odinstalován. Killoval llama-server uprostřed CUDA inicializace a nvidia driver při tom natrvalo ztratil napinovanou host paměť. Po ~15 killech chybělo 9,27 GiB a ollama nenačetla vůbec nic.

Tím vznikla kladná zpětná vazba: leak ubere RAM → práh -m 10 je blíž → earlyoom zabíjí dřív → další leak. Nakonec umíral každý nový llama-server už při 300 MiB RSS.

Poučení: proti procesu, který drží GPU kontext, je externí zabíječ horší než nic. Náhradou je cgroup strop, který nenechá tlak vzniknout. → history.md 2026-09-13 15:45

earlyoom ve výchozí konfiguraci by nezasáhl nikdy

Balík byl na stroji nainstalovaný a běžel už před incidentem, jen ve výchozím -m 10 -s 10. earlyoom zabíjí jen když jsou obě podmínky splněné současně. Ze swapu bylo obsazeno 1,4 z 32 GB, tedy ~95 % volných — podmínka swap <= 10 % nemohla nastat. Běžící earlyoom tedy nebyl žádná ochrana.

earlyoom odvozuje SIGKILL práh jako polovinu zadaného

První pokus s -s 100 dal SIGKILL práh swap <= 50 %, takže při 95 % volného swapu by eskalace nikdy nenastala. Musí se psát -s 100,100.

MemoryHigh mezi spotřebou a MemoryMax = past

Nastaveno MemoryHigh=5G + MemoryMax=6G. gemma4 potřebuje host buffer 5901 MiB, což padlo přesně doprostřed. Výsledek při testu 13. 9. 17:05:

memory.events:  high 84358   max 0   oom 0   oom_kill 0
memory.peak:    5,46 GiB

Cgroup 84 tisíckrát reclaimovala, proces se plazil, tvrdý strop nezasáhl ani jednou a load visel 600 s bez jediné chybové odpovědi. Ani neselhal, ani neproběhl.

Poučení: když má konfigurace „rychle selhat", MemoryHigh se nesmí nastavit — ať se narazí rovnou do MemoryMax. Odstraněno. → history.md 2026-09-13 17:10

systemd-oomd — nepoužito

Na stroji není nainstalovaný. Nenasazovali jsme ho: je to PSI varianta téhož principu jako earlyoom, tedy SIGKILL zvenčí, takže se dá čekat týž leak. Neověřeno měřením, jen odvozeno.


2. Slepé uličky v konfiguraci ollamy

Ollama nemá admission control

Ověřeno z ollama serve --help (v0.33.2): neexistuje proměnná typu „odmítni load, když se nevejde do VRAM". Ollama je navržená tak, že přeteče do CPU vždycky. K dispozici je jen OLLAMA_MAX_LOADED_MODELS, OLLAMA_MAX_QUEUE, OLLAMA_NUM_PARALLEL, OLLAMA_GPU_OVERHEAD, OLLAMA_CONTEXT_LENGTH, LLAMA_ARG_FIT_TARGET, OLLAMA_LOAD_TIMEOUT — nic z toho není admission control.

Jediné skutečné odmítnutí je nenabídat nebezpečné modely.

OLLAMA_NO_MMAP neexistuje

Žádost o globální řízení mmapu je #4895, PR #6854 nebyl nikdy sloučen („upstream changes broke this patch"). Lidé to žádají ještě 6/2026. Vypnutý mmap se tedy konfigurací obejít nedá.

Navýšení RAM problém neřeší

Podmínka je FreeMemory < modelSize + max(8 GB, TotalMemory/10). Headroom je tedy konstanta 7,5 GiB až do 80 GB RAM. Pro gemma4 (8,9 GiB) je potřeba 16,4 GiB volné RAM, takže mmap by zůstal zapnutý až u ~20GiB VM.

Důkaz z journalu — tři loady při zcela různé volné paměti, všechny tři disabling mmap ... headroom="7.5 GiB":

čas system_free
12:09:20 8,4 GiB
12:44:50 3,4 GiB
13:00:03 115,1 MiB

Volná paměť nikdy nehrála roli. Uživatel proto paměť naopak ubral z 11,4 na 9,5 GiB.

Omezování počtu modelů — zavrženo

OLLAMA_MAX_LOADED_MODELS=1 + NUM_PARALLEL=1 + MAX_QUEUE=8 byly v plánu a uživatel je oprávněně vyhodil: stroj shodila hostitelská RAM, ne VRAM (karta má 16 GB volných), a 49 requestů z nanobotu nepoložilo stroj frontou, ale sekvencí loadů — na což MAX_QUEUE vůbec nesahá. Byly to škrty bez vazby na příčinu, které by degradovaly běžný provoz.

Upgrade ollamy problém neřeší

Nainstalovaná 0.33.2, nejnovější v0.34.0 (5. 9. 2026). Release notes obsahují jen ChatGPT Desktop, structured output na Apple Silicon a OpenAI tool search — nic o scheduleru ani mmapu.

OLLAMA_KV_CACHE_TYPE=q4_0 rozbíjí modely

Proměnná je globální a shodí modely s n_embd_head_k=80. granite-code kvůli ní 12. 9. spadl 6× po sobě na K cache type q4_0 with block size 32 does not divide n_embd_head_k=80 — a každý pokus přitom nastartoval llama-server a načetl 1,9 GiB s vypnutým mmapem. Stále nastaveno, neopraveno.

gemma4 se na tenhle stroj nevejde a nikdy nevejde

Není to otázka velikosti modelu. Architektura Per-Layer Embeddings drží embeddingy na CPU záměrně:

| - CUDA0 (RTX 4060 Ti) | 16076 = 15951 + (2966 = 2829 + 29 + 107) + -2842 |
| - Host                |                 5938 = 5901 +  0 +  37           |

Do VRAM jde 2966 MiB z 16076 volných, zatímco host drží 5901 MiB. Kontraintuitivní důsledek: čím prázdnější karta, tím spíš se mmap vypne — při těsné VRAM zůstává zapnutý. Ollama si navíc CPU buffer započítá do předpovědi predicted="9.4 GiB" jako by šel celý do VRAM.


3. Chyby v systemd konfiguraci

StartLimitIntervalSec / StartLimitBurst v [Service] se tiše ignorují

Patří do [Unit]. RestartSec naopak do [Service]. Property se navíc jmenuje RestartUSecsystemctl show -p RestartSec nevrátí nic.

MemoryMax sám o sobě nelimituje swap

Mapuje na memory.max, což je strop jen pro RAM. Po jeho dosažení kernel proces nezabije, ale začne ho swapovat. Se swapem 32 GB to znamená thrashing místo rychlého OOM killu. Tvrdý strop vznikne až s MemorySwapMax.

Instalátor ollamy přepisuje unit soubor

install.sh (ř. 215) bezpodmínečně přepíše /etc/systemd/system/ollama.service přes tee. Do ollama.service.d/ nesahá, takže vlastní nastavení patří do drop-inu.

Zavrženo: držet kopii unitu a po upgradu ji kopírovat zpět — konzervuje i upstream řádky (ExecStart, User, PATH), takže upstream změna se ztratí.

Limity z 12. 9. ze stroje nevysvětleně zmizely

Zapsány v 21:10, v 21:44 už v override.conf nebyly (zůstaly jen StartLimit*, RestartSec a Environment). Při incidentu 13. 9. proto systemctl show ollama hlásil MemoryMax=infinity. Příčina zmizení nebyla nikdy zjištěna. Proto teď existuje záloha override.conf.bak-20260913 a kontrola limitů je součástí ověření.

Překlep LLAMA_MAX_LOADED_MODELS

V drop-inu je zakomentovaný řádek Environment="LLAMA_MAX_LOADED_MODELS=1". Proměnná se jmenuje OLLAMA_MAX_LOADED_MODELS — nefungovala by ani po odkomentování.


4. Co nástroje neukážou

Napinovanou paměť neukáže ps, nvidia-smi ani systemd-cgtop

Při leaku 9,27 GiB hlásily:

  • ps -eo rss přes všechny procesy dohromady ~250 MB
  • nvidia-smi: 2 MiB / 16380 MiB, No running processes found
  • systemd-cgtop: žádná služba nic nedrží
  • /proc/meminfo: AnonPages 135 MiB, ale Active(anon)+Inactive(anon) 9,4 GiB

Jediné, co to ukáže, je rozdíl nr_foll_pin_acquired nr_foll_pin_released ve /proc/vmstat. Na to je teď skript ~/bin/nvidia-reload na nvidia.hell.

Nedopočet paměti v sar z 13. 9. byl tentýž leak

Záznam z 13:40 skončil otevřeným bodem: kbmemused 10,6 GB proti kbanonpg 54126 MB, kbcached 975 MB, kbslab 187 MB — ~9,4 GB nesedělo do žádné kategorie a po rebootu to nešlo dovyšetřit. Vysvětleno až v 15:45: byla to právě ta napinovaná paměť. sar pro ni nemá sloupec.

Restart ollamy leak neuvolní a vystrčí ho z cgroup

Během incidentu je leak účtovaný cgroup ollamy (memory.current 5,59 GiB při anon jen 15,9 MB), takže ho MemoryMax drží. Po systemctl restart ollama je foll_pin delta identická, ale memory.current spadne na 18 MB a paměť si nese system.slice.

Kontejnerování tedy platí jen do prvního restartu; pak leak uteče ze stropu a trvale ubere stroji RAM. Uvolní ho až teardown nvidia driveru nebo reboot.

Proxmox ballooning je pro LLM stroje nevhodný

pvestatd (auto_ballooning) počítá cíl z volné paměti hostitele, ne z tlaku v guestu. maxchange je 100 MB na cyklus á 10 s, tedy ~10 MB/s: navýšení o 8 GB trvá ~14 minut, zatímco načtení modelu 17 s. Během OOM incidentu zůstalo total na 7,4 GiB, přestože guest padl na 136 MB volných.


5. Chybné závěry, které jsme museli opravit

„Viníkem je LibreChat" — ne

První diagnóza (13:40) označila LibreChat za spouštěč. Uživatel zatuhnutí reprodukoval i z ollama CLI, čímž závěr padl. LibreChat byl jen zesilovač: po 15minutovém timeoutu load okamžitě zopakoval při 115 MiB volných. Spouštěčem je samotný model.

„Embedding drží LibreChat" — ne

qwen3-embedding:0.6b drží nanobot.hell (192.168.4.64), ne LibreChat (192.168.4.45). Periodické POST /api/embed. Nanobot je zároveň zdrojem dávek desítek requestů přes ~10 různých modelů během minut.

„Regresi 0.32.10 způsobil Vulkan" — na nás nesedí

Rozbor na dev.to připisuje regresi Vulkanu na integrovaných GPU. Máme CUDA a diskrétní kartu. Náš log uvádí jiný spouštěč (host memory pressure), ale ústí do téhož --load-mode none. Že to není jen Vulkan, potvrzuje #18373.

„Leakuje jen externí kill" — ne

Leak vznikne i když load zruší sám ollama scheduler (Load failed: timed out waiting for llama-server to start: context canceled po odpadnutí klienta). Jedno takové zrušení stálo 5,19 GiB. Nezáleží tedy na tom, kdo llama-server ukončí — záleží na tom, že byl ukončen uprostřed loadu.


6. Upstream — nahlášené a neopravené

Issue Co říká Stav
#18373 Regrese načítání: 0.33.3 load 2m34s vs. 0.23.4 28s, SSD 600 MB/s místo 3 GB/s. Explicitně „all models", hlášeno i na NVIDII. otevřené, bez reakce maintainerů
#10104 200 GB RAM, 80 GB VRAM, ollama sama nasadí --no-mmap a load selže. zavřené bez řešení
#10341 gemma plní RAM navzdory volné VRAM, na RTX 4060 Ti 16 GB — stejná karta. duplikát #10040
#8654 „Available memory check should be disabled when mmap is in use" otevřené od 1/2025
#4895 + PR #6854 Žádost o globální řízení mmapu. PR nesloučen

Námitka uživatele, že by tohle muselo trápit spoustu lidí, byla správná — jen míří opačným směrem: problém je široce hlášený a upstream ho neřeší.


7. Drobnosti k opravě

  • zabbix-agent běží a míří na zabbix.hell, ale má Hostname=Zabbix server (defaultní hodnota) — stroj se nehlásí jako nvidia.
  • llama-server běží s --log-verbosity 4; journald kvůli tomu při incidentu žral 1,9 % CPU a journal je zaplevelený.
  • LibreChat titleConvo: true + titleModel: "current_model" posílá na týž model druhý souběžný požadavek (v logu Title generation timeout).
  • Balík earlyoom je ve stavu rc — zbývá apt purge, na disku jsou /etc/default/earlyoom a /etc/default/earlyoom.bak-20260913.