- Updated the project structure to store configuration files (.env, models.yaml, extensions.yaml) in the project root instead of the user's home directory. - Enhanced the setup process to automatically copy example files to the project root on first run. - Implemented a migration function to transfer legacy configuration files from the user's home directory to the new project structure. - Revised documentation to reflect changes in file locations and setup instructions. - Improved code readability and maintainability by refactoring path management functions.
174 lines
10 KiB
Markdown
174 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`, `tunnel`, `open`, `status`, `stop`, `destroy`, `logs`, `ssh`, `doctor`, `hold`, `seed-*`, `push-models`, `pull-output`, `resize-data`, `dry-run` |
|
||
| `config` | `.env`, валидация, пути к ключам |
|
||
| `state` | JSON сессии: ids, фаза, timestamps |
|
||
| `os_client` | `openstacksdk`, refresh IAM-токена |
|
||
| `inventory` | Flavors/images в сегменте, квоты, **фоллбек flavor** |
|
||
| `bootstrap` | Идемпотентный first-boot; после успеха — snapshot boot volume |
|
||
| `doctor` | Preflight без mutating compute |
|
||
| `civitai_seed` | Манифест + Civitai API на `.red` |
|
||
| `models_push` | SFTP `./Models`, `./Wildcards`, `./CustomWorkflows` |
|
||
| `output_pull` | Опциональный SFTP с VM `Output/` |
|
||
| `git_seed` | Clone `extensions.yaml` |
|
||
| `autocomplete_seed` | Word-list + Settings.fds |
|
||
| `notify` | Toast/звук при backend Idle |
|
||
| `tunnel` | paramiko / sshtunnel, порт **17801** |
|
||
| `watchdog` | EXPIRED → unshelve, пока туннель жив |
|
||
| `idle_killer` | systemd на VM + hold-файл + «качалка занята» |
|
||
| `reconcile` | Сироты по state и тегу `gpu-rent` |
|
||
|
||
Каталог сервисов — из 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_…`.
|
||
|
||
## Стейт-машина
|
||
|
||
```
|
||
idle
|
||
│ gpu-rent up
|
||
▼
|
||
provisioning → bootstrapping → seeding_extensions → seeding_autocomplete → seeding_models → waiting_ui
|
||
│
|
||
▼
|
||
ready_cloud ← compute жив, idle-killer вооружён
|
||
│
|
||
│ gpu-rent tunnel (опционально, пока ноут онлайн)
|
||
▼
|
||
ready_tunneled ← localhost:17801
|
||
│
|
||
│ хостер: EXPIRED
|
||
▼
|
||
restoring (unshelve) → ready_cloud / ready_tunneled
|
||
│
|
||
│ stop | idle-killer | destroy
|
||
▼
|
||
stopping → idle
|
||
```
|
||
|
||
`ready_cloud` ≠ Nova `ACTIVE`. ACTIVE бывает раньше SSH и UI.
|
||
|
||
## 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` — абсолютное продление. `--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, Typer, Rich, questionary, dotenv |
|
||
| SSH | paramiko + sshtunnel (Windows без системного `ssh -L`) |
|
||
| Конфиг | `<repo>/.env` + runtime в `<repo>/.gpu-rent/` |
|
||
| Лицензия | MIT |
|