- Added `ollama-models.yaml` to .gitignore and implemented logic to copy it in gpu-rent.ps1 and gpu-rent.sh. - Enhanced env.example to include new variables for LLM runtime options and local watchdog configuration. - Updated CLI commands to support LLM options during setup and execution, including new flags for Ollama and llama.cpp. - Improved documentation in cli.md and README.md to reflect changes in LLM integration and local watchdog functionality. - Adjusted architecture and decisions documentation to clarify the role of LLMs and local watchdog in the system.
176 lines
10 KiB
Markdown
176 lines
10 KiB
Markdown
# Архитектура
|
||
|
||
## Обзор
|
||
|
||
```
|
||
┌─ локальная машина (Windows / Linux) ─────────────────────────┐
|
||
│ gpu-rent CLI │
|
||
│ up / stop / status / doctor / hold │
|
||
│ tunnel / open — SSH localhost:17801 → VM :7801 │
|
||
│ state <repo>/.gpu-rent/state.json │
|
||
│ │
|
||
│ браузер / MCP / curl API → http://127.0.0.1:17801 │
|
||
│ локальный SwarmUI → http://127.0.0.1:7801 (не трогаем)
|
||
└───────────────────────────────┬──────────────────────────────┘
|
||
│ SSH :22 и OpenStack API
|
||
▼
|
||
┌─ Selectel, сегмент пула (например ru-7a) ────────────────────┐
|
||
│ GPU VM (tag preemptible + gpu-rent) │
|
||
│ SwarmUI : 127.0.0.1:7801 │
|
||
│ idle-killer: очередь / hold / качалка в UI → delete self │
|
||
│ application credential (compute delete/shelve only) │
|
||
│ │
|
||
│ boot volume (network) ОС + NVIDIA + SwarmUI нативно + snapshot │
|
||
│ data volume (network) Models, Output, Data, workflows │
|
||
│ floating IP только SSH, удаляется на stop │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
Два сетевых диска. После удаления VM boot-диск снова указывают как `--volume` при create.
|
||
|
||
Разделение **жизни GPU** и **локального туннеля** — следствие [decisions.md](decisions.md): ноут можно закрыть.
|
||
|
||
## Слои CLI
|
||
|
||
| Модуль | Ответственность |
|
||
| --- | --- |
|
||
| `cli` | Typer: `up`, `setup`, `tunnel`, `open`, `status`, `stop`, `destroy`, `logs`, `ssh`, `doctor`, `hold`, `seed-*`, `push` / `pull-output`, `resize-data`, `watchdog`, `dry-run` |
|
||
| `config` / `varsfile` | `.env` + `gpu-rent.vars`, пути |
|
||
| `state` | JSON сессии: ids, фаза, timestamps |
|
||
| `os_client` / `cloud` / `pools` | openstacksdk, ресурсы, скан пулов |
|
||
| `inventory` | Flavors/images, квоты, фоллбек flavor |
|
||
| `session` | `cmd_up` / `cmd_stop` / adopt |
|
||
| `bootstrap` + `remote/bootstrap.sh` | Идемпотентный first-boot; light без apt |
|
||
| `provision` | extensions, autocomplete, civitai seed, push, idle-killer arm, start SwarmUI |
|
||
| `doctor` | Preflight без mutating compute |
|
||
| `sync_files` | SFTP `Models` / Wildcards / workflows / Output |
|
||
| `notify` | Toast/звук при backend Idle |
|
||
| `tunnel` | sshtunnel + Nova EXPIRED watchdog |
|
||
| `local_watchdog` | Опциональный локальный тик → stop при unclean exit |
|
||
| `llm_runtime` / `setup_wizard` | Opt-in Ollama/llama.cpp + `ollama-models.yaml` |
|
||
| `idle_killer` / `hold` | systemd на VM + hold-файл |
|
||
| `ready` / `snapshot` | Idle backend + boot snapshot |
|
||
|
||
Каталог сервисов — из Keystone, не из выдуманного `api.selectel.ru/v3/`.
|
||
|
||
## Два диска
|
||
|
||
### Boot volume
|
||
|
||
Сетевой, тот же сегмент, что VM. Первый раз — GPU-optimized образ **без Docker**. `delete_on_termination=false`. Локальный boot запрещён (preemptible сотрёт ОС).
|
||
|
||
После bootstrap: NVIDIA из образа, `/opt/swarmui` + systemd, unit idle-killer, application credential в `/root/.gpu-rent/` (mode 600).
|
||
|
||
После **первого** успешного `waiting_ui` + backend Idle: один snapshot boot-диска `gpu-rent-boot-ok` (если ещё нет). Следующий create VM может идти из snapshot, не из сырого GPU-образа. Старый snapshot не плодить каждый `up`.
|
||
|
||
### Data volume
|
||
|
||
Второй BDM при create (не attach после ACTIVE). FS один раз, маркер `/mnt/swarm_data/.gpu-rent-ready`. Первый seed: [extensions.md](extensions.md), [autocomplete.md](autocomplete.md), [models.md](models.md), [local-folders.md](local-folders.md).
|
||
|
||
| На хосте (data volume) | В дереве SwarmUI (`/opt/swarmui`, bind) |
|
||
| --- | --- |
|
||
| `/mnt/swarm_data/Models` | `/opt/swarmui/Models` |
|
||
| `/mnt/swarm_data/Output` | `/opt/swarmui/Output` |
|
||
| `/mnt/swarm_data/Data` | `/opt/swarmui/Data` |
|
||
| `/mnt/swarm_data/dlbackend` | `/opt/swarmui/dlbackend` |
|
||
| `/mnt/swarm_data/Extensions` | `/opt/swarmui/src/Extensions` |
|
||
| `/mnt/swarm_data/DLNodes` | `/opt/swarmui/src/BuiltinExtensions/ComfyUIBackend/DLNodes` |
|
||
| `/mnt/swarm_data/CustomWorkflows` | `/opt/swarmui/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows` |
|
||
|
||
`mkfs` только если нет маркера **и** `blkid` подтвердил пустое устройство по serial/by-id. Не хардкодить `scsi-0Selectel_Volume_…`.
|
||
|
||
## Стейт-машина
|
||
|
||
Фазы в `state.json` (код пишет только эти):
|
||
|
||
```
|
||
idle
|
||
│ gpu-rent up
|
||
▼
|
||
provisioning ← create volumes / server
|
||
│
|
||
▼
|
||
bootstrapping ← SSH + bootstrap + seed (пока не ready)
|
||
│
|
||
▼
|
||
ready_cloud ← compute жив, idle-killer вооружён
|
||
│
|
||
│ gpu-rent tunnel (или up с туннелем)
|
||
▼
|
||
ready_tunneled ← localhost:17801
|
||
│
|
||
│ stop | idle-killer | local-watchdog | destroy
|
||
▼
|
||
idle
|
||
```
|
||
|
||
Подшаги seed (extensions / autocomplete / models) идут внутри `bootstrapping`, отдельными фазами в state не пишутся. `ready_cloud` ≠ Nova `ACTIVE` (ACTIVE бывает раньше SSH и UI).
|
||
|
||
На EXPIRED туннель сам делает unshelve → снова `bootstrapping`/`ready_*`.
|
||
|
||
## Idle-killer (на VM)
|
||
|
||
Пока очередь SwarmUI пуста дольше **30 минут**, скрипт удаляет **этот** compute через OpenStack (диски не трогать). Открытый браузер без джобы жизнь **не** продлевает.
|
||
|
||
Killer молчит:
|
||
|
||
- clone расширений, Civitai-seed, push локальных папок;
|
||
- первые **45 минут** после ACTIVE или unshelve;
|
||
- пока backend/ComfyUI ещё не Idle;
|
||
- пока существует hold: файл `/mnt/swarm_data/.gpu-rent-hold-until` с unix ts (пишет `gpu-rent hold`);
|
||
- пока SwarmUI качает модель в UI (Model Downloader / активный download — точный JSON на spike). Нет сигнала — пользователь жмёт `hold`.
|
||
|
||
`gpu-rent hold` без аргументов = +`IDLE_MINUTES` от сейчас. `--minutes 90` — hold до now+90 мин (заменяет предыдущий, не складывает). `--until` ISO опционально. `hold --clear` снимает.
|
||
|
||
Потом счётчик 30 минут пустой очереди.
|
||
|
||
Почему не `shutdown -h now`: у Selectel останов из гостя не обязан снять GPU с биллинга. Нужен API delete/shelve.
|
||
|
||
Почему не только локальный CLI: ноут спит — процесса нет — GPU продолжает тарифицироваться.
|
||
|
||
Учётные данные на VM: application credential (или роль без `compute:create`), только delete/shelve. Файл не попадает в Output/Models. Истечение кредов = killer слеп; тогда спасает `gpu-rent status` с ноутбука.
|
||
|
||
Дефолты: `IDLE_MINUTES=30`, `IDLE_GRACE_MINUTES=45`.
|
||
|
||
## Watchdog туннеля (на ноутбуке)
|
||
|
||
Работает только пока открыт туннель:
|
||
|
||
1. Refresh IAM-токена (TTL 24 ч, как у preemptible).
|
||
2. `EXPIRED` → unshelve → переоткрыть туннель.
|
||
3. `ERROR` / нет GPU → выход, не бесконечный recreate.
|
||
4. Туннель мёртв при ACTIVE → reconnect.
|
||
|
||
`Ctrl+C` здесь закрывает туннель, **не** вызывает `stop`.
|
||
|
||
## Teardown (`gpu-rent stop`)
|
||
|
||
Compute удаляется через OpenStack без SSH. Optional pull Output — только если VM ещё отвечает по SSH:
|
||
|
||
1. Если `PULL_OUTPUT` и SSH жив — забрать новые файлы в `./Output` (`--no-pull` пропускает).
|
||
2. Закрыть туннель этого процесса, если открыт.
|
||
3. Удалить compute. Volumes оставить.
|
||
4. Дождаться исчезновения сервера.
|
||
5. Удалить floating IP (`KEEP_FLOATING_IP=false`).
|
||
6. State → `idle`, сохранить volume ids.
|
||
|
||
`destroy` — то же + диски после `--i-understand-data-loss`.
|
||
|
||
Reconcile: сервер с тегом `gpu-rent` есть, локального процесса нет — это норма (`ready_cloud`). Сирота = нет тега в state и наоборот; `status` показывает «жив, туннеля нет, idle-killer: …».
|
||
|
||
## Сеть на VM
|
||
|
||
- Private net + subnet + router — один раз на пул, переиспользовать.
|
||
- SG: ingress TCP/22 с IP оператора. **Не** открывать 7801 наружу.
|
||
- SwarmUI: `--host 127.0.0.1 --port 7801` (systemd `swarmui`).
|
||
- Туннель: `127.0.0.1:17801` → `127.0.0.1:7801` на VM.
|
||
|
||
## Стек
|
||
|
||
| Слой | Выбор |
|
||
| --- | --- |
|
||
| Python 3.11+ | openstacksdk, paramiko, sshtunnel, Typer, Rich, dotenv, httpx, pyyaml |
|
||
| SSH | paramiko + sshtunnel (Windows без системного `ssh -L`) |
|
||
| Конфиг | `<repo>/.env` + `gpu-rent.vars` + runtime в `<repo>/.gpu-rent/` |
|
||
| Лицензия | MIT |
|