Files
vector/SETUP_GUIDE.md

31 KiB
Raw Permalink Blame History

ВЕКТОР МЧС — Гайд по развёртыванию на серверах МЧС

Пошаговое руководство для IT-специалистов МЧС Республики Беларусь по развёртыванию системы определения приоритетных направлений поиска (ВЕКТОР) на серверах организации.

Версия документа: 1.1 от 25 сентября 2026 г. Репозиторий: Sadmin/vector


Оглавление

  1. Требования к серверу
  2. Архитектура развёртывания
  3. Подготовка сервера
  4. Установка Docker и Docker Compose
  5. Получение кода проекта
  6. Конфигурация окружения (.env)
  7. Запуск сервисов
  8. Инициализация базы данных (миграции, сиды)
  9. Загрузка OSM-геослоя (PostGIS)
  10. Проверка работоспособности
  11. Создание пользователей и справочника подразделений
  12. Настройка интеграции с КОНТУР
  13. Обратный прокси и HTTPS
  14. Обновление проекта
  15. Резервное копирование (RPO/RTO)
  16. Мониторинг и логи
  17. Диагностика проблем 17а. Чек-лист передачи проекта
  18. Чек-лист развёртывания

1. Требования к серверу

Минимальные требования

Параметр Значение
ОС Debian 12/13, Ubuntu 22.04+ (любая с Docker)
CPU 2 vCPU
RAM 4 ГБ (2 ГБ свободно для импорта OSM — см. раздел 9)
Диск 20 ГБ + 1 ГБ под дамп OSM
Сеть Доступ браузеров к серверу по HTTP(S); для первичной загрузки дампа OSM — доступ в интернет (одноразово)

Требования безопасности

  • Сервер в закрытом контуре организации; данные ПДн не должны покидать контур — не настраивать проксирование на внешние сервисы
  • Смена всех паролей по умолчанию (см. раздел 6)
  • Для полностью offline-сценария без интернета: скачать дамп OSM заранее (раздел 9) и учесть, что фронт тянет OSM-тайлы с CDN — для абсолютного offline нужен локальный tile-сервер (в планах)

2. Архитектура развёртывания

Docker Compose поднимает 4 сервиса:

Контейнер Образ Порт Назначение
vector-postgres postgres:16 (+PostGIS 3.6 в контейнере) 5432 БД vector_mchs, данные + OSM
vector-backend сборка backend/Dockerfile 8000 FastAPI (uvicorn --reload)
vector-frontend сборка frontend/Dockerfile 3000 React SPA (CRA dev-сервер)
vector-adminer adminer 8080 Веб-администрирование БД

Код backend/services и frontend смонтирован volume'ами — правки подхватываются без пересборки (dev-режим).


3. Подготовка сервера

# 1. Обновление системы
apt update && apt upgrade -y

# 2. Базовые утилиты
apt install -y curl git ca-certificates gnupg

# 3. (опционально) NTP-синхронизация — важна для корректных меток времени аудита
apt install -y systemd-timesyncd
timedatectl set-ntp true

4. Установка Docker и Docker Compose

# Официальный скрипт (или репозиторий docker-ce — по политике организации)
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker

docker --version        # 24+
docker compose version  # v2

5. Получение кода проекта

mkdir -p /root/vector && cd /root/vector
git clone http://<gitea-host>:3000/Sadmin/vector.git .

Ключевые ветки: master — рабочая.


6. Конфигурация окружения (.env)

cp .env.example .env

Обязательные переменные:

# Подключение к БД (внутри compose — хост postgres)
DATABASE_URL=postgresql://postgres:<СМЕНИТЬ_ПАРОЛЬ>@postgres:5432/vector_mchs

# Секрет JWT — сгенерировать: openssl rand -hex 32
JWT_SECRET=<СЛУЧАЙНАЯ_СТРОКА>

# CORS — адрес(а) фронтенда
CORS_ORIGINS=http://<адрес-сервера>:3000

# Интеграция с КОНТУР (раздел 12; можно отложить)
CONTOUR_API_URL=http://<kontur-host>:8000
CONTOUR_TOKEN=<токен из КОНТУРа>

НЕ добавлять никаких ключей внешних ИИ/API — по политике закрытого контура их в проекте нет и быть не должно (тест-страж TestNoLLM).

Пароль БД должен совпадать с POSTGRES_PASSWORD в docker-compose.yml (сервис postgres).


7. Запуск сервисов

cd /root/vector
docker compose up -d
docker compose ps          # все 4 контейнера Up/healthy

Первый старт backend создаёт таблицы через init_db (create_all). Порядок применения Alembic-миграций — раздел 8.


8. Инициализация базы данных (миграции, сиды)

8.1 Миграции Alembic

docker exec vector-backend sh -c 'cd /app/backend && alembic upgrade head'
docker exec vector-backend sh -c 'cd /app/backend && alembic current'   # 010_b23_policy

Миграции идемпотентны на чистой БД. Если БД уже была инициализирована через create_all и alembic upgrade падает DuplicateTable — применить стемп-скрипт:

docker exec -i vector-postgres psql -U postgres -d vector_mchs < scripts/prod-stamp-backfill.sql

8.2 Сид первого администратора

docker exec vector-backend sh -c 'cd /app && python backend/seed_users.py'

Создаётся стартовый администратор (логин admin, пароль по умолчанию — сменить при первом входе через форму). Скрипт пропускается, если пользователи уже есть.

8.3 Сид подразделений (142 юнита)

docker exec vector-backend sh -c 'cd /app && python backend/seed_units.py'

Создаёт РЦУ РЧС → 6 ОУМЧС → 135 Г(Р)ОЧС/ПАСО (справочник идемпотентный: существующие названия не дублируются). Без него админка пользователей бесполезна (пустые селекты подразделений).


9. Загрузка OSM-геослоя (PostGIS)

Бэкенд рассчитывает рельеф секторов из локальной OSM. На чистой БД геосервис автоматически fallback'ится на публичный Overpass (медленно, требует интернет) — для продуктива импортируйте дамп.

9.1 Установка PostGIS в контейнер БД

docker exec -u root vector-postgres apt update
docker exec -u root vector-postgres apt install -y postgresql-16-postgis-3 osm2pgsql
docker exec vector-postgres psql -U postgres -d vector_mchs -c 'CREATE EXTENSION IF NOT EXISTS postgis;'

Установка пакетом в живой контенер переживает перезапуск контейнера (данные в volume), но слетает при ПЕРЕСОЗДАНИИ контейнера — тогда повторить команды.

9.2 Импорт дампа OSM

# Скачивание (одноразово, ~333 МБ; можно перенести на сервер любым носителем)
wget https://download.geofabrik.de/europe/belarus-latest.osm.pbf -O /tmp/belarus-latest.osm.pbf

# Импорт (~4,5 мин, RAM 2 ГБ, node cache 800)
sh scripts/b16-import.sh

Скрипт сам: удаляет старые planet_osm_* таблицы, копирует дамп в контейнер, запускает osm2pgsql (--slim, SRID по умолчанию), выводит список таблиц.

9.3 Проверка

docker exec vector-postgres psql -U postgres -d vector_mchs -t -c "
SELECT 'roads', count(*) FROM planet_osm_roads
UNION ALL SELECT 'forests', count(*) FROM planet_osm_polygon WHERE landuse='forest';"

Ожидается ~1 млн дорог, ~190 тыс. лесов.


10. Проверка работоспособности

# 1. Health бэкенда
curl -s http://localhost:8000/api/v1/health

# 2. Логин (ТОЛЬКО form-urlencoded!)
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'username=admin&password=<пароль>' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')

# 3. Профиль (9 прав у admin)
curl -s http://localhost:8000/api/v1/auth/me -H "Authorization: Bearer $TOKEN"

# 4. Фронт
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/    # 200

# 5. Полный расчёт (e2e-скрипты в scripts/e2e-*.sh — см. примеры вызовов)

В браузере: http://<адрес>:3000 → дашборд «Активные поиски».


11. Создание пользователей и справочника подразделений

Пользователи создаются через UI: Дашборд → Пользователи → «+ Новый пользователь» (доступ: роль admin РЦУ РЧС или coordinator ОУМЧС в рамках своего поддерева).

  • При создании задаётся временный пароль — пользователь обязан сменить его при первом входе
  • Роли: РЦУ РЧС / ОУМЧС / Г(Р)ОЧС / Наблюдатель (описание прав — README.md, раздел RBAC)
  • Инлайн-операции в таблице: смена роли/подразделения, отключение/включение, сброс пароля (отзывает все сессии)
  • Все изменения пишутся в журнал аудита (вкладка «Аудит», ретенция 90 дней)

Иерархию подразделений менять: правкой backend/seed_units.py (идемпотентно, kind ∈ rcu/oblast/gor_rayon — ограничение БД ck_mchs_units_kind).


12. Настройка интеграции с КОНТУР

КОНТУР — система полевой координации (ATAK + Meshtastic) на отдельном сервере. ВЕКТОР отправляет в неё приоритетные зоны.

  1. В .env: CONTOUR_API_URL (адрес API КОНТУРа) и CONTOUR_TOKEN (выдаётся на стороне КОНТУРа)
  2. docker compose up -d backend — пересоздать контейнер (restart не перечитывает env!)
  3. Проверка: операция → «Отправить зоны в КОНТУР» на странице анализа; при недоступности КОНТУРа бэкенд вернёт понятную ошибку 502 и запишет contour_send_failed в аудит

Входящие данные (треки групп, проверенные квадраты) — этап B17, в разработке.


13. Обратный прокси и HTTPS

Для доступа пользователей по HTTPS поставьте nginx/traefik перед фронтендом и API:

server {
    listen 443 ssl;
    server_name vector.mchs.example;

    ssl_certificate     /etc/ssl/certs/vector.crt;
    ssl_certificate_key /etc/ssl/private/vector.key;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
    }
    location /api/ {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        client_max_body_size 10m;
    }
}

В CORS_ORIGINS укажите итоговый https-адрес. В закрытом контуре допустима работа по HTTP без прокси (порты 3000/8000 напрямую).


14. Обновление проекта

cd /root/vector
git pull origin master

# Если менялись миграции:
docker exec vector-backend sh -c 'cd /app/backend && alembic upgrade head'

# Если менялись env-переменные (.env / compose environment) — ПЕРЕСОЗДАТЬ контейнер:
docker compose up -d backend        # restart НЕ перечитывает env!

# Обычное обновление кода:
docker restart vector-backend vector-frontend

Схема обновления, применяемая при разработке: правки коммитятся в Gitea → на сервере git pull → docker restart. После рестарта frontend пользователи перелогиниваются.


15. Резервное копирование

# Полный дамп БД (данные + OSM-таблицы)
docker exec vector-postgres pg_dump -U postgres -d vector_mchs -F c \
  -f /tmp/vector_mchs_$(date +%F).dump
docker cp vector-postgres:/tmp/vector_mchs_$(date +%F).dump /root/backups/

# Только прикладные данные (без тяжёлых planet_osm_* — их восстанавливают импортом дампа):
docker exec vector-postgres pg_dump -U postgres -d vector_mchs \
  --exclude-table='planet_osm_*' -F c -f /tmp/vector_app_$(date +%F).dump

Рекомендация: ежедневный cron прикладного дампа (он небольшой) + еженедельный полный. Хранить копии вне сервера.

Cron (рекомендуемый на сервере)

# 03:10 — ретенция журналов (удаление audit_events/auth_events старше audit_retention_days)
10 3 * * * docker exec vector-backend python -m backend.purge_logs >> /var/log/vector-purge.log 2>&1
# 03:00 — прикладной дамп (planet_osm_* исключены; OSM восстанавливается b16-import.sh)
0 3 * * * /root/vector/scripts/backup-app.sh >> /var/log/vector-backup.log 2>&1

backup-app.sh (в репо, scripts/) делает: дамп прикладных таблиц → выгрузка в /root/backups/ → архив .env + docker-compose.yml (chmod 600) → проверка оглавления через pg_restore → чистка старше 14 дней.

Проверка разового прогона: docker exec vector-backend python -m backend.purge_logs → purge: {'audit_events': N, 'auth_events': N} (0 — норма).

Грабля pg_wrapper PG18 (реальный прогон на CT108, postgres:16): pg_dump/pg_restore без пути — это клиент PG18. pg_dump 18 создаёт архив версии 1.16, который pg_restore 16 не читает (unsupported version), и пишет SET transaction_timeout, отсутствующий в PG16-сервере. Оба скрипта используют только /usr/lib/postgresql/16/bin/pg_*.

Docker volume — не backup. Если сервер умер вместе с диском, volume умер вместе с ним. Что подлежит резервному копированию:

БД (прикладные таблицы + OSM)
+ .env (JWT_SECRET, пароль БД, CONTOUR_TOKEN)
+ конфигурация compose

backup-app.sh покрывает БД и конфигурацию. Копии должны уходить с сервера — иначе гибель LXC/диска убивает и бэкапы. Варианты (по возрастанию надёжности):

  1. rsync на второй Proxmox-узел по SSH (минимум для пилота): rsync -az -e ssh /root/backups/ <второй-узел>:/root/backups/vector/ — добавить в backup-app.sh
  2. Syncthing/NAS (в инфраструктуре Виктора NAS уже есть)
  3. Для боевого контура МЧС — сетевой шарой организации

Backup ≠ восстановление. Копия проверяется периодическим restore на отдельном стенде: файл читается → БД восстанавливается → backend подключается → расчёт выполняется. Непроверенный restore — это не стратегия, а файл с именем «backup».

Восстановление

/root/vector/scripts/restore-app.sh /root/backups/vector_app_<дата>.dump

Скрипт: --clean --if-exists (пересоздание объектов из дампа) + TOC без EXTENSION postgis (иначе дропает postgis и падает на зависимостях planet_osm_*). OSM-таблицы не трогает (их в дампе нет). Проверено на CT108: restore → counts (cases/users/audit/password_history) совпали → login ok → analyze 200.

RPO и RTO

Величина Вопрос Рекомендация для пилота
RPO Сколько данных допустимо потерять Ежедневный дамп → RPO ≤ 24 ч. Потеря последних суток карточек; активная операция восстановляется из бумажного журнала смены
RTO Сколько времени допустимо восстанавливать Развернуть compose + restore дампа на новом LXC ≈ 30–60 мин (без импорта OSM — прикладной дамп + повторный импорт по необходимости; расчёт временно fallback'ится на Overpass)

16. Мониторинг и логи

docker compose logs -f                 # все сервисы
docker compose logs -f backend         # API + прикладные логи vector.*
docker compose logs -f frontend        # сборка CRA / ошибки компиляции
docker stats                           # потребление контейнеров
tail -f /tmp/vector/vector.log         # файл-лог внутри контейнера (ротация 5×2 МБ)

Прикладные события (B22, backend/logging_setup.py) — префикс vector.<модуль>:

# Все матрасчёты с длительностью
docker compose logs backend | grep 'vector.analyze'

# Входы и блокировки
docker compose logs backend | grep -E 'vector\.auth'

# Отправки в КОНТУР (ok/failed/rejected)
docker compose logs backend | grep 'vector.contour'

# Аудит-записи (операционный след; юридический — в БД audit_events)
docker compose logs backend | grep 'vector.audit'

Ключевые события: анализ case=… радиус=… время=…мс (каждый расчёт), login_ok/login_failed/lockout (безопасность), contour_send_ok/failed (связь с КОНТУРом), operation_complete op=… исход=… (завершение поиска), admin closed case … (синхронизация закрытия), Overpass … недоступен / OSM PostGIS недоступен (геослой ушёл в fallback).

Уровень детализации: VectorLOGLEVEL=debug в .env + docker compose up -d backend (restart не перечитывает env). Файл-лог отключается VectorLOGFILE=0.


17. Диагностика проблем

Быстрые проверки

docker compose ps                      # 4 контейнера Up/healthy
curl -s http://localhost:8000/api/v1/health   # {"status":"ok"}
df -h && free -h                       # диск/память

Типовые симптомы

Симптом Причина Решение
Логин даёт 422 JSON вместо form-urlencoded Логин строго application/x-www-form-urlencoded
«Session expired or revoked» сразу после логина Несовпадение sha256-токена Убедиться, что БД и backend на одной БД; пересоздать сессии
Селекты подразделений пусты Не выполнен сид юнитов python backend/seed_units.py
alembic upgrade → DuplicateTable create_all создал таблицы раньше миграций scripts/prod-stamp-backfill.sql, затем upgrade
Зоны «всегда вверх-вправо» Расчёт по case_id терял tnp_lat/lon Исправлено; при доработках сверять имена полей карточки с входом движка
Расчёт падает TypeError float×None Payload содержит None-поля SearchInput.to_case_data() выбрасывает None — регресс-тест есть
Правка .env не применилась restart не перечитывает env docker compose up -d backend (пересоздание)
Импорт OSM OOM Мало RAM Параметры --cache 800 --number-processes 2 уже в скрипте; закрыть лишние сервисы
КОНТУР недоступен при отправке зон Нет связи/токена curl до CONTOUR_API_URL; проверить CONTOUR_TOKEN; после правки env — up -d
Тайлы карты не грузятся Нет интернета у браузера Для полного offline — локальный tile-сервер (в планах)

Диагностика по цепочке (начинать с места проблемы, не с перезапусков)

Смена пароля даёт 400 «Пароль должен содержать буквы и цифры» / «совпадает с ранее использованным» — это B23-политика (security_settings), не ошибка: с 25.09.2026 требования сложности и истории исполняются. Пароль, возвращённый ранее, отвергается, пока он в последних password_history_count записях.

Бэкенд не подключается к БД — цепочка backend → docker network → postgres:5432 → authentication → vector_mchs:

docker compose logs backend | grep -iE 'error|failed|refused'
docker exec vector-backend python -c "from services.osm_local import osm_available; print(osm_available())"
docker exec vector-postgres pg_isready -U postgres

Проверить: DATABASE_URL (хост postgres, пароль совпадает с POSTGRES_PASSWORD), общую compose-сеть, состояние БД.

500 Internal Server Error — «500» это результат, а не диагноз. Сначала traceback:

docker compose logs backend --tail 100

Затем определить слой: FastAPI / SQLAlchemy / БД / данные / конфигурация. Прикладной контекст ищется по vector.* в логах.

Расчёт отдал fallback-зоны («Ближняя зона N / Средняя E») — геослой не ответил:

docker compose logs backend | grep -E 'vector\.(rules|geo)'

Geo/scoring service error → смотреть причину выше по стеку (PostGIS? Overpass breaker?). При работе на локальном OSM — проверить таблицы:

docker exec vector-postgres psql -U postgres -d vector_mchs -t -c \
  "SELECT count(*) FROM planet_osm_line;"

Пользователь не входит — цепочка login → bcrypt → lockout → session → JWT:

docker compose logs backend | grep 'vector.auth'
docker exec vector-postgres psql -U postgres -d vector_mchs -c \
  "SELECT username, is_active, status, failed_login_count, locked_until FROM users WHERE username='<логин>';"

401 после смены JWT_SECRET — все сессии инвалидны, ожидаемо: перелогин. 403 при валидном входе — проверить роль/юнит пользователя (скоуп).


17а. Чек-лист передачи проекта (по образцу РВС)

Код

  • код в Git (Gitea, Sadmin/vector), история коммитов осмысленная
  • production-конфигурация без паролей в репо (.env.example вместо .env)
  • README актуален (архитектура, API, БД, логирование §13а)
  • SETUP_GUIDE актуален (развертывание, диагностика)
  • тесты зелёные: pytest (239 passed)

База

  • известен production-сервер PostgreSQL и БД vector_mchs
  • схема актуальна: alembic current = 009_e3_operations
  • понятны таблицы OSM (planet_osm_*) и способ их восстановления (дамп + b16-import.sh)
  • сиды (users, units) воспроизводимы

Docker

  • понятны compose-файлы, volumes, сети
  • понятно, что можно пересоздавать (backend/frontend/adminer), а что содержит данные (postgres volume)
  • известен порядок обновления (§14) и отката

Security

  • JWT_SECRET не дефолтный; пароль БД сменён
  • пароль admin сменён; must_change_password для созданных пользователей работает
  • .env защищён (chmod 600, не в репо)
  • внешних ИИ/API-ключей нет (политика закрытого контура, тест TestNoLLM)
  • Adminer и порты БД не торчат наружу без необходимости

Логи и аудит

  • прикладные логи vector.* доступны (docker compose logs backend, файл в контейнере)
  • аудит 90 дней читается (вкладка «Аудит», право view_audit)
  • ретенция исполняется: cron purge-логов установлен, python -m backend.purge_logs даёт счётчики

Backup

  • cron прикладного дампа настроен (backup-app.sh, 03:00), копии вне сервера
  • первый дамп лежит в /root/backups/ и проверен pg_restore --list
  • restore проверен на стенде (restore-app.sh), counts совпали
  • копии выгружаются вне сервера (rsync на второй узел / NAS) — см. §15
  • RPO/RTO осознаны и приняты (§15)

18. Чек-лист развёртывания

  • Docker + Compose установлены
  • Код получен (/root/vector), .env создан из .env.example
  • JWT_SECRET заменён на случайный; пароль БД сменён (compose + DATABASE_URL)
  • docker compose up -d — 4 контейнера Up
  • alembic current = 010_b23_policy
  • cron бэкапа И cron purge-логов настроены (crontab -l), первый дамп лежит в /root/backups
  • seed_users.py выполнен, пароль админа сменён при первом входе
  • seed_units.py выполнен (142 подразделения в селектах)
  • PostGIS установлен в контейнере БД, extension создан
  • Дамп OSM импортирован (b16-import.sh), таблицы заполнены
  • Логин через UI работает, смена пароля обязательна
  • Пробный поиск: карточка → анализ → зоны на карте, радиус соответствует контрольным точкам
  • Создан тестовый пользователь ОУМЧС — скоуп видимости проверен
  • Завершение поиска: исход внесён, кейс в архиве, «прогноз vs факт» отображается
  • (опционально) КОНТУР: env-переменные, пересоздание backend, тестовая отправка зон
  • (опционально) HTTPS/обратный прокси настроен, CORS обновлён
  • Cron резервного копирования настроен, копия выгружена вне сервера
  • Внешние ИИ/API-ключи отсутствуют в .env и docker-compose.yml (политика закрытого контура)

Сопровождение: репозиторий Sadmin/vector, план работ — vector_tasks.md, описание системы — README.md.