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:
@@ -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, 3–7 punktów) |
|
||||
| `manual` | Stała prędkość 30–100% |
|
||||
| `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 |
|
||||
Reference in New Issue
Block a user