nanobot: 2026-09-14 07:24:46

This commit is contained in:
lachtan
2026-09-14 07:24:46 +02:00
parent 5e98beca1f
commit bd14485ce3
7 changed files with 569 additions and 150 deletions

View File

@@ -0,0 +1,278 @@
# 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`.