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
+149
View File
@@ -0,0 +1,149 @@
# Control plane — jeden plik `.env`
**Data:** 2026-07-05
**Status:** wdrożone
gpu-fan (agent NVML) i Server UI (panel stacków) używają **jednego** pliku credentials.
## Kanoniczne ścieżki
| Środowisko | Plik |
|------------|------|
| Produkcja (systemd) | `/opt/control-plane/.env` |
| Dev w repo | `stacks/control-plane/.env` |
| Szablon | `stacks/control-plane/.env.example` |
Oba serwisy systemd:
```ini
EnvironmentFile=-/opt/control-plane/.env
```
- `server-ui.service`
- `gpu-fan.service`
## Zmienne
| Zmienna | Opis |
|---------|------|
| `API_KEY` | **Wspólny** klucz: auth panelu Server UI (`X-API-Key`) + auth agenta gpu-fan (proxy `/api/gpu-fan/*`) |
| `SERVER_UI_HOST`, `SERVER_UI_PORT` | Bind Server UI (domyślnie `0.0.0.0:8091`) |
| `REPO_ROOT` | Ścieżka do `ubuntu-bare-metal` (wymagane dla Docker Server UI) |
| `DOCKER_GID` | GID grupy docker (Docker install) |
| `GPU_FAN_AGENT_URL` | URL agenta z perspektywy Server UI (`http://127.0.0.1:18090` native, `http://host.docker.internal:18090` Docker) |
| `GPU_FAN_API_HOST`, `GPU_FAN_API_PORT` | Bind agenta gpu-fan (domyślnie `127.0.0.1:18090`) |
| `CURVE_PATH`, `POLL_INTERVAL`, `GPU_INDEX` | Konfiguracja NVML / krzywej wentylatorów |
**Usunięte:** `GPU_FAN_AGENT_KEY` — duplikat `API_KEY`.
## Ładowanie w Pythonie
[`stacks/control-plane/env_loader.py`](../stacks/control-plane/env_loader.py) — `load_control_plane_env(stack_dir)`:
**Produkcja** (`stack_dir` pod `/opt/`): tylko `/opt/control-plane/.env` + `os.environ` (systemd).
**Dev** (repo `stacks/*`): tylko `stacks/control-plane/.env` + `os.environ`.
**Nie ładowane:** `stacks/server-ui/.env`, `/opt/server-ui/.env` (legacy — `API_KEY` usuwany przez `setup-control-plane-env.sh`).
Kolejność priorytetu: pliki env → **`os.environ` wygrywa** (systemd `EnvironmentFile`).
`api_key_source(stack_dir, values)` — log przy starcie Server UI (bez ujawniania sekretu).
Produkcja: `env_loader.py` kopiowany do `/opt/control-plane/env_loader.py` przez `setup-control-plane-env.sh`.
## Instalacja / migracja
```bash
sudo bash stacks/server-ui/scripts/setup-control-plane-env.sh
sudo systemctl daemon-reload
sudo systemctl restart gpu-fan server-ui
```
Skrypt:
- tworzy `/opt/control-plane/.env` z example jeśli brak
- **migruje** z `/opt/server-ui/.env` i `/opt/gpu-fan/.env` (backup `.env.bak.<timestamp>`)
- generuje `API_KEY` jeśli brak lub `change-me`
- **sync** `stacks/control-plane/.env` z produkcją (ten sam `API_KEY`)
- **usuwa** `API_KEY` / `GPU_FAN_AGENT_KEY` z legacy `stacks/server-ui/.env`
- wypisuje gotowy URL z kluczem (`print-api-key-instructions.sh`)
Instrukcja dla użytkownika po instalacji:
```bash
bash stacks/server-ui/scripts/show-api-key.sh
```
Pełna instalacja:
```bash
sudo bash stacks/server-ui/scripts/install-control-plane.sh
```
## Weryfikacja
```bash
bash stacks/server-ui/scripts/show-api-key.sh
API_KEY=$(sudo grep ^API_KEY= /opt/control-plane/.env | cut -d= -f2)
# Test klucza w panelu
curl -s -X POST http://127.0.0.1:8091/api/auth/verify \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"${API_KEY}\"}"
# Agent bezpośrednio
curl -s http://127.0.0.1:18090/api/status -H "X-API-Key: ${API_KEY}"
# Proxy przez Server UI
curl -s http://127.0.0.1:8091/api/gpu-fan/health -H "X-API-Key: ${API_KEY}"
```
Oczekiwane: JSON z danymi / `"ok": true`.
## Docker Server UI
[`stacks/server-ui/docker-compose.yml`](../stacks/server-ui/docker-compose.yml):
```yaml
env_file:
- ../control-plane/.env
```
## Auth flow
```mermaid
sequenceDiagram
participant Browser
participant ServerUI
participant GpuFanAgent
Browser->>ServerUI: X-API-Key: API_KEY
ServerUI->>GpuFanAgent: X-API-Key: API_KEY (ten sam)
GpuFanAgent-->>ServerUI: status JSON
ServerUI-->>Browser: proxy response
```
## Poza scope
Sekrety workloadów Docker (`LOCALAI_API_KEY`, `HF_TOKEN`, `INITIAL_ADMIN_PASSWORD`, …) pozostają w `stacks/<nazwa>/.env` — edycja portów przez Server UI bez zmian.
## Pliki zmienione (implementacja)
- `stacks/control-plane/``.env.example`, `env_loader.py`
- `stacks/server-ui/scripts/setup-control-plane-env.sh`
- `server-ui.service`, `gpu-fan.service`
- `server-ui/app.py`, `gpu_fan_proxy.py`, `gpu-fan/fan_daemon.py`, `gpu-fan/app.py`
- Instalatory: `install-control-plane.sh`, `install.sh`, `install-docker.sh`, `gpu-fan/install.sh`
- Skrypty dev: `start.sh`, `deploy-gpu-fan-fix.sh`, `restart-stack.sh`, `enable-lan.sh`, `status.sh`
## Troubleshooting — Invalid API key
1. Klucz tylko z `/opt/control-plane/.env` (`sudo grep ^API_KEY= …`)
2. UI: **Zapisz****Sprawdź klucz**
3. Sync: `sudo bash stacks/server-ui/scripts/setup-control-plane-env.sh`
4. Tutorial: [`manual-tutorial/04a-api-key.md`](../manual-tutorial/04a-api-key.md)
## Legacy
Stare pliki `/opt/server-ui/.env` i `/opt/gpu-fan/.env` nie są już czytane przez systemd. Po migracji można je usunąć (backup w `.env.bak.*`).