10 KiB
Архитектура
Обзор
┌─ локальная машина (Windows / Linux) ─────────────────────────┐
│ gpu-rent CLI │
│ up / stop / status / doctor / hold │
│ tunnel / open — SSH localhost:17801 → VM :7801 │
│ state ~/.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: ноут можно закрыть.
Слои 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, autocomplete.md, models.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 туннеля (на ноутбуке)
Работает только пока открыт туннель:
- Refresh IAM-токена (TTL 24 ч, как у preemptible).
EXPIRED→ unshelve → переоткрыть туннель.ERROR/ нет GPU → выход, не бесконечный recreate.- Туннель мёртв при ACTIVE → reconnect.
Ctrl+C здесь закрывает туннель, не вызывает stop.
Teardown (gpu-rent stop)
Compute удаляется через OpenStack без SSH. Optional pull Output — только если VM ещё отвечает по SSH:
- Если
PULL_OUTPUTи SSH жив — забрать новые файлы в./Output(--no-pullпропускает). - Закрыть туннель этого процесса, если открыт.
- Удалить compute. Volumes оставить.
- Дождаться исчезновения сервера.
- Удалить floating IP (
KEEP_FLOATING_IP=false). - 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(systemdswarmui). - Туннель:
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) |
| Конфиг | %USERPROFILE%\.gpu-rent\.env |
| Лицензия | MIT |