Update README and CLI documentation for tunnel functionality

- Enhanced README.md to clarify the `gpu-rent up` command, including default tunnel behavior and options for cloud-only operation.
- Updated cli.md to reflect changes in the `gpu-rent up` command, detailing the new `--no-tunnel` option and its implications.
- Revised decisions.md to explain the separation of `up` and `tunnel` commands, emphasizing the default tunnel opening after `up`.
- Improved doctor.py to provide clearer instructions regarding tunnel creation and cloud-only usage.
- Adjusted notify.py to reflect the updated command usage for accessing the SwarmUI.
This commit is contained in:
Leonid Pershin
2026-08-21 04:43:33 +03:00
parent c7a56bdd56
commit ec42830579
6 changed files with 48 additions and 12 deletions
+7 -4
View File
@@ -13,8 +13,9 @@
.\gpu-rent.ps1 doctor # preflight .\gpu-rent.ps1 doctor # preflight
.\gpu-rent.ps1 flavors # что выберет фоллбек .\gpu-rent.ps1 flavors # что выберет фоллбек
.\gpu-rent.ps1 dry-run # план без create .\gpu-rent.ps1 dry-run # план без create
.\gpu-rent.ps1 up --yes # GPU + SwarmUI + seed .\gpu-rent.ps1 up --yes # GPU + SwarmUI + seed + туннель :17801
.\gpu-rent.ps1 tunnel # UI на :17801 (Ctrl+C ≠ stop) .\gpu-rent.ps1 up --yes --no-tunnel # только облако
.\gpu-rent.ps1 tunnel --open # снова открыть UI, если туннель закрыли
.\gpu-rent.ps1 hold # отложить idle-killer .\gpu-rent.ps1 hold # отложить idle-killer
.\gpu-rent.ps1 stop # гасить compute, диски оставить .\gpu-rent.ps1 stop # гасить compute, диски оставить
``` ```
@@ -38,8 +39,10 @@ Unix: `./gpu-rent.sh …` (один раз `chmod +x gpu-rent.sh`).
| --- | --- | | --- | --- |
| `doctor` | Keystone, квота GPU, flavor, Civitai, манифесты | | `doctor` | Keystone, квота GPU, flavor, Civitai, манифесты |
| `flavors` | Живой список по `FLAVOR_PREFERENCE` | | `flavors` | Живой список по `FLAVOR_PREFERENCE` |
| `up` / `up --yes` / `up --no-spot` / `up --flavor ID` | Поднять GPU | | `up` / `up --yes` | Поднять GPU + туннель :17801 (браузер). Ctrl+C = туннель off |
| `tunnel` / `tunnel --open` | SSH-проброс; watchdog на EXPIRED | | `up --no-tunnel` | Только облако |
| `up --no-spot` / `up --flavor ID` | Обычный тариф / явный flavor |
| `tunnel` / `tunnel --open` | Повторный проброс; watchdog на EXPIRED |
| `open` | Браузер на 17801 | | `open` | Браузер на 17801 |
| `hold` / `hold --minutes 90` / `hold --clear` | Пауза idle-killer | | `hold` / `hold --minutes 90` / `hold --clear` | Пауза idle-killer |
| `status` | Nova + диск + killer | | `status` | Nova + диск + killer |
+3 -2
View File
@@ -8,10 +8,11 @@
| --- | --- | | --- | --- |
| `gpu-rent flavors` | Скан `SCAN_POOLS` (ru-6 multizone…) × `FLAVOR_PREFERENCE`, затем список в текущем `OS_REGION_NAME` | | `gpu-rent flavors` | Скан `SCAN_POOLS` (ru-6 multizone…) × `FLAVOR_PREFERENCE`, затем список в текущем `OS_REGION_NAME` |
| `gpu-rent doctor` | Preflight **без** create: Keystone, квота GPU, flavor в AZ, диски, Civitai token+`.red`, манифесты, SSH-ключ. Код выхода ≠ 0, если сессию нельзя начать | | `gpu-rent doctor` | Preflight **без** create: Keystone, квота GPU, flavor в AZ, диски, Civitai token+`.red`, манифесты, SSH-ключ. Код выхода ≠ 0, если сессию нельзя начать |
| `gpu-rent up` | Preflight → create/unshelve (с фоллбеком flavor) → extensions → autocomplete → Civitai seed → push локальных папок → ждать UI → toast/звук | | `gpu-rent up` / `up --yes` | Preflight → create/unshelve → bootstrap → **туннель** `localhost:17801` + URL панели; Ctrl+C закрывает туннель |
| `gpu-rent up --no-tunnel` | Только облако + bootstrap, без локального проброса |
| `gpu-rent up --no-spot` | Обычный (не preemptible) сервер | | `gpu-rent up --no-spot` | Обычный (не preemptible) сервер |
| `gpu-rent up --flavor … --yes` | Без вопросов. `--flavor` бьёт список фоллбека. `--yes` без `--flavor` берёт первый доступный из `FLAVOR_PREFERENCE` | | `gpu-rent up --flavor … --yes` | Без вопросов. `--flavor` бьёт список фоллбека. `--yes` без `--flavor` берёт первый доступный из `FLAVOR_PREFERENCE` |
| `gpu-rent tunnel` | SSH-проброс `localhost:17801` → VM `:7801`. Блокируется. Ctrl+C = закрыть туннель, GPU **оставить** | | `gpu-rent tunnel` | Повторный SSH-проброс, если `up --no-tunnel` или туннель уже закрыли. Ctrl+C = закрыть туннель, GPU **оставить** |
| `gpu-rent tunnel --open` | Туннель + сразу открыть браузер на 17801 | | `gpu-rent tunnel --open` | Туннель + сразу открыть браузер на 17801 |
| `gpu-rent open` | Открыть браузер на `http://127.0.0.1:17801`. Туннель уже должен слушать порт | | `gpu-rent open` | Открыть браузер на `http://127.0.0.1:17801`. Туннель уже должен слушать порт |
| `gpu-rent status` | State + Nova + диск used/free + ₽/час + сколько до 24 ч preempt + killer/hold. Туннель не обязателен | | `gpu-rent status` | State + Nova + диск used/free + ₽/час + сколько до 24 ч preempt + killer/hold. Туннель не обязателен |
+1 -1
View File
@@ -19,7 +19,7 @@
| Floating IP | `KEEP_FLOATING_IP=false`: выделить на `up`, удалить на `stop` | | Floating IP | `KEEP_FLOATING_IP=false`: выделить на `up`, удалить на `stop` |
| Idle-killer | 30 минут пустой очереди генерации. Открытый браузер не продлевает жизнь | | Idle-killer | 30 минут пустой очереди генерации. Открытый браузер не продлевает жизнь |
| Льгота после boot | Killer молчит во время clone/seed/push, hold, качалки в UI SwarmUI, пока backend не Idle, и 45 мин после ACTIVE/unshelve | | Льгота после boot | Killer молчит во время clone/seed/push, hold, качалки в UI SwarmUI, пока backend не Idle, и 45 мин после ACTIVE/unshelve |
| `up` / `tunnel` | Разделены: `up` только облако, `tunnel` отдельно. Ctrl+C на туннеле не удаляет VM | | `up` / `tunnel` | `up` по умолчанию после ready открывает туннель `:17801`, печатает URL и ждёт. `--no-tunnel` — только облако. Ctrl+C на туннеле GPU не гасит (`stop` отдельно). Команда `tunnel` остаётся для повторного входа |
| Data-диск | Старт **100 GB**, рост через resize вверх (вниз Selectel не умеет) | | Data-диск | Старт **100 GB**, рост через resize вверх (вниз Selectel не умеет) |
| SSH | CLI генерирует `<repo>/.gpu-rent/id_ed25519` без passphrase и сам регистрирует keypair | | SSH | CLI генерирует `<repo>/.gpu-rent/id_ed25519` без passphrase и сам регистрирует keypair |
| Локальные файлы | Всё в корне репозитория: `.env`, `models.yaml`, `extensions.yaml`; runtime (`state.json`, lock, SSH) в `<repo>/.gpu-rent/`. Не `%USERPROFILE%\.gpu-rent` | | Локальные файлы | Всё в корне репозитория: `.env`, `models.yaml`, `extensions.yaml`; runtime (`state.json`, lock, SSH) в `<repo>/.gpu-rent/`. Не `%USERPROFILE%\.gpu-rent` |
+34 -2
View File
@@ -75,6 +75,7 @@ def _print_checks(checks) -> int:
_print_next_steps(failed) _print_next_steps(failed)
return 1 return 1
console.print("\n[green]Можно идти дальше.[/green] Дальше: gpu-rent up --yes") console.print("\n[green]Можно идти дальше.[/green] Дальше: gpu-rent up --yes")
console.print(" (туннель :17801 по умолчанию; только облако: --no-tunnel)")
console.print("Чеклист spike: docs/spike-notes.md") console.print("Чеклист spike: docs/spike-notes.md")
return 0 return 0
@@ -290,8 +291,18 @@ def up(
flavor: Optional[str] = typer.Option(None, "--flavor", help="Flavor id, без фоллбека"), flavor: Optional[str] = typer.Option(None, "--flavor", help="Flavor id, без фоллбека"),
yes: bool = typer.Option(False, "--yes", help="Без вопросов"), yes: bool = typer.Option(False, "--yes", help="Без вопросов"),
adopt: bool = typer.Option(False, "--adopt", help="Подхватить тег gpu-rent без state"), adopt: bool = typer.Option(False, "--adopt", help="Подхватить тег gpu-rent без state"),
no_tunnel: bool = typer.Option(
False,
"--no-tunnel",
help="Только облако + bootstrap, без локального туннеля",
),
open_browser: bool = typer.Option(
True,
"--open/--no-open",
help="После туннеля открыть браузер на 17801 (по умолчанию да)",
),
) -> None: ) -> None:
"""Create/unshelve GPU и bootstrap SwarmUI (без Docker).""" """Create/unshelve GPU, bootstrap SwarmUI, по умолчанию туннель на :17801."""
try: try:
checks = run_doctor() checks = run_doctor()
code = _print_checks(checks) code = _print_checks(checks)
@@ -302,7 +313,7 @@ def up(
def confirm(msg: str) -> bool: def confirm(msg: str) -> bool:
return typer.confirm(msg) return typer.confirm(msg)
cmd_up( state = cmd_up(
cfg, cfg,
no_spot=no_spot, no_spot=no_spot,
flavor=flavor, flavor=flavor,
@@ -311,6 +322,27 @@ def up(
confirm=confirm, confirm=confirm,
log=lambda m: console.print(m), log=lambda m: console.print(m),
) )
if no_tunnel:
console.print(
f"[bold]готово[/bold] (без туннеля). "
f"UI: gpu-rent tunnel --open | stop: gpu-rent stop"
)
if state.floating_ip:
console.print(f"FIP {state.floating_ip}")
return
if not state.floating_ip:
raise GpuRentError("нет floating IP после up — туннель не открыть")
console.print(
f"[bold]панель[/bold] http://127.0.0.1:{cfg.swarmui_local_port} "
"(Ctrl+C = закрыть туннель, GPU жив → gpu-rent stop)"
)
run_tunnel(
cfg,
state.floating_ip,
open_browser=open_browser,
log=lambda m: console.print(m),
)
except GpuRentError as exc: except GpuRentError as exc:
_die(exc) _die(exc)
+2 -2
View File
@@ -363,8 +363,8 @@ def dry_run_plan(checks: list[Check]) -> list[str]:
f"idle-killer: {cfg.idle_minutes} мин пустой очереди, льгота {cfg.idle_grace_minutes} мин", f"idle-killer: {cfg.idle_minutes} мин пустой очереди, льгота {cfg.idle_grace_minutes} мин",
"₽: в API нет — смотри панель; диск 24/7 даже после stop", "₽: в API нет — смотри панель; диск 24/7 даже после stop",
f"туннель: localhost:{cfg.swarmui_local_port} -> VM :7801", f"туннель: localhost:{cfg.swarmui_local_port} -> VM :7801",
"gpu-rent up --yes создаст сеть/диски/compute и поставит SwarmUI (если doctor зелёный и квота GPU > 0)", "gpu-rent up --yes создаст GPU + SwarmUI и откроет туннель :17801 (Ctrl+C не гасит GPU)",
"после up: gpu-rent tunnel (Ctrl+C не гасит GPU)", "только облако без туннеля: gpu-rent up --yes --no-tunnel",
] ]
flavor = next((c.detail for c in checks if c.name == "flavor" and c.ok), None) flavor = next((c.detail for c in checks if c.name == "flavor" and c.ok), None)
if flavor: if flavor:
+1 -1
View File
@@ -74,7 +74,7 @@ def print_mcp_snippet(cfg: Config, log: Log) -> None:
log("}") log("}")
log("") log("")
log(f"SwarmUI на VM: 127.0.0.1:7801 (только через туннель)") log(f"SwarmUI на VM: 127.0.0.1:7801 (только через туннель)")
log(f"Локально: gpu-rent tunnel") log(f"Локально: уже в up (или gpu-rent tunnel)")
log(f"Браузер: gpu-rent open → http://127.0.0.1:{port}") log(f"Браузер: gpu-rent open → http://127.0.0.1:{port}")
log(f"API: http://127.0.0.1:{port}/API/") log(f"API: http://127.0.0.1:{port}/API/")
log(f"MCP: http://127.0.0.1:{port}/mcp") log(f"MCP: http://127.0.0.1:{port}/mcp")