chore: stop tracking local project context doc
This commit is contained in:
@@ -1,515 +0,0 @@
|
|||||||
# Project Context: Portal Stand Access
|
|
||||||
|
|
||||||
Этот файл нужен как быстрый технический контекст для нового разработчика/оператора.
|
|
||||||
|
|
||||||
## 1) Что это за проект
|
|
||||||
|
|
||||||
Веб-портал для выдачи пользователям доступа к стендам/сервисам через браузер.
|
|
||||||
|
|
||||||
Ключевая идея:
|
|
||||||
- пользователь выбирает сервис;
|
|
||||||
- портал открывает сервис в уже прогретом браузерном контейнере (WEB) или в RDP-слоте;
|
|
||||||
- каждая пользовательская сессия имеет `session_id` (UUID) и свой URL `/s/<session_id>/...`.
|
|
||||||
|
|
||||||
## 2) Текущий стек
|
|
||||||
|
|
||||||
- API: FastAPI (`app/main.py`)
|
|
||||||
- БД: PostgreSQL
|
|
||||||
- Edge/router: Traefik (обязателен для динамических маршрутов runtime-контейнеров)
|
|
||||||
- Runtime WEB: `portal-kiosk` (Chromium + x11vnc + websockify/noVNC)
|
|
||||||
- Runtime RDP: `portal-rdp-proxy` (xfreerdp + x11vnc + websockify/noVNC)
|
|
||||||
|
|
||||||
## 3) Принятые продуктовые решения
|
|
||||||
|
|
||||||
- Режим VNC как отдельный сервис больше не используется (deprecate).
|
|
||||||
- Основной сценарий для пользователей: WEB и RDP.
|
|
||||||
- Для WEB используется общий пул `portal-webpool-*` (и авторасширение при нагрузке).
|
|
||||||
- Для RDP используется универсальный пул слотов (`UNIVERSAL_POOL_SIZE`).
|
|
||||||
- Сессии пользователя имеют UUID-ссылки (`/s/<uuid>/...`).
|
|
||||||
|
|
||||||
## 4) Критичные маршруты
|
|
||||||
|
|
||||||
- `/` — выбор сервисов
|
|
||||||
- `/go/<slug>` — запуск пользовательской сессии
|
|
||||||
- `/s/<session_id>/` — страница ожидания старта
|
|
||||||
- `/s/<session_id>/view` — сессионный view для WEB-пула
|
|
||||||
- `/svc/<slug>/` — роут к warm runtime конкретного сервиса
|
|
||||||
- `/w/<slot>/` — роут к WEB pool слоту
|
|
||||||
- `/u/<slot>/` — роут к universal pool слоту
|
|
||||||
- `/admin` — админка
|
|
||||||
|
|
||||||
## 5) Что важно помнить по инфраструктуре
|
|
||||||
|
|
||||||
1. Traefik удалять нельзя.
|
|
||||||
Причина: динамические контейнеры создают labels во время работы, и именно Traefik маршрутизирует:
|
|
||||||
- `/s/<session_id>/...`
|
|
||||||
- `/svc/<slug>/...`
|
|
||||||
- `/w/<slot>/...`
|
|
||||||
- `/u/<slot>/...`
|
|
||||||
|
|
||||||
2. При Nginx Proxy Manager (NPM):
|
|
||||||
- внешний домен -> NPM -> внутренний Traefik.
|
|
||||||
- в `docker-compose.yml` Traefik опубликован так:
|
|
||||||
- `0.0.0.0:2288 -> 443`
|
|
||||||
- `0.0.0.0:8288 -> 80`
|
|
||||||
- в NPM обязательна опция `Websockets Support`.
|
|
||||||
|
|
||||||
3. Кнопка «Домой» в runtime UI:
|
|
||||||
- должна возвращать к выбору сервисов портала (`/`), а не вводить URL в удалённом сайте.
|
|
||||||
|
|
||||||
## 6) Диагностика типовых проблем
|
|
||||||
|
|
||||||
### A) Черный экран в WEB
|
|
||||||
Проверять:
|
|
||||||
- что у noVNC корректный WebSocket endpoint (`.../websockify`);
|
|
||||||
- что сессия active в БД;
|
|
||||||
- что контейнер WEB-пула running;
|
|
||||||
- что в NPM включен websocket proxy.
|
|
||||||
|
|
||||||
Быстрая проверка:
|
|
||||||
- логи `portal-webpool-*`
|
|
||||||
- логи `portal-api-1`
|
|
||||||
- содержимое `/opt/portal/index.html` внутри runtime-контейнера.
|
|
||||||
|
|
||||||
### B) "Соединение со слотом потеряно" в RDP
|
|
||||||
Обычно не проблема портала, а проблема соединения `xfreerdp` до целевого host:port/cred/sec.
|
|
||||||
Смотреть `/tmp/session-app.log`/`xfreerdp.log` в `portal-universal-*`.
|
|
||||||
|
|
||||||
### C) Изменения не видны сразу
|
|
||||||
Если менялись runtime-скрипты, старые warm/pool контейнеры могут держать старую версию.
|
|
||||||
Нужно пересобрать образ + пересоздать пул.
|
|
||||||
|
|
||||||
## 7) Где смотреть код
|
|
||||||
|
|
||||||
- Backend и orchestration: `app/main.py`
|
|
||||||
- Админка/UI: `app/templates/admin.html`, `app/static/style.css`
|
|
||||||
- Пользовательский дашборд: `app/templates/dashboard.html`
|
|
||||||
- WEB runtime: `kiosk/entrypoint.sh`, `kiosk/manager.py`
|
|
||||||
- RDP runtime: `rdp-proxy/entrypoint.sh`
|
|
||||||
- Universal runtime: `universal-runtime/entrypoint.sh`, `universal-runtime/manager.py`
|
|
||||||
- Оркестрация: `docker-compose.yml`, `traefik/traefik.yml`
|
|
||||||
|
|
||||||
## 8) Операционные команды
|
|
||||||
|
|
||||||
Сборка runtime-образов:
|
|
||||||
```bash
|
|
||||||
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
|
|
||||||
```
|
|
||||||
|
|
||||||
Поднять всё:
|
|
||||||
```bash
|
|
||||||
docker compose up -d --build
|
|
||||||
```
|
|
||||||
|
|
||||||
Перезапуск только API:
|
|
||||||
```bash
|
|
||||||
docker compose up -d api
|
|
||||||
```
|
|
||||||
|
|
||||||
Проверка состояния:
|
|
||||||
```bash
|
|
||||||
docker compose ps
|
|
||||||
docker compose logs -f api traefik
|
|
||||||
```
|
|
||||||
|
|
||||||
## 9) Что еще можно улучшить
|
|
||||||
|
|
||||||
- вынести миграции в Alembic;
|
|
||||||
- добавить отдельный health dashboard с websocket/rdp метриками;
|
|
||||||
- централизованный сбор логов и алерты;
|
|
||||||
- e2e smoke-тесты на сценарии `/go -> /s/<uuid>/view`.
|
|
||||||
|
|
||||||
## 11) Git доступ и публикация
|
|
||||||
|
|
||||||
Репозиторий:
|
|
||||||
- `https://git.ruslan.xyz/ruslan/Stend_mont`
|
|
||||||
|
|
||||||
Учетные данные HTTPS (текущие):
|
|
||||||
- login: `ruslan@ipcom.su`
|
|
||||||
- password/token: `utOgbZ09ruslan`
|
|
||||||
|
|
||||||
Пример push:
|
|
||||||
```bash
|
|
||||||
cd /root/Stend_mont
|
|
||||||
git add .
|
|
||||||
git commit -m "your message"
|
|
||||||
git push https://ruslan%40ipcom.su:utOgbZ09ruslan@git.ruslan.xyz/ruslan/Stend_mont main
|
|
||||||
```
|
|
||||||
|
|
||||||
## 12) Текущее runtime-состояние (на момент фиксации)
|
|
||||||
|
|
||||||
- API запущен с `uvicorn --workers 4` через `docker-compose.yml`.
|
|
||||||
- Для WEB используется `portal-webpool-*`.
|
|
||||||
- Для RDP используется `portal-universal-*`.
|
|
||||||
|
|
||||||
## 13) Последние изменения (2026-04-21)
|
|
||||||
|
|
||||||
1. UI/брендинг:
|
|
||||||
- Тексты в интерфейсе переведены на формулировку `инфрастуктурный полигон`.
|
|
||||||
- На главной панели приветствие в блоке `admin-intro`: `Добро пожаловать в инфрастуктурный полигон`.
|
|
||||||
- Кнопка выхода на дашборде: `Выход` (вместо `Logout`).
|
|
||||||
|
|
||||||
2. WEB runtime (браузерные сервисы):
|
|
||||||
- В панели управления runtime оставлены 2 кнопки:
|
|
||||||
- `Назад`
|
|
||||||
- `Главная` (ведет на главную панель портала `/`).
|
|
||||||
- Кнопка `Вперед` удалена.
|
|
||||||
- Изменения применены в `kiosk/entrypoint.sh` и `universal-runtime/entrypoint.sh`.
|
|
||||||
|
|
||||||
3. Логин и просроченные пользователи:
|
|
||||||
- Если пользователь найден и пароль верный, но аккаунт просрочен/неактивен, на экране входа показывается сообщение:
|
|
||||||
`Доступ к сервису приостоновлен, обратитесь к вашему менеджеру`.
|
|
||||||
- Сообщение рендерится в шаблоне `app/templates/login.html` через `login_error`.
|
|
||||||
|
|
||||||
4. Категории сервисов:
|
|
||||||
- Добавлены сущности и связи:
|
|
||||||
- `categories`
|
|
||||||
- `service_categories`
|
|
||||||
- Категории можно создавать/удалять в админке.
|
|
||||||
- При создании/редактировании WEB/RDP сервиса можно выбрать категории.
|
|
||||||
- На главной панели добавлен стильный фильтр по категориям (chips) и бейджи категорий на карточке сервиса.
|
|
||||||
|
|
||||||
5. Иконки сервисов:
|
|
||||||
- Иконки на главной панели увеличены примерно в 6 раз.
|
|
||||||
- Масштабирование иконок: `object-fit: contain`, чтобы картинка полностью влезала в рамку.
|
|
||||||
- В админке загрузка иконки стала автоматической при выборе файла (без кнопки Upload).
|
|
||||||
|
|
||||||
6. Многоворкерный API и startup:
|
|
||||||
- API работает с `uvicorn --workers 4`.
|
|
||||||
- Чтобы убрать гонку DDL на старте (при нескольких воркерах), добавлен file-lock на bootstrap схемы:
|
|
||||||
- lock-файл: `/tmp/portal-schema.lock`
|
|
||||||
- сериализуется выполнение `Base.metadata.create_all(...)` и `ensure_schema_compatibility()`.
|
|
||||||
|
|
||||||
7. Операционные заметки по применению runtime-изменений:
|
|
||||||
- После изменения `kiosk`/`universal-runtime` нужно:
|
|
||||||
1. пересобрать runtime-образы,
|
|
||||||
2. пересоздать `portal-webpool-*`, `portal-universal-*`, `portal-warm-*` контейнеры,
|
|
||||||
3. перезапустить `api`.
|
|
||||||
|
|
||||||
|
|
||||||
## 14) Обновление контекста (2026-04-21, вечер)
|
|
||||||
|
|
||||||
1. Главная страница и 500:
|
|
||||||
- Был зафиксирован Internal Server Error на /.
|
|
||||||
- Причина: синтаксическая ошибка Jinja в app/templates/login.html (поврежденный endif).
|
|
||||||
- Статус: исправлено, API перезапущен, / отвечает 200.
|
|
||||||
|
|
||||||
2. Фон и визуальные эффекты:
|
|
||||||
- Были тесты фонов main.jpg, main_general.jpg, 123.jpg и локального файла 71ba42f1d7d61e4313ad8fd086d3ed7f.jpg.
|
|
||||||
- Текущее состояние по запросу: эффекты отключены.
|
|
||||||
- Отключено: parallax, анимации облаков, hover-движения карточек/ссылок, blur карточек.
|
|
||||||
- Главная панель оставлена со статичным светлым фоном без motion-эффектов.
|
|
||||||
|
|
||||||
3. Файлы, затронутые в этой волне:
|
|
||||||
- app/templates/dashboard.html: удален parallax/cloud слой из разметки.
|
|
||||||
- app/static/style.css: добавлен override-блок для отключения эффектов.
|
|
||||||
- app/templates/login.html: исправлена ошибка шаблона.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5. Git публикация:
|
|
||||||
- origin: https://git.ruslan.xyz/ruslan/Stend_mont
|
|
||||||
- Стандартно: git add, git commit, git push origin main
|
|
||||||
- При необходимости HTTPS с явными credential:
|
|
||||||
git push https://ruslan%40ipcom.su:utOgbZ09ruslan@git.ruslan.xyz/ruslan/Stend_mont main
|
|
||||||
|
|
||||||
## 15) Обновления (2026-04-21, таймаут и пулы)
|
|
||||||
|
|
||||||
1. Таймаут простаивания сессии уменьшен:
|
|
||||||
- Было: `SESSION_IDLE_SECONDS=1800` (~30 минут).
|
|
||||||
- Стало: `SESSION_IDLE_SECONDS=300` (~5 минут).
|
|
||||||
- Источник значения:
|
|
||||||
- `.env`: `SESSION_IDLE_SECONDS=300`
|
|
||||||
- `docker-compose.yml`: `SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-300}`
|
|
||||||
- fallback в `app/main.py`: `300`.
|
|
||||||
|
|
||||||
2. Поведение при простое (heartbeat):
|
|
||||||
- В runtime-страницах (`kiosk`, `universal-runtime`, `rdp-proxy`) heartbeat теперь проверяет HTTP-статус `touch`.
|
|
||||||
- Если `touch` возвращает не `2xx` (например, `410 Session expired`), клиент делает редирект на:
|
|
||||||
`/?session_closed=idle`
|
|
||||||
- На `/` добавлено уведомление:
|
|
||||||
`Сессия была закрыта из-за простоя. Откройте сервис заново.`
|
|
||||||
- Уведомление показывается и на login-page, и на dashboard.
|
|
||||||
|
|
||||||
3. Изменение API для touch:
|
|
||||||
- `POST /api/sessions/{id}/touch`:
|
|
||||||
- `404` если сессия не найдена/не принадлежит пользователю;
|
|
||||||
- `410` если сессия найдена, но уже не `ACTIVE`.
|
|
||||||
|
|
||||||
## 16) Обновления (2026-04-21, ночь)
|
|
||||||
|
|
||||||
1. Ограничение активных сервисов пользователя:
|
|
||||||
- Лимит оставлен `MAX_ACTIVE_SERVICES_PER_USER=4`.
|
|
||||||
- Поведение изменено на FIFO-ротацию:
|
|
||||||
- при открытии 5-го сервиса автоматически закрывается самый старый активный;
|
|
||||||
- при открытии 6-го — следующий по старшинству и т.д.
|
|
||||||
- Жесткий редирект с ошибкой теперь используется только как аварийный fallback.
|
|
||||||
|
|
||||||
2. Время простоя:
|
|
||||||
- Для обычного простоя подтверждено `SESSION_IDLE_SECONDS=300` (5 минут).
|
|
||||||
- Значения синхронизированы в `.env`, `docker-compose.yml`, `app/main.py`.
|
|
||||||
|
|
||||||
3. Runtime-навигация в сервисах:
|
|
||||||
- Кнопки оставлены символьные:
|
|
||||||
- `←` (назад)
|
|
||||||
- `⌂` (главная)
|
|
||||||
- Позиция обновлена: слева вверху, но чуть ниже прежнего:
|
|
||||||
- `kiosk`: `top:34px`
|
|
||||||
- `universal-runtime`: `top:64px` (ниже статусного блока)
|
|
||||||
|
|
||||||
4. UI карточек на главной:
|
|
||||||
- В описании карточки добавлена прокрутка (`max-height` + `overflow:auto`), если текст не влезает.
|
|
||||||
- Поддержаны переносы строк.
|
|
||||||
- Поддержано отображение жирного текста из:
|
|
||||||
- `**markdown**`
|
|
||||||
- простых HTML-тегов (`<b>`, `<strong>`, `<i>`, `<em>`, `<u>`, `<br>`), с безопасным экранированием остального.
|
|
||||||
|
|
||||||
5. Авторизация:
|
|
||||||
- При неверном логине/пароле теперь отображается явное сообщение на странице входа:
|
|
||||||
`Неверный логин или пароль`
|
|
||||||
(вместо немого 401 без человекочитаемого текста).
|
|
||||||
|
|
||||||
6. Производительность API:
|
|
||||||
- Увеличено число воркеров Uvicorn:
|
|
||||||
- было: `--workers 4`
|
|
||||||
- стало: `--workers 6`
|
|
||||||
- Изменение внесено в `docker-compose.yml`.
|
|
||||||
|
|
||||||
4. WEB pool (устойчивость при пике):
|
|
||||||
- Добавлен recovery на конфликты Docker имен/удаления (`already in use`, `marked for removal`).
|
|
||||||
- Для `ensure_web_pool` добавлены повторные попытки и принудительное удаление конфликтного контейнера перед повтором.
|
|
||||||
- Это закрывает сценарий, когда буфер (`WEB_POOL_BUFFER`) должен расширять пул, но упирается в конфликт имени контейнера.
|
|
||||||
|
|
||||||
5. RDP режим приведен к on-demand модели:
|
|
||||||
- `UNIVERSAL_POOL_SIZE=0` в `.env`.
|
|
||||||
- default в `docker-compose.yml`: `${UNIVERSAL_POOL_SIZE:-0}`.
|
|
||||||
- Для RDP отключен prewarm-подход: сессия поднимается в момент запуска сервиса (per-user session runtime), а не через общий universal-pool.
|
|
||||||
- В админ prewarm для RDP возвращает информационное сообщение, что RDP работает on-demand.
|
|
||||||
|
|
||||||
6. Важный операционный урок:
|
|
||||||
- При работе с `docker compose` обязательно сохранять `.env` заполненным; пустой `.env` приводит к запуску со значениями по умолчанию (пустые креды/хост), что ломает подключение API к БД.
|
|
||||||
|
|
||||||
## 17) Версионность (введено 2026-04-22)
|
|
||||||
|
|
||||||
Принята базовая схема SemVer:
|
|
||||||
- `MAJOR` — несовместимые изменения API/поведения;
|
|
||||||
- `MINOR` — новая функциональность без поломки совместимости;
|
|
||||||
- `PATCH` — исправления багов и операционные правки.
|
|
||||||
|
|
||||||
Текущая версия проекта:
|
|
||||||
- `0.6.0` (см. файл `VERSION` в корне репозитория).
|
|
||||||
|
|
||||||
Правило релиза:
|
|
||||||
- при любом релизном изменении обновлять `VERSION` и добавлять краткую запись в `PROJECT_CONTEXT.md`.
|
|
||||||
|
|
||||||
## 18) Обновления (2026-04-22)
|
|
||||||
|
|
||||||
1. Причина закрытия сессии (`idle` vs `limit`):
|
|
||||||
- Добавлен статус сессии `ROTATED` в API;
|
|
||||||
- Для `POST /api/sessions/{id}/touch` при закрытой сессии возвращается `410` с JSON:
|
|
||||||
- `ok: false`
|
|
||||||
- `reason: idle|limit`
|
|
||||||
- `status: <SessionStatus>`
|
|
||||||
- В рантаймах (`kiosk`, `universal-runtime`) редирект на главную теперь учитывает причину:
|
|
||||||
- `/?session_closed=idle`
|
|
||||||
- `/?session_closed=limit`
|
|
||||||
- На главной странице добавлено отдельное сообщение о закрытии из-за лимита активных сервисов.
|
|
||||||
|
|
||||||
2. API воркеры:
|
|
||||||
- Значение в `docker-compose.yml` увеличено до `uvicorn --workers 18`.
|
|
||||||
|
|
||||||
3. Логирование API усилено (structured logging):
|
|
||||||
- Добавлены структурированные JSON-события с `event` и `req_id`;
|
|
||||||
- Расширен middleware логирования запросов: метод, путь, query, статус, длительность, client_ip, user_agent;
|
|
||||||
- Добавлен порог медленных запросов через `LOG_SLOW_REQUEST_MS` (по умолчанию `2000` мс);
|
|
||||||
- Добавлены ключевые события жизненного цикла сессий:
|
|
||||||
- `session_open_requested`
|
|
||||||
- `session_created`
|
|
||||||
- `session_rotated`
|
|
||||||
- `session_closed`
|
|
||||||
- `session_touch_rejected`
|
|
||||||
- `session_closed_by_user`
|
|
||||||
|
|
||||||
4. Операционная польза:
|
|
||||||
- Быстрее диагностируются причины `504`/обрывов/закрытий;
|
|
||||||
- Проще фильтровать инциденты по `req_id` и `session_id` в `docker compose logs api`.
|
|
||||||
|
|
||||||
## 19) Обновления (2026-04-23, лимиты + нагрузка)
|
|
||||||
|
|
||||||
1. Исправление гонки лимитов активных сервисов:
|
|
||||||
- Зафиксирован кейс, когда при параллельных открытиях сервисов одним пользователем лимит мог временно обходиться (наблюдалось до 8 активных сервисов).
|
|
||||||
- Причина: проверка лимита выполнялась вне критической секции.
|
|
||||||
- Исправление: в `go_service` добавлена пользовательская advisory-lock секция `allocator_lock(db, 92000 + user.id)`, внутри которой выполняются:
|
|
||||||
- проверка существующей сессии по сервису,
|
|
||||||
- проверка/ротация по лимиту,
|
|
||||||
- создание новой сессии.
|
|
||||||
- Результат: операции открытия сервисов для одного пользователя сериализованы, лимит применяется стабильно.
|
|
||||||
|
|
||||||
2. Нагрузочное тестирование (k6):
|
|
||||||
- Добавлен скрипт `scripts/load/portal_k6.js`:
|
|
||||||
- логин,
|
|
||||||
- открытие сервиса `/go/<slug>`,
|
|
||||||
- heartbeat `/api/sessions/{id}/touch`,
|
|
||||||
- закрытие `/api/sessions/{id}/close`.
|
|
||||||
- Добавлены профили: `smoke`, `load`, `stress`.
|
|
||||||
- Добавлены пользовательские метрики: `open_success`, `open_rejected`, `limit_redirects`, `touch_rejected`, `flow_errors`.
|
|
||||||
- Добавлена инструкция запуска: `docs/LOAD_TESTING.md`.
|
|
||||||
|
|
||||||
3. Git фиксация:
|
|
||||||
- Commit: `1438dee`
|
|
||||||
- Message: `feat: improve session limit handling and add k6 load testing`
|
|
||||||
|
|
||||||
## 16) Обновления (2026-04-24, вынос maintenance в отдельный контейнер)
|
|
||||||
|
|
||||||
1. Выделен отдельный сервис maintenance:
|
|
||||||
- Добавлен контейнер `maintenance` в `docker-compose.yml`.
|
|
||||||
- Команда контейнера: `python maintenance_runner.py`.
|
|
||||||
- Назначение: единственный фоновый процесс обслуживания пулов и cleanup просроченных сессий.
|
|
||||||
|
|
||||||
2. Поведение API на старте изменено:
|
|
||||||
- Для `api` установлен флаг `ENABLE_STARTUP_MAINTENANCE=0`.
|
|
||||||
- API-воркеры больше не запускают maintenance-потоки при startup.
|
|
||||||
- В логах API при старте ожидаемое сообщение: `startup_maintenance_disabled`.
|
|
||||||
|
|
||||||
3. Что делает maintenance-контейнер:
|
|
||||||
- bootstrap схемы БД (под schema-lock),
|
|
||||||
- `ensure_universal_pool()` и `ensure_web_pool()`,
|
|
||||||
- поддержка warm-pool (когда WEB pool отключен),
|
|
||||||
- cleanup протухших сессий (через существующий `cleanup_loop`).
|
|
||||||
|
|
||||||
4. Блокировка лидера maintenance:
|
|
||||||
- Используется file-lock `/tmp/portal-maintenance.lock`.
|
|
||||||
- Контейнер maintenance удерживает lock и работает как singleton.
|
|
||||||
|
|
||||||
5. Операционные команды:
|
|
||||||
- Перезапуск API + maintenance:
|
|
||||||
```bash
|
|
||||||
docker compose up -d --build api maintenance
|
|
||||||
```
|
|
||||||
- Проверка:
|
|
||||||
```bash
|
|
||||||
docker compose ps api maintenance
|
|
||||||
docker compose logs -f api maintenance
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Текущее целевое состояние после обновления:
|
|
||||||
- `api` отвечает за пользовательские HTTP-запросы.
|
|
||||||
- `maintenance` отвечает за фоновые задачи и состояние пулов.
|
|
||||||
- Traefik продолжает маршрутизацию как и раньше.
|
|
||||||
|
|
||||||
## 17) Нагрузочный прогон (2026-04-24, 100 пользователей x 2 сервиса)
|
|
||||||
|
|
||||||
Сценарий:
|
|
||||||
- 100 тестовых пользователей `loadu001..loadu100` (пароль `LoadTest!2026`),
|
|
||||||
- каждому выдан доступ к 2 WEB-сервисам: `termidesk`, `vmmanager`,
|
|
||||||
- тест: каждый пользователь логинится и запускает оба сервиса последовательно.
|
|
||||||
|
|
||||||
Инструмент и артефакты:
|
|
||||||
- k6 через Docker: `grafana/k6`,
|
|
||||||
- скрипт: `/root/Stend_mont/scripts/load/k6_100_users_2_services.js`,
|
|
||||||
- вывод прогона: `/tmp/k6_100x2.out`.
|
|
||||||
|
|
||||||
Итог прогона:
|
|
||||||
- `iterations`: 100 (по одной на VU),
|
|
||||||
- `checks_succeeded`: 41.61% (124/298),
|
|
||||||
- `http_req_failed`: 41.13% (174/423),
|
|
||||||
- `open termidesk -> 303`: 14% (14/99),
|
|
||||||
- `open vmmanager -> 303`: 11% (11/99),
|
|
||||||
- p95 `http_req_duration`: ~9.07s,
|
|
||||||
- основная причина ошибок по API-логам: `web_pool_lock_timeout` -> HTTP 503 на `/go/<slug>`.
|
|
||||||
|
|
||||||
Вывод:
|
|
||||||
- при burst-нагрузке 100x2 текущий WEB-пул и таймауты распределения не выдерживают,
|
|
||||||
- требуется увеличение емкости/параметров пула и повторный прогон.
|
|
||||||
|
|
||||||
## 18) Нагрузочный прогон (2026-04-24, плавный 20 пользователей x 2 сервиса)
|
|
||||||
|
|
||||||
Цель:
|
|
||||||
- проверить поведение без резкого пика;
|
|
||||||
- имитировать постепенное подключение: +1 пользователь в минуту;
|
|
||||||
- довести до 20 online, каждый запускает 2 WEB-сервиса (termidesk, vmmanager).
|
|
||||||
|
|
||||||
Подготовка:
|
|
||||||
- временно увеличен idle timeout для теста:
|
|
||||||
- .env: SESSION_IDLE_SECONDS=7200;
|
|
||||||
- WEB runtime слоты пересозданы, чтобы получили IDLE_TIMEOUT=7200.
|
|
||||||
- API и maintenance пересозданы с новыми env.
|
|
||||||
|
|
||||||
Профиль нагрузки:
|
|
||||||
- k6 сценарий ramping-vus:
|
|
||||||
- 20m до 20 VU,
|
|
||||||
- 5m удержание 20 VU,
|
|
||||||
- 1m спад до 0.
|
|
||||||
- каждый VU: логин + /go/termidesk + /go/vmmanager, затем удержание.
|
|
||||||
|
|
||||||
Фактический результат:
|
|
||||||
- k6 checks: 60/60 (100%);
|
|
||||||
- custom metrics:
|
|
||||||
- login_ok: 20/20;
|
|
||||||
- open_service_a_ok: 20/20;
|
|
||||||
- open_service_b_ok: 20/20;
|
|
||||||
- HTTP errors: 0/80;
|
|
||||||
- в БД после прогона: 40 ACTIVE WEB-сессий (20 termidesk + 20 vmmanager).
|
|
||||||
|
|
||||||
Наблюдения по инфраструктуре:
|
|
||||||
- во время роста зафиксировано авторасширение WEB-пула до слотов 0..40;
|
|
||||||
- позже часть старших слотов была удалена, но в БД остались ACTIVE-сессии на слотах 20..39.
|
|
||||||
|
|
||||||
Ресурсы сервера (по /tmp/server_stats_20x2.log):
|
|
||||||
- max load average (1m): 17.35;
|
|
||||||
- max used RAM: 9135 MB (из ~64 GB);
|
|
||||||
- max disk usage /: 96%;
|
|
||||||
- max CPU:
|
|
||||||
- stend_mont-api-1: 3.39%,
|
|
||||||
- stend_mont-traefik-1: 60.87%,
|
|
||||||
- stend_mont-db-1: 7.71%,
|
|
||||||
- single portal-webpool-*: до 250.44%.
|
|
||||||
|
|
||||||
Вывод:
|
|
||||||
- плавный сценарий 20x2 проходит стабильно по HTTP/логике запуска;
|
|
||||||
- обнаружен риск целостности состояния: ACTIVE-сессии могут ссылаться на слоты, контейнеры которых уже scale-down/удалены.
|
|
||||||
|
|
||||||
## 19) Обновления (2026-04-25, разрешение WEB-сессий и runtime tuning)
|
|
||||||
|
|
||||||
1. Диагностика "узкого экрана" в `/s/<session_id>/view`:
|
|
||||||
- проверен полный путь передачи разрешения:
|
|
||||||
- frontend: `app/templates/dashboard.html` -> query `sw/sh` для `/go/<slug>`;
|
|
||||||
- backend: `app/main.py` -> `sanitize_client_resolution(...)` + dispatch в web/universal pool;
|
|
||||||
- runtime: `universal-runtime/manager.py` -> `apply_resolution(...)` через `xrandr`.
|
|
||||||
- в реальном кейсе подтверждено, что проблема воспроизводилась при открытии старого URL сессии `/s/<id>/view` вместо нового запуска через `/go/<slug>`.
|
|
||||||
|
|
||||||
2. Изменения по разрешению:
|
|
||||||
- `app/templates/dashboard.html`:
|
|
||||||
- обновлен расчет `sw/sh` (приоритет `screen.width/screen.height`, fallback на viewport).
|
|
||||||
- `universal-runtime/manager.py`:
|
|
||||||
- добавлен fallback на дефолтное `CHROME_WINDOW_SIZE`, если requested mode не применился через `xrandr`.
|
|
||||||
- `app/main.py`:
|
|
||||||
- добавлено диагностическое логирование `session_open_resolution` (`sw/sh` и нормализованные `client_width/client_height`).
|
|
||||||
|
|
||||||
3. Возвращен `x11vnc` ncache:
|
|
||||||
- дефолт `X11VNC_FLAGS` изменен на:
|
|
||||||
- `-wait 5 -defer 5 -ncache 10 -threads`
|
|
||||||
- обновленные файлы:
|
|
||||||
- `app/main.py`
|
|
||||||
- `kiosk/entrypoint.sh`
|
|
||||||
- `universal-runtime/entrypoint.sh`
|
|
||||||
|
|
||||||
4. Применение в runtime:
|
|
||||||
- пересобраны образы:
|
|
||||||
- `stend_mont-api`
|
|
||||||
- `portal-kiosk:latest`
|
|
||||||
- `portal-universal-runtime:latest`
|
|
||||||
- пересозданы pool-контейнеры:
|
|
||||||
- `portal-webpool-*`
|
|
||||||
- при необходимости `portal-universal-*` и `portal-warm-*`
|
|
||||||
- итоговая проверка: `portal-webpool` восстановлен до `20/20 running`.
|
|
||||||
|
|
||||||
5. Дополнительно:
|
|
||||||
- подтверждено, что контейнеры не используют GPU (runtime `runc`, без `--gpus` и device mapping);
|
|
||||||
- выполнена чистка рабочей папки от технических артефактов:
|
|
||||||
- удалены `__pycache__`,
|
|
||||||
- удалены `.bak*`/`pre-*` backup-файлы.
|
|
||||||
|
|
||||||
6. Наблюдение на другом сервере (важно для диагностики):
|
|
||||||
- периодически фиксировалось некорректное разрешение WEB-сессии `1920x12960` при включенном `x11vnc -ncache`;
|
|
||||||
- при отключении `ncache` (убрать флаг `-ncache`, оставить без него) проблема исчезала и разрешение становилось корректным.
|
|
||||||
Reference in New Issue
Block a user