Initial import: bare-metal stacks, Server UI, GPU fan, tutorials.

Infrastructure configs for GMKtec K11 (Docker, vLLM, LocalAI, ComfyUI,
control-plane, gpu-fan agent, Server UI with CLI/file explorer/GPU fan curve).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
tomasz-syn-grzegorza
2026-07-05 12:02:04 +00:00
commit 359afb3a59
153 changed files with 18169 additions and 0 deletions
@@ -0,0 +1,228 @@
# GPU Fan — Docker vs host (raport techniczny)
**Data:** 2026-07-04
**Stack:** `stacks/gpu-fan/`
**Host docelowy:** gmktec-k11, RTX 3090 Ti, Ubuntu headless
---
## 1. Executive summary
**GPU Fan musi działać na hoście jako usługa systemd (root), nie w Dockerze.**
Aplikacja steruje wentylatorami karty NVIDIA przez zapis do NVML (`nvmlDeviceSetFanSpeed_v2`, `nvmlDeviceSetFanControlPolicy`). Na kartach GeForce wymaga to uprawnień root i bezpośredniego dostępu do sterownika hosta. Repo nie zawiera Dockerfile ani compose dla tego stacku — jedyny wspierany model to `sudo scripts/install.sh``/opt/gpu-fan` + `gpu-fan.service`.
Docker jest teoretycznie możliwy (privileged container, host network, mount `/dev/nvidia*`), ale kruchy, nieutrzymywany i niezgodny z architekturą ubuntu-bare-metal (gpu-fan jako daemon sprzętowy obok workloadów AI w kontenerach).
---
## 2. Co robi aplikacja
| Komponent | Plik | Rola |
|-----------|------|------|
| Pętla sterowania | `fan_controller.py` | Odczyt temp/mocy, interpolacja krzywej, zapis prędkości wentylatorów |
| Web UI + API | `app.py` | FastAPI na porcie **8090**, wątek daemon NVML |
| UI statyczne | `static/index.html` | Wykres krzywej, status live, edycja trybu |
| Krzywa | `/etc/gpu-fan/curve.json` | Mapowanie temp °C → speed % |
### Tryby pracy
| Tryb | Zachowanie |
|------|------------|
| `curve` | Prędkość z krzywej JSON (interpolacja liniowa, 37 punktów) |
| `manual` | Stała prędkość 30100% |
| `auto` | Przywraca politykę drivera NVIDIA (`NVML_FAN_POLICY_TEMPERATURE_CONTINOUS_SW`) |
### API (port 8090)
| Endpoint | Metoda | Uwagi |
|----------|--------|-------|
| `/` | GET | Web UI |
| `/api/status` | GET | Metryki GPU + tryb |
| `/api/curve` | GET/PUT | Odczyt/zapis krzywej |
| `/api/mode` | POST | Zmiana trybu |
| `/api/reload` | POST | Przeładowanie `curve.json` (jak SIGHUP) |
Nagłówek `X-API-Key` wymagany gdy `GPU_FAN_HOST` ≠ localhost (domyślnie LAN bind `0.0.0.0`).
### Shutdown
Przy `SIGTERM` / `SIGINT` kontroler wywołuje `_restore_auto_policy()` przed `nvmlShutdown()` — wentylatory nie zostają w trybie manual po zatrzymaniu usługi.
---
## 3. Zależności sprzętowe i software
| Zależność | Wymagana | Uwagi |
|-----------|----------|-------|
| NVIDIA driver ≥ 520 | Tak | Testowane: 595-server-open |
| `nvidia-ml-py` (pynvml) | Tak | Jedyny interfejs sterowania w kodzie |
| `nvidia-smi` | Nie w kodzie | Tylko weryfikacja w dokumentacji; **nie ustawia** wentylatorów |
| Root (euid 0) | Tak | `app.py` kończy się błędem bez root (chyba że `DRY_RUN=true`) |
| `nvidia-persistenced` | Zalecane | `gpu-fan.service` After=/Wants= |
| IPMI | Nie | Brak referencji w kodzie |
| D-Bus | Nie | Brak referencji |
| X11 / nvidia-settings | Nie | Headless — celowo unikane |
| Python 3 + venv | Tak | FastAPI, uvicorn |
### Ścieżki produkcyjne
| Ścieżka | Zawartość |
|---------|-----------|
| `/opt/gpu-fan/` | Kod aplikacji (rsync z repo przez `install.sh`) |
| `/opt/control-plane/.env` | `API_KEY`, `GPU_FAN_API_*`, `CURVE_PATH`, … |
| `/etc/gpu-fan/curve.json` | Krzywa temp → speed |
| `/etc/systemd/system/gpu-fan.service` | Unit systemd |
**Uwaga:** `stacks/control-plane/.env` w repo ≠ `/opt/control-plane/.env``setup-control-plane-env.sh` migruje i synchronizuje.
---
## 4. Obecny model wdrożenia
```
repo stacks/gpu-fan/
│ sudo scripts/install.sh
/opt/gpu-fan/ ← kod + .venv + .env
/etc/gpu-fan/curve.json
/etc/systemd/system/gpu-fan.service
│ systemctl enable --now gpu-fan
Proces root: python app.py
├── wątek: fan_controller.run_loop() (co POLL_INTERVAL s)
└── uvicorn: 0.0.0.0:8090
```
Skrypty pomocnicze:
| Skrypt | Cel |
|--------|-----|
| `scripts/install.sh` | Instalacja produkcyjna |
| `scripts/enable-lan.sh` | `GPU_FAN_HOST=0.0.0.0`, API_KEY, restart |
| `scripts/start.sh` | Foreground debug (wymaga stop systemd) |
| `scripts/status.sh` | Diagnostyka portu/procesu |
| `scripts/self-test.sh` | Test krzywej, NVML read, API dry-run |
---
## 5. Analiza Docker — dlaczego nie
### Brak artefaktów w repo
- Brak `Dockerfile`, `compose.yaml`, profilu w `server-ui/stacks.yaml`
- Inne stacki GPU (ComfyUI, LocalAI, vLLM) używają Docker; gpu-fan jest wyjątkiem celowym
### Blokery techniczne
| Bloker | Szczegóły |
|--------|-----------|
| NVML write na GeForce | `nvmlDeviceSetFanSpeed_v2` wymaga root; kontenery GPU (`NVIDIA_DRIVER_CAPABILITIES=compute,utility`) nie gwarantują zapisu fan policy |
| Coupling do host driver | Wersja NVML w kontenerze musi pasować do kernel drivera hosta |
| Lifecycle | `docker kill` / crash kontenera może pominąć `_restore_auto_policy()` → wentylatory w manual |
| `nvidia-persistenced` | Daemon na hoście; kontener nie zarządza persystencją GPU |
| Privileged + host network | Minimalny „Docker” wyglądałby jak host install z dodatkową warstwą — bez korzyści |
### Hipotetyczny kontener (nie implementować)
Gdyby ktoś eksperymentował:
```yaml
# NIE WDRAŻAĆ — tylko dokumentacja ryzyka
privileged: true
network_mode: host
user: root
pid: host # opcjonalnie, nadal ryzykowne
volumes:
- /etc/gpu-fan:/etc/gpu-fan
devices:
- /dev/nvidia0
- /dev/nvidiactl
- /dev/nvidia-uvm
```
Nawet wtedy sukces nie jest gwarantowany na RTX 3090 Ti; repo nie będzie tego utrzymywać.
---
## 6. Współistnienie z Docker AI stacks
```
┌─────────────────────────────────────────────────┐
│ Host (gmktec-k11) │
│ │
│ gpu-fan.service (root, :8090) ──NVML──► GPU │
│ │
│ ┌─────────────┐ ┌──────────┐ ┌──────────┐ │
│ │ comfyui │ │ localai │ │ vllm │ │
│ │ :8188 │ │ :8070 │ │ :8000 │ │
│ └─────────────┘ └──────────┘ └──────────┘ │
│ Docker containers (GPU compute) │
└─────────────────────────────────────────────────┘
```
- ComfyUI/LocalAI obciążają GPU → temperatura rośnie → gpu-fan reaguje krzywą
- Zatrzymaj gpu-fan **nie** jest wymagane przed startem kontenerów AI
- Polityka GPU w Server UI (jeden duży workload) dotyczy LLM/SD, nie gpu-fan
- Firewall (NPMPlus): port 8090 nie powinien być publiczny; LAN + API key
Źródło: `manual-tutorial/06-gpu-fan-control.md` — „gpu-fan.service na hoście (NVML, root)”.
---
## 7. Checklist operacyjny (coding-agent)
### Instalacja / upgrade
- [ ] `nvidia-smi` działa
- [ ] `sudo scripts/install.sh` z katalogu `stacks/gpu-fan`
- [ ] `sudo scripts/enable-lan.sh` jeśli dostęp z LAN
- [ ] `sudo systemctl enable --now gpu-fan`
- [ ] `curl -s http://127.0.0.1:18090/api/status -H "X-API-Key: $(grep ^API_KEY= /opt/control-plane/.env | cut -d= -f2)"` → JSON z `temperature_c`
### Po zmianie kodu
```bash
sudo scripts/install.sh && sudo systemctl restart gpu-fan
```
### Diagnostyka
```bash
systemctl status gpu-fan
journalctl -u gpu-fan -f
scripts/status.sh
sudo scripts/status.sh --cleanup # tylko gdy port zajęty przez osierocony proces
```
### Czego nie robić
- Nie uruchamiać `start.sh` i systemd jednocześnie (port 8090)
- Nie pakować gpu-fan do Docker bez nowego ADR i testów na sprzęcie
- Nie edytować tylko `stacks/control-plane/.env` — produkcja czyta `/opt/control-plane/.env`
---
## 8. Rekomendacja
| Decyzja | Uzasadnienie |
|---------|--------------|
| **Zostaw na hoście (systemd)** | Wspierane, przetestowane, bezpieczny shutdown, zgodne z tutorial 06 |
| **Nie dodawaj Docker** | Brak wartości, wysokie ryzyko, duplikacja root access |
| **Dokumentacja użytkownika** | `docs/` — kroki instalacji i troubleshooting |
| **Ten raport** | `coding-agent/DOCKER-VS-HOST-REPORT.md` — odniesienie dla agentów |
---
## 9. Pliki źródłowe (indeks)
| Plik | Kluczowe fragmenty |
|------|-------------------|
| `app.py:161-164` | Wymóg root |
| `fan_controller.py:250-279` | NVML fan policy + speed write |
| `fan_controller.py:335-343` | Shutdown → auto policy |
| `gpu-fan.service` | User=root, After=nvidia-persistenced |
| `scripts/install.sh` | rsync → /opt/gpu-fan |
| `requirements.txt` | fastapi, uvicorn, nvidia-ml-py |