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

278 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](history.md), ověřená
pozitivní fakta v [knowledge.md](knowledge.md).
Rozsah: jen stroj nvidia.hell a ollama. Slepé uličky z PVE migrace tu nejsou —
ty jsou v [knowledge.md](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](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](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](https://github.com/ollama/ollama/issues/4895),
[PR #6854](https://github.com/ollama/ollama/pull/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 `RestartUSec``systemctl 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](https://dev.to/milkyway008/ollama-model-loads-time-out-after-the-03210-upgrade-the-load-mode-change-and-the-fix-25m)
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](https://github.com/ollama/ollama/issues/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](https://github.com/ollama/ollama/issues/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](https://github.com/ollama/ollama/issues/10104) | 200 GB RAM, 80 GB VRAM, ollama sama nasadí `--no-mmap` a load selže. | zavřené bez řešení |
| [#10341](https://github.com/ollama/ollama/issues/10341) | gemma plní RAM navzdory volné VRAM, **na RTX 4060 Ti 16 GB** — stejná karta. | duplikát #10040 |
| [#8654](https://github.com/ollama/ollama/issues/8654) | „Available memory check should be disabled when mmap is in use" | otevřené od 1/2025 |
| [#4895](https://github.com/ollama/ollama/issues/4895) + [PR #6854](https://github.com/ollama/ollama/pull/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`.