- Updated `env.example` and `gpu-rent.vars.example` to include new variables for LLM runtime and SwarmUI options. - Refactored CLI commands to support interactive selection of LLM runtime and workload type (SwarmUI, LLM, or both). - Improved access link generation to handle cases where SwarmUI is disabled, providing clearer user feedback. - Enhanced provisioning logic to conditionally bootstrap SwarmUI based on user configuration, allowing for LLM-only setups. - Updated documentation across multiple files to reflect changes in LLM integration, CLI usage, and configuration management.
10 KiB
Архитектура
Обзор
┌─ локальная машина (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 / LLM busy → delete this server │
│ app cred: DELETE/GET только этот server_id (fail closed) │
│ optional: Ollama :11434 / llama.cpp :8080 (туннель 17811/12)│
│ │
│ 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 / term / prompts |
Typer + цветной лог + нумерованные меню |
config / varsfile |
.env + gpu-rent.vars, пути |
state |
JSON сессии: ids, фаза, timestamps |
os_client / cloud / pools |
openstacksdk, ресурсы, скан пулов |
inventory / ux |
Flavors, квоты; preview / ServerPlan (flavor/disk/spot) |
session |
cmd_up / cmd_stop / adopt |
bootstrap + remote/bootstrap.sh |
Идемпотентный first-boot; light без apt |
provision |
extensions, autocomplete, civitai seed, LLM, push, idle-killer arm, start SwarmUI |
capture |
Инвентарь VM → merge ссылок в локальные манифесты |
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 / llamacpp-models.yaml |
idle_killer / hold |
systemd на VM + hold-файл |
ready / snapshot |
Idle backend + boot snapshot |
access_card |
URL / MCP panel после ready |
Каталог сервисов — из 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_….
Стейт-машина
Фазы в 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 туннеля (на ноутбуке)
Работает только пока открыт туннель:
- 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, sshtunnel, Typer, Rich, dotenv, httpx, pyyaml |
| SSH | paramiko + sshtunnel (Windows без системного ssh -L) |
| Конфиг | <repo>/.env + gpu-rent.vars + runtime в <repo>/.gpu-rent/ |
| Лицензия | MIT |