278 lines
12 KiB
Markdown
278 lines
12 KiB
Markdown
# 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`
|
||
54–126 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`. |