Adds bootstrap.sh implementing Stage 3 of the roadmap: it detects whether it runs on a plain Debian/Ubuntu host or directly on a Proxmox VE host. On a PVE host it provisions a Debian 12 LXC, installs the panel inside it via the existing deploy.sh, then creates a root@pam API token locally and registers the host as the panel's first node — no Proxmox password is ever transmitted. Also removes the insecure pvemanager_secure_password fallback for POSTGRES_PASSWORD in compose.yml, since every deploy path already generates a real .env before the stack starts.
18 KiB
План развития PVEmanager
Документ описывает шесть направлений развития панели, согласованных 24.07.2026. Два из них уже реализованы, четыре — предстоят. Для каждого этапа указаны цель, затрагиваемые файлы, шаги и критерии готовности, чтобы задачу мог взять любой разработчик без дополнительного контекста.
Статус
| # | Направление | Статус | Приоритет |
|---|---|---|---|
| 1 | Автосоздание API-токена при подключении ноды | ✅ Сделано | — |
| 2 | Графики метрик нод | ✅ Сделано (не проверено визуально) | — |
| 3 | Одна команда установки (curl | bash) |
🟡 Загрузчик готов (ветка B), не проверен на реальном PVE | 1 |
| 4 | Интеграция с Proxmox Backup Server | ⬜ Запланировано | 2 |
| 5 | Redis: вынос воркера задач и общий кэш | ⬜ Запланировано | 3 |
| 6 | Внешний стек метрик и логов (опционально) | ⬜ Запланировано | 4 |
Порядок выбран так: сначала дистрибуция (от неё зависит рост аудитории), затем крупная продуктовая фича, затем инфраструктурные улучшения. Этап 6 — самый дорогой и наименее обязательный, поэтому идёт последним.
✅ Этап 1. Автосоздание API-токена
Что сделано. При добавлении сервера пользователь вводит логин и пароль, панель подключается ими один раз, создаёт на ноде собственный API-токен и дальше работает по нему. Токен отзывается из UI Proxmox, не даёт SSH- и веб-логина и виден в аудите PVE под своим именем.
backend/app/proxmox/token_provisioning.py— создание токена (privsep=0), выбор свободного имени (pvemanager, при коллизииpvemanager-2), откат черезrevoke_api_token.POST /api/servers(backend/app/api/proxmox/servers.py) — полеauto_create_token; при сбое коммита созданный токен отзывается, чтобы на ноде не оставался сирота с потерянным секретом.POST /api/servers/{id}/provision-token— перевод уже подключённого сервера с пароля на токен по сохранённому паролю.api_token_valueпереведён наEncryptedString(backend/app/models/proxmox.py) — раньше токены лежали в БД открытым текстом, в отличие от паролей.- Фронтенд: выбор из трёх режимов подключения в
NodesPage.tsx, кнопка «Перевести на API-токен» на карточке сервера.
Принятые решения. Пароль после создания токена остаётся в БД
(зашифрованным) — он нужен ProxmoxClient._password для SSH-фолбэков вроде
pvecm delnode. Токен создаётся с privsep=0, то есть наследует права
пользователя.
✅ Этап 2. Графики метрик нод
Что сделано. Вкладка «Графики» в NodeDetailPage: CPU, IO delay, память,
load average, сеть in/out, swap, корневая ФС. Пресеты 1h/24h/7d/30d/1y.
frontend/src/components/shared/MetricChart.tsx— общийChartCard, переиспользуется вкладкой инстансов.useNodeMetricsвfrontend/src/hooks/use-nodes.ts— нормализация сырого RRD (PVE отдаётcpu/iowaitдолями 0..1).frontend/src/features/nodes/NodeGraphs.tsx.
Бэкенд не менялся — эндпоинт /api/{server_id}/node/rrddata существовал ранее.
Дисковых I/O и IOPS в наборе нет: PVE их в node RRD не отдаёт.
Открытый хвост. Рендер с реальными данными не проверялся визуально.
🟡 Этап 3. Установка одной командой
Что сделано. bootstrap.sh в корне репозитория — единый загрузчик для обеих
веток, определяет среду по наличию /etc/pve:
- Ветка A (
install_branch_a): ставит Docker (черезget.docker.com, если его ещё нет) и git/curl/openssl/jq, клонирует репозиторий вPVEMANAGER_DIR(/opt/pvemanagerпо умолчанию) на выбранныйgit-реф (PVEMANAGER_VERSION, по умолчанию — последний тег репозитория, иначеmain), передаёт управлениеdeploy.sh --standalone. Docker ставится заранее нарочно: интерактивный запрос внутриcheck_requirements()вdeploy.shиначе заблокировал бы неинтерактивный запуск из LXC. - Ветка B (
install_branch_b): создаёт LXC (Debian 12, DHCP наvmbr0,nesting=1,keyctl=1для Docker внутри), параметры (vmid, storage, bridge, ресурсы) настраиваются переменнымиPVEMANAGER_LXC_*; шаблон скачивается черезpveam, если его ещё нет. Затем клонирует репозиторий во временную директорию на хосте только чтобы забрать актуальную версию самогоbootstrap.sh, кладёт его в контейнер черезpct pushи выполняет там — внутри LXC/etc/pveне существует, поэтому скрипт сам уходит в ветку A. После того как панель поднялась (GET /health), хост создаёт токенroot@pam!pvemanagerлокально черезpvesh create ... --privsep 0, логинится в панель подadmin/сгенерированным паролем и регистрирует сам себя (POST /api/servers) — пароль от Proxmox никуда не передаётся.
Принятые решения при реализации.
- Убран небезопасный дефолт
${POSTGRES_PASSWORD:-pvemanager_secure_password}вcompose.yml(заменён на${POSTGRES_PASSWORD:?...}) — раньше стек мог молча подняться с предсказуемым паролем БД при отсутствии.env. - Реф для клонирования (
resolve_ref()) уже поддерживает пиннинг по последнему git-тегу черезgit ls-remote --tags, но в репозитории пока нет ни одного тега — до первого релиза загрузчик fallback'ится наmain.
Не сделано / не проверено.
- Скрипт не тестировался на живом Proxmox-хосте — нет доступного стенда
в этой сессии. Нужна проверка: создание LXC, установка Docker внутри
unprivileged-контейнера, полный проход
deploy.sh --standalone, логин и регистрация ноды. - Инфраструктура раздачи (домен
get.<домен>, статика с нужнымиContent-Type/Cache-Control, публикация sha256, второй путь установки в README) — не делалась, требует внешних ресурсов (домен, хостинг).bash bootstrap.shуже работает как локальный путь установки. pve --helpне проверялся в связке с этим потоком (устанавливается тем жеdeploy.shчерезinstall_pve_cli --auto, логика не менялась).
Осталось до полного закрытия этапа (инфраструктурная часть, не код):
- Домен
get.<домен>под контролем проекта + TLS, раздача статики (Cloudflare Pages/Worker или R2) сContent-Type: text/plainи короткимCache-Control. - Публикация sha256
bootstrap.shи второй путь установки в README («скачать → прочитать → запустить»). - Первый git-тег в репозитории, чтобы пиннинг версии в
resolve_ref()начал реально на что-то указывать (сейчас fallback наmain). - Живой прогон на Debian/Ubuntu-машине и на PVE-хосте — см. «Не сделано / не проверено» выше.
Готово, когда:
- на чистой Debian/Ubuntu-машине одна команда поднимает работающую панель;
- на PVE-хосте та же команда создаёт LXC, ставит панель внутрь и регистрирует ноду без ввода пароля;
pve --helpотвечает;- повторный запуск обновляет установку, не ломая её.
⬜ Этап 4. Интеграция с Proxmox Backup Server
Цель. Управлять PBS напрямую, а не только через PVE.
Что уже есть. PBS подключается к PVE как storage типа pbs, поэтому
бэкап и восстановление уже работают через backend/app/api/proxmox/backup.py
(storages, native jobs, restore, bulk restore).
Чего не хватает (не видно через PVE API): использование datastore и дедупликация, задания verify/prune/GC, namespaces, sync- и tape-задания, браузер снапшотов с восстановлением отдельных файлов.
Шаги.
- Модель
PBSServer: host, порт (по умолчанию 8007),api_user, токен (user@pbs!tokenid), fingerprint сертификата,verify_ssl. Секреты — черезEncryptedString. - Клиент
backend/app/pbs/client.pyпо образцуProxmoxClientс миксинами: datastores, snapshots, verify/prune/GC, namespaces. - API-роутер
backend/app/api/pbs/+ права вbackend/app/rbac/permissions.py. - Фронтенд: раздел PBS со списком серверов, datastore-обзором и браузером снапшотов.
- Связать с существующим разделом бэкапов: если storage типа
pbsсоответствует зарегистрированному PBS-серверу — показывать расширенные данные.
Готово, когда можно добавить PBS-сервер, увидеть занятость datastore и коэффициент дедупликации, запустить verify/prune и просмотреть содержимое снапшота.
Оценка. Сопоставимо по объёму со всем текущим модулем бэкапов.
⬜ Этап 5. Redis: вынос воркера и общий кэш
Цель. Вынести исполнение фоновых задач из процесса app в отдельный
контейнер и убрать состояние, которое сейчас живёт только в памяти процесса.
Текущее состояние. TaskQueueService хранит задачи в Postgres, а
исполняются они внутри app через APScheduler при UVICORN_WORKERS=1.
Последствия: воркеры не масштабируются, задача в статусе running зависает при
рестарте контейнера, нет ретраев.
Важно: очередь в Postgres сама по себе не проблема — для домашней нагрузки её достаточно. Выигрыш даёт именно вынос воркера; Redis нужен как брокер и попутно закрывает три места, которые сейчас ломаются при масштабировании:
connection_cacheвbackend/app/proxmox/client.py— словарь в памяти;_rrd_cacheвbackend/app/services/metrics_broadcaster.py— то же;websocket_manager.py— при более чем одном uvicorn-воркере рассылка не работает без pub/sub.
Шаги.
- Добавить сервис
redisвcompose.yml(alpine-образ, ограничение памяти). - Подключить ARQ как брокер (asyncio-native; Celery для этой задачи избыточен). Postgres остаётся источником правды по истории задач.
- Вынести обработку
TaskQueueв отдельный контейнерworker. - Перевести
connection_cacheи_rrd_cacheна Redis. - Перевести WebSocket-рассылку на Redis pub/sub, после чего можно поднять
UVICORN_WORKERS. - Добавить ретраи и переустановку статуса зависших задач при старте воркера.
Готово, когда задачи исполняются в отдельном контейнере, переживают рестарт
app, а панель корректно работает при UVICORN_WORKERS > 1.
⬜ Этап 6. Внешний стек метрик и логов
Цель. Снять с панели хранение метрик и дать историю глубже, чем позволяет RRD, — но не навязывая всем пользователям тяжёлый стек мониторинга.
Ключевой факт. Собирать метрики самим не нужно: у Proxmox есть встроенный
external metric server (протоколы InfluxDB и Graphite) — нода push-ит метрики
наружу без всякого кода в панели. Для pull-модели существует
prometheus-pve-exporter.
Решения по составу.
- VictoriaMetrics вместо Prometheus и VictoriaLogs вместо Loki — заметно меньше RAM и диска при совместимом PromQL/LogsQL.
- Grafana необязательна. Если панель сама ходит в PromQL, Grafana нужна только продвинутым пользователям — подключается опционально.
- Весь стек включается через
docker compose --profile monitoring, а не входит в поставку по умолчанию: для типовой домашней установки это дороже самой панели.
Шаги.
- Профиль
monitoringвcompose.yml. - В панели — переключаемый источник метрик:
native(RRD через PVE API, как сейчас) либоvictoriametrics(PromQL). Настройка в разделе Settings. - Абстрагировать слой доступа к данным на фронтенде, чтобы
GraphsTabиNodeGraphsне переписывать при смене источника. - Инструкция по настройке external metric server на ноде.
- Опциональная Grafana с преднастроенными дашбордами.
Готово, когда при выключенном профиле всё работает как сейчас, а при включённом графики строятся из VictoriaMetrics с историей глубже RRD.
Зависимость. Этап 2 намеренно сделан на RRD. При выполнении этого этапа слой доступа к данным нужно спрятать за один хук, а не переписывать компоненты.
Технический долг, замеченный попутно
- Хардкод русских строк в
frontend/src/features/instances/tabs/GraphsTab.tsx: «IOPS чтение», «IOPS запись», «Заполненность диска», «Все», «Сбросить», «Нет данных» — не проходят через i18n. graphify update .отказывается перезаписывать граф: AST-версия даёт 5938 узлов против 5959 в текущемgraph.json. Нужно решить, форсить ли перестроение.