602 lines
25 KiB
Markdown
602 lines
25 KiB
Markdown
# Инструкция по развёртыванию — МОНТ Инфраструктурный Полигон
|
||
|
||
Версия проекта: **0.6.0**
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
1. [Что это такое](#1-что-это-такое)
|
||
2. [Архитектура](#2-архитектура)
|
||
3. [Требования к серверу](#3-требования-к-серверу)
|
||
4. [Установка зависимостей](#4-установка-зависимостей)
|
||
5. [Клонирование репозитория](#5-клонирование-репозитория)
|
||
6. [Настройка переменных окружения](#6-настройка-переменных-окружения)
|
||
7. [Настройка Traefik](#7-настройка-traefik)
|
||
8. [Сборка Docker-образов рантаймов](#8-сборка-docker-образов-рантаймов)
|
||
9. [Первый запуск](#9-первый-запуск)
|
||
10. [Инициализация базы данных](#10-инициализация-базы-данных)
|
||
11. [Проверка работоспособности](#11-проверка-работоспособности)
|
||
12. [Настройка через админку](#12-настройка-через-админку)
|
||
13. [Настройка RDP-слотов](#13-настройка-rdp-слотов)
|
||
14. [Обновление проекта](#14-обновление-проекта)
|
||
15. [Описание всех переменных окружения](#15-описание-всех-переменных-окружения)
|
||
16. [Частые проблемы и решения](#16-частые-проблемы-и-решения)
|
||
|
||
---
|
||
|
||
## 1. Что это такое
|
||
|
||
Веб-портал для выдачи пользователям браузерного доступа к стендам и сервисам.
|
||
Поддерживает три типа сервисов:
|
||
|
||
| Тип | Описание |
|
||
|-----|----------|
|
||
| **WEB** | Открывает веб-сайт в браузере Chromium внутри виртуального дисплея (noVNC-стриминг) |
|
||
| **VNC** | Подключается по VNC к внешнему хосту |
|
||
| **RDP** | Подключается по RDP к внешнему хосту; пул слотов — несколько пользователей одновременно |
|
||
|
||
Стек: **FastAPI + PostgreSQL + Traefik + Docker**.
|
||
|
||
---
|
||
|
||
## 2. Архитектура
|
||
|
||
```
|
||
Интернет
|
||
│ HTTPS :443
|
||
▼
|
||
┌─────────┐
|
||
│ Traefik │ — edge-прокси, TLS, маршрутизация по URL
|
||
└────┬────┘
|
||
│ docker network: portal_net
|
||
├──────────────────────────────┐
|
||
▼ ▼
|
||
┌─────────┐ ┌────────────┐
|
||
│ api │ FastAPI/uvicorn │ maintenance│ (отдельный процесс очистки)
|
||
└────┬────┘ └────────────┘
|
||
│
|
||
├─ /var/run/docker.sock (API управляет контейнерами напрямую)
|
||
│
|
||
▼
|
||
┌──────────┐
|
||
│ db │ PostgreSQL 16
|
||
└──────────┘
|
||
|
||
Динамически создаваемые контейнеры (не в compose):
|
||
portal-webpool-N — WEB-сессии (portal-universal-runtime)
|
||
portal-universal-N — VNC-сессии (portal-universal-runtime)
|
||
portal-rdpslot-SLUG-N — RDP-слоты (portal-rdp-proxy)
|
||
```
|
||
|
||
Traefik читает метки (`labels`) у динамически создаваемых контейнеров и автоматически добавляет маршруты без перезапуска.
|
||
|
||
---
|
||
|
||
## 3. Требования к серверу
|
||
|
||
| Параметр | Минимум | Рекомендовано |
|
||
|----------|---------|---------------|
|
||
| ОС | Ubuntu 22.04 / Debian 12 | Ubuntu 24.04 |
|
||
| CPU | 2 ядра | 4+ ядра |
|
||
| RAM | 4 ГБ | 8+ ГБ |
|
||
| Диск | 20 ГБ | 40+ ГБ (Docker-образы большие) |
|
||
| Docker | 24+ | 29+ |
|
||
| Docker Compose | v2.20+ | v2.40+ |
|
||
| Внешний IP | Обязателен | — |
|
||
| DNS-запись | A-запись домена → IP сервера | — |
|
||
| Порты открыты | 80, 443 | — |
|
||
|
||
> **Важно**: домен и DNS-запись нужны для автоматического получения TLS-сертификата через Let's Encrypt.
|
||
> Без домена — использовать самоподписанный сертификат (см. [раздел 7](#7-настройка-traefik)).
|
||
|
||
---
|
||
|
||
## 4. Установка зависимостей
|
||
|
||
```bash
|
||
# Обновляем систему
|
||
sudo apt-get update && sudo apt-get upgrade -y
|
||
|
||
# Устанавливаем Docker (официальный способ)
|
||
curl -fsSL https://get.docker.com | sh
|
||
|
||
# Добавляем текущего пользователя в группу docker (без sudo)
|
||
sudo usermod -aG docker $USER
|
||
newgrp docker
|
||
|
||
# Проверяем
|
||
docker --version # Docker version 29.x.x
|
||
docker compose version # Docker Compose version 2.x.x
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Клонирование репозитория
|
||
|
||
Все исходники находятся в git-репозитории:
|
||
|
||
```
|
||
https://git.ruslan.xyz/ruslan/Stend_mont
|
||
```
|
||
|
||
```bash
|
||
# Рабочий каталог — можно любой, например /opt/stand или ~/docker/stand
|
||
mkdir -p ~/docker && cd ~/docker
|
||
|
||
# Репозиторий приватный — нужны credentials
|
||
git clone https://USER:TOKEN@git.ruslan.xyz/ruslan/Stend_mont.git stand
|
||
cd stand
|
||
```
|
||
|
||
> Запросите логин и токен у владельца репозитория.
|
||
|
||
Структура проекта после клонирования:
|
||
```
|
||
stand/
|
||
├── app/ # FastAPI-приложение (Dockerfile + main.py + шаблоны)
|
||
├── rdp-proxy/ # Docker-образ RDP-рантайма (xvfb + xfreerdp + noVNC)
|
||
├── universal-runtime/ # Docker-образ WEB/VNC-рантайма (Chromium + x11vnc + noVNC)
|
||
├── kiosk/ # Docker-образ kiosk-режима (опционально)
|
||
├── traefik/ # Конфиг Traefik (traefik.yml + dynamic/)
|
||
├── scripts/ # SQL-схема БД
|
||
├── docker-compose.yml
|
||
└── .env.example # Шаблон переменных окружения
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Настройка переменных окружения
|
||
|
||
Скопируйте шаблон и отредактируйте:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
nano .env
|
||
```
|
||
|
||
**Минимальный набор значений, которые нужно поменять:**
|
||
|
||
```dotenv
|
||
# Домен, на котором будет работать портал
|
||
PUBLIC_HOST=your-domain.example.com
|
||
|
||
# Email для Let's Encrypt (уведомления об истечении сертификата)
|
||
LETSENCRYPT_EMAIL=admin@example.com
|
||
|
||
# Пароль PostgreSQL (придумайте сами, не менее 16 символов)
|
||
POSTGRES_PASSWORD=supersecretdbpassword
|
||
|
||
# Секретный ключ для подписи сессий (минимум 32 случайных символа)
|
||
# Генерация: python3 -c "import secrets; print(secrets.token_hex(32))"
|
||
SIGNING_KEY=your_random_signing_key_here
|
||
|
||
# Логин и пароль администратора портала
|
||
ADMIN_USERNAME=admin
|
||
ADMIN_PASSWORD=your_admin_password
|
||
```
|
||
|
||
**Остальные переменные** — описание см. в [разделе 15](#15-описание-всех-переменных-окружения).
|
||
|
||
---
|
||
|
||
## 7. Настройка Traefik
|
||
|
||
### 7.1 Файл `traefik/traefik.yml`
|
||
|
||
Откройте и проверьте email в секции `certificatesResolvers`:
|
||
|
||
```bash
|
||
nano traefik/traefik.yml
|
||
```
|
||
|
||
```yaml
|
||
certificatesResolvers:
|
||
letsencrypt:
|
||
acme:
|
||
email: admin@example.com # ← ваш email
|
||
storage: /letsencrypt/acme.json
|
||
tlsChallenge: {}
|
||
```
|
||
|
||
Порты по умолчанию: **HTTP :8288**, **HTTPS :2288** (нестандартные, прописаны в `docker-compose.yml`).
|
||
Если нужны стандартные 80/443:
|
||
|
||
```bash
|
||
nano docker-compose.yml
|
||
# Найдите секцию traefik -> ports и замените:
|
||
# - "0.0.0.0:8288:80" → - "80:80"
|
||
# - "0.0.0.0:2288:443" → - "443:443"
|
||
```
|
||
|
||
### 7.2 Создание файла хранилища сертификатов
|
||
|
||
```bash
|
||
mkdir -p traefik/letsencrypt
|
||
touch traefik/letsencrypt/acme.json
|
||
chmod 600 traefik/letsencrypt/acme.json
|
||
```
|
||
|
||
### 7.3 Работа без домена (самоподписанный сертификат)
|
||
|
||
Если домена нет и нужен только HTTP или самоподписанный HTTPS:
|
||
|
||
В `traefik/traefik.yml` удалите секцию `certificatesResolvers`.
|
||
|
||
В `docker-compose.yml` в labels контейнера `api` замените:
|
||
```yaml
|
||
- traefik.http.routers.portal.entrypoints=websecure
|
||
- traefik.http.routers.portal.tls=true
|
||
- traefik.http.routers.portal.tls.certresolver=letsencrypt
|
||
```
|
||
на:
|
||
```yaml
|
||
- traefik.http.routers.portal.entrypoints=web
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Сборка Docker-образов рантаймов
|
||
|
||
Рантаймы — это образы для WEB/VNC/RDP-сессий. Они **не входят** в основной `docker compose up`, их нужно собрать отдельно.
|
||
|
||
```bash
|
||
cd ~/docker/stand
|
||
|
||
# Образ для WEB и VNC сессий (Chromium + x11vnc + noVNC)
|
||
docker build -t portal-universal-runtime:latest ./universal-runtime/
|
||
|
||
# Образ для RDP сессий (xvfb + xfreerdp + x11vnc + noVNC)
|
||
docker build -t portal-rdp-proxy:latest ./rdp-proxy/
|
||
|
||
# Образ kiosk (опционально, если используется kiosk-режим)
|
||
docker build -t portal-kiosk:latest ./kiosk/
|
||
```
|
||
|
||
> Сборка занимает 3–10 минут в зависимости от скорости интернета (скачивается Chromium и другие пакеты).
|
||
|
||
Проверьте что образы появились:
|
||
```bash
|
||
docker images | grep portal
|
||
# Должно быть:
|
||
# portal-universal-runtime latest ...
|
||
# portal-rdp-proxy latest ...
|
||
# portal-kiosk latest ...
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Первый запуск
|
||
|
||
```bash
|
||
cd ~/docker/stand
|
||
|
||
# Запускаем всё (traefik, db, api, maintenance)
|
||
docker compose up -d --build
|
||
|
||
# Смотрим логи запуска
|
||
docker compose logs -f api
|
||
```
|
||
|
||
Дождитесь строки:
|
||
```
|
||
INFO: Application startup complete.
|
||
```
|
||
|
||
Затем откройте браузер: `https://your-domain.example.com`
|
||
|
||
---
|
||
|
||
## 10. Инициализация базы данных
|
||
|
||
База данных инициализируется **автоматически** при первом старте API через SQLAlchemy (ORM создаёт все таблицы).
|
||
|
||
Если нужно создать таблицы вручную (на случай сбоя):
|
||
|
||
```bash
|
||
# Подключитесь к контейнеру PostgreSQL
|
||
docker exec -it stend_mont-db-1 psql -U portal -d portal
|
||
|
||
# Выполните скрипт схемы
|
||
\i /dev/stdin
|
||
# вставьте содержимое scripts/schema.sql и нажмите Ctrl+D
|
||
```
|
||
|
||
Или через файл:
|
||
```bash
|
||
docker exec -i stend_mont-db-1 psql -U portal -d portal < scripts/schema.sql
|
||
```
|
||
|
||
> Таблица `rdp_slots` создаётся автоматически ORM, её нет в `schema.sql` — это нормально.
|
||
|
||
---
|
||
|
||
## 11. Проверка работоспособности
|
||
|
||
```bash
|
||
# Статус всех контейнеров
|
||
docker compose ps
|
||
|
||
# Логи отдельных сервисов
|
||
docker compose logs api # FastAPI
|
||
docker compose logs db # PostgreSQL
|
||
docker compose logs traefik # Traefik (маршруты, сертификаты)
|
||
docker compose logs maintenance # Фоновая очистка сессий
|
||
|
||
# Проверка что база доступна
|
||
docker exec stend_mont-db-1 pg_isready -U portal
|
||
|
||
# Проверка API (изнутри сети)
|
||
docker exec stend_mont-api-1 curl -s http://localhost:8000/health
|
||
```
|
||
|
||
Ожидаемый вывод `docker compose ps`:
|
||
```
|
||
NAME STATUS
|
||
stend_mont-api-1 Up
|
||
stend_mont-db-1 Up (healthy)
|
||
stend_mont-maintenance-1 Up
|
||
stend_mont-traefik-1 Up
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Настройка через админку
|
||
|
||
1. Откройте `https://your-domain.example.com/admin`
|
||
2. Войдите с данными `ADMIN_USERNAME` / `ADMIN_PASSWORD` из `.env`
|
||
|
||
### Создание пользователей
|
||
|
||
**Пользователи → Добавить пользователя:**
|
||
- Логин, пароль, срок действия аккаунта
|
||
- После создания — назначить права доступа к сервисам (раздел ACL)
|
||
|
||
### Создание сервисов
|
||
|
||
**Сервисы → Добавить сервис:**
|
||
|
||
| Поле | Описание |
|
||
|------|----------|
|
||
| Название | Отображаемое название |
|
||
| Slug | URL-идентификатор (латиница, цифры, дефис) |
|
||
| Тип | WEB / VNC / RDP |
|
||
| Адрес | `host:port` для VNC/RDP; URL для WEB |
|
||
| Иконка | PNG/SVG, загружается через форму |
|
||
| Размер пула | Для WEB: количество прогретых контейнеров (0 = по запросу) |
|
||
|
||
### Назначение доступа (ACL)
|
||
|
||
**ACL → выберите пользователя → отметьте сервисы → Сохранить**
|
||
|
||
---
|
||
|
||
## 13. Настройка RDP-слотов
|
||
|
||
RDP-сервисы используют **пул слотов**: каждый слот = отдельный RDP-пользователь + отдельный контейнер.
|
||
Пользователи занимают свободные слоты, при их нехватке получают ошибку 503.
|
||
|
||
### Шаг 1: Создайте RDP-сервис
|
||
|
||
В админке создайте сервис с типом **RDP**:
|
||
- **Адрес**: `hostname_или_ip:3389` (без протокола)
|
||
- Домен, безопасность — если нужны
|
||
|
||
### Шаг 2: Добавьте слоты (RDP-пользователей)
|
||
|
||
В карточке сервиса появится раздел **«RDP пользователи (слоты пула)»**:
|
||
- Введите RDP-логин и пароль пользователя на RDP-сервере
|
||
- Нажмите **Добавить**
|
||
- Повторите для каждого параллельного пользователя (слота)
|
||
|
||
### Шаг 3: Запустите контейнеры слотов
|
||
|
||
```bash
|
||
# Перезапустите API с флагом инициализации
|
||
cd ~/docker/stand
|
||
ENABLE_STARTUP_MAINTENANCE=1 docker compose up -d api maintenance
|
||
```
|
||
|
||
После запуска появятся контейнеры вида `portal-rdpslot-SLUG-N`.
|
||
|
||
Проверка:
|
||
```bash
|
||
docker ps | grep rdpslot
|
||
```
|
||
|
||
### Назначение прав на RDP-сервис
|
||
|
||
В разделе ACL назначьте нужным пользователям доступ к RDP-сервису (обычный чекбокс).
|
||
Любой пользователь с доступом может занять любой свободный слот.
|
||
|
||
---
|
||
|
||
## 14. Обновление проекта
|
||
|
||
```bash
|
||
cd ~/docker/stand
|
||
|
||
# Получаем изменения
|
||
git pull
|
||
|
||
# Пересобираем API (если менялся main.py или шаблоны)
|
||
docker compose up -d --build api maintenance
|
||
|
||
# Пересобираем рантаймы (если менялись rdp-proxy/, universal-runtime/, kiosk/)
|
||
docker build -t portal-rdp-proxy:latest ./rdp-proxy/
|
||
docker build -t portal-universal-runtime:latest ./universal-runtime/
|
||
docker build -t portal-kiosk:latest ./kiosk/
|
||
|
||
# После пересборки рантаймов — перезапустить слот-контейнеры
|
||
# Старые запущенные сессии продолжат работать, новые получат новый образ
|
||
docker compose down api maintenance
|
||
ENABLE_STARTUP_MAINTENANCE=1 docker compose up -d api maintenance
|
||
```
|
||
|
||
> **Важно**: `docker compose up -d api` **без** `--build` НЕ обновит код, если контейнер уже запущен.
|
||
> Всегда используйте `--build` после изменений в `app/`.
|
||
|
||
---
|
||
|
||
## 15. Описание всех переменных окружения
|
||
|
||
| Переменная | По умолчанию | Описание |
|
||
|------------|-------------|----------|
|
||
| `COMPOSE_PROJECT_NAME` | `stend_mont` | Префикс имён контейнеров Docker |
|
||
| `PUBLIC_HOST` | — | **Обязательно.** Домен сайта, напр. `stand.example.com` |
|
||
| `LETSENCRYPT_EMAIL` | — | Email для Let's Encrypt |
|
||
| `POSTGRES_DB` | `portal` | Имя базы данных |
|
||
| `POSTGRES_USER` | `portal` | Пользователь PostgreSQL |
|
||
| `POSTGRES_PASSWORD` | — | **Обязательно.** Пароль PostgreSQL |
|
||
| `SIGNING_KEY` | — | **Обязательно.** Секрет для подписи сессионных токенов (мин. 32 символа) |
|
||
| `ADMIN_USERNAME` | `admin` | Логин администратора |
|
||
| `ADMIN_PASSWORD` | — | **Обязательно.** Пароль администратора |
|
||
| `ADMIN_TTL_DAYS` | `3650` | Срок действия аккаунта admin (дни) |
|
||
| `SESSION_IDLE_SECONDS` | `7200` | Тайм-аут сессии по бездействию (секунды). Рекомендуется `300` (5 мин) |
|
||
| `UVICORN_WORKERS` | `6` | Количество воркеров uvicorn |
|
||
| `WEB_POOL_SIZE` | `20` | Максимальное число WEB-контейнеров в пуле |
|
||
| `WEB_POOL_BUFFER` | `2` | Сколько прогретых WEB-контейнеров держать в запасе |
|
||
| `PREWARM_POOL_SIZE` | `2` | Размер пула прогрева для VNC |
|
||
| `UNIVERSAL_POOL_SIZE` | `0` | Размер универсального пула |
|
||
| `MAX_ACTIVE_SERVICES_PER_USER` | `4` | Максимум одновременных сессий на одного пользователя |
|
||
| `ENABLE_STARTUP_MAINTENANCE` | `0` | `1` = при старте запустить/переинициализировать все пул-контейнеры |
|
||
| `TRAEFIK_INTERNAL_URL` | `http://traefik` | URL Traefik изнутри Docker-сети (не менять без нужды) |
|
||
| `LOG_LEVEL` | `INFO` | Уровень логирования: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
||
| `LOG_SLOW_REQUEST_MS` | `2000` | Запросы дольше этого (мс) логируются как медленные |
|
||
| `GO_USER_LOCK_TIMEOUT_SECONDS` | `8` | Тайм-аут блокировки при запуске сессии пользователем |
|
||
| `GO_POOL_LOCK_TIMEOUT_SECONDS` | `20` | Тайм-аут блокировки при захвате слота пула |
|
||
| `POOL_DISPATCH_RETRIES` | `6` | Число попыток занять слот пула |
|
||
| `POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS` | `2.0` | Тайм-аут одного запроса к пул-контейнеру |
|
||
| `POOL_DISPATCH_SLEEP_SECONDS` | `0.3` | Пауза между попытками диспетчеризации |
|
||
| `X11VNC_FLAGS` | `-wait 5 -defer 5 -threads` | Дополнительные флаги x11vnc |
|
||
| `WEB_RESOLUTION_MIN_WIDTH` | `1024` | Минимальная ширина разрешения для WEB-сессий |
|
||
| `WEB_RESOLUTION_MIN_HEIGHT` | `720` | Минимальная высота |
|
||
| `WEB_RESOLUTION_MAX_WIDTH` | `3840` | Максимальная ширина |
|
||
| `WEB_RESOLUTION_MAX_HEIGHT` | `2160` | Максимальная высота |
|
||
|
||
---
|
||
|
||
## 16. Частые проблемы и решения
|
||
|
||
### Сертификат не получается (Let's Encrypt)
|
||
|
||
**Симптом:** Браузер показывает ошибку TLS, в логах Traefik: `acme: error`.
|
||
|
||
**Причины и решения:**
|
||
1. DNS не указывает на сервер — проверьте A-запись: `nslookup your-domain.example.com`
|
||
2. Порт 80 или 443 закрыт фаерволом — откройте: `sudo ufw allow 80 && sudo ufw allow 443`
|
||
3. Файл `acme.json` не имеет прав 600: `chmod 600 traefik/letsencrypt/acme.json`
|
||
4. Слишком много запросов к LE — подождите час и попробуйте снова
|
||
|
||
---
|
||
|
||
### API не стартует (ошибка подключения к БД)
|
||
|
||
**Симптом:** В логах `api`: `connection refused` или `could not connect to server`.
|
||
|
||
```bash
|
||
# Проверьте что база запущена
|
||
docker compose ps db
|
||
docker compose logs db
|
||
|
||
# Убедитесь что переменные совпадают в .env
|
||
grep POSTGRES .env
|
||
```
|
||
|
||
Если база не успела подняться — подождите 5–10 секунд и перезапустите API:
|
||
```bash
|
||
docker compose restart api
|
||
```
|
||
|
||
---
|
||
|
||
### RDP-сессия не подключается (чёрный экран)
|
||
|
||
**Симптом:** noVNC открывается, но экран чёрный или появляется ошибка.
|
||
|
||
```bash
|
||
# Найдите имя контейнера слота
|
||
docker ps | grep rdpslot
|
||
|
||
# Смотрите логи внутри контейнера
|
||
docker exec portal-rdpslot-SLUG-N cat /tmp/xfreerdp.log
|
||
docker exec portal-rdpslot-SLUG-N cat /tmp/xvfb.log
|
||
docker exec portal-rdpslot-SLUG-N cat /tmp/x11vnc.log
|
||
```
|
||
|
||
Типичные ошибки:
|
||
|
||
| Ошибка | Причина | Решение |
|
||
|--------|---------|---------|
|
||
| `Server is already active for display :1` | Старый lock-файл Xvfb | Обновите `rdp-proxy/entrypoint.sh` (уже исправлено в v0.6.0) |
|
||
| `Authentication failure` | Неверный RDP логин/пароль | Проверьте слот в админке |
|
||
| `failed to open display :1` | Xvfb не запустился | Перезапустите контейнер слота |
|
||
| `Connection refused` к `:3389` | RDP-сервер недоступен | Проверьте `host:port` сервиса |
|
||
|
||
Перезапуск конкретного слота:
|
||
```bash
|
||
docker restart portal-rdpslot-SLUG-N
|
||
```
|
||
|
||
---
|
||
|
||
### Слот показывается свободным, но пользователь всё ещё в сессии
|
||
|
||
**Симптом:** В админке слот «Свободен», но пользователь зашёл в /view.
|
||
|
||
Это возникает если сессия осталась в статусе `ACTIVE` без обновления `last_access_at`.
|
||
Принудительно истечь сессию через PostgreSQL:
|
||
|
||
```bash
|
||
docker exec -it stend_mont-db-1 psql -U portal -d portal -c \
|
||
"UPDATE sessions SET status='EXPIRED' WHERE status='ACTIVE' AND service_id=<ID>;"
|
||
```
|
||
|
||
---
|
||
|
||
### Контейнеры пула не создаются при старте
|
||
|
||
**Симптом:** После запуска нет контейнеров `portal-rdpslot-*` или `portal-webpool-*`.
|
||
|
||
Убедитесь что стартовали с флагом:
|
||
```bash
|
||
ENABLE_STARTUP_MAINTENANCE=1 docker compose up -d api maintenance
|
||
```
|
||
|
||
Или добавьте в `.env`:
|
||
```dotenv
|
||
ENABLE_STARTUP_MAINTENANCE=1
|
||
```
|
||
и перезапустите:
|
||
```bash
|
||
docker compose up -d api maintenance
|
||
```
|
||
|
||
---
|
||
|
||
### Полная переустановка (сброс данных)
|
||
|
||
> **Внимание**: удалятся все пользователи, сессии, сервисы!
|
||
|
||
```bash
|
||
cd ~/docker/stand
|
||
docker compose down -v # -v удаляет volume с данными PostgreSQL
|
||
docker compose up -d --build
|
||
```
|
||
|
||
---
|
||
|
||
## Итоговый чеклист первого развёртывания
|
||
|
||
- [ ] Сервер с Ubuntu 22.04+ и публичным IP
|
||
- [ ] DNS A-запись `ваш-домен → IP сервера`
|
||
- [ ] Установлен Docker 24+ и Docker Compose v2
|
||
- [ ] Репозиторий склонирован
|
||
- [ ] Заполнен `.env` (PUBLIC_HOST, POSTGRES_PASSWORD, SIGNING_KEY, ADMIN_PASSWORD)
|
||
- [ ] Создан `traefik/letsencrypt/acme.json` с правами 600
|
||
- [ ] Собраны образы рантаймов (`portal-rdp-proxy`, `portal-universal-runtime`, `portal-kiosk`)
|
||
- [ ] Выполнен `docker compose up -d --build`
|
||
- [ ] Открывается `https://ваш-домен/admin` → вход в систему
|
||
- [ ] Созданы пользователи и сервисы
|
||
- [ ] Для RDP: добавлены слоты, перезапущен API с `ENABLE_STARTUP_MAINTENANCE=1`
|