22 KiB
type, work_item_id, title, version, status, created_at, authors
| type | work_item_id | title | version | status | created_at | authors | |
|---|---|---|---|---|---|---|---|
| infra-requirements | ET-008 | Инфраструктурные требования — ET-008: GPS-треки с публичных платформ | 1 | approved | 2026-06-01 |
|
Инфраструктурные требования — ET-008
1. Резюме
В отличие от ET-007 (только-фронтенд), ET-008 — серверная фича со scheduled-pipeline. Изменения охватывают:
- Новый docker-compose service
gps-collector(тот же образ, чтоapp, сprofiles: [batch]). - Новый файл БД на mva154:
data/gps_tracks.sqlite(≤ 2 ГБ). - Новая cron-запись на хосте mva154.
- Новый каталог логов
/var/log/enduro-trails/. - Новые Python-зависимости в общем образе:
defusedxml,pyyaml. - Новые исходящие HTTPS-вызовы из контейнера
gps-collectorк 1–3 внешним источникам.
Все изменения помещаются в существующий docker-compose стек без введения новых контейнеров API/нового reverse-proxy/новой БД-движка. Эскалация: arch:major-change (см. ADR-005, ADR-007).
2. Контейнеры и сервисы
| Аспект | Требование |
|---|---|
Новый сервис app (FastAPI) |
Не вводится; существующий API расширяется новыми routes /api/gps-tracks/* через регистрацию роутера из src/api/gps_tracks/endpoint.py |
Новый сервис gps-collector |
Да. docker-compose service, profiles: ["batch"], тот же build: ., command python -m scripts.gps_collect, restart: "no". Не стартует штатно при docker compose up -d. Активируется только запуском docker compose --profile batch run --rm gps-collector |
Изменение Dockerfile |
COPY scripts/ ./scripts/, COPY config/ ./config/. Текущий Dockerfile (COPY src/api/ src/api/, COPY src/web/ src/web/) не содержит scripts/ и config/ — нужно добавить две COPY-строки |
Новый блок в docker-compose.yml |
≈ 15 строк (см. ADR-007 §1) |
| Изменения OSRM, nginx | Нет |
| Перезапуск API после деплоя | Нужен (новые routes регистрируются при старте FastAPI) — стандартный docker compose up -d --no-deps app |
| Простой API | ≤ 5 секунд (рестарт контейнера API). Pipeline-сервис independent — его запуск/остановка не аффектит API |
2.1 Зависимости между сервисами
gps-collectorне имеетdepends_on: [app]. Он работает с БД-файлом напрямую через примонтированный volume/app/data.- В конце прогона pipeline дёргает HTTP
POST http://app:5556/api/gps-tracks/cache/clear(внутренняя docker-сеть). Еслиappнедоступен — pipeline пишет WARNING в лог, успех прогона не отменяется (ADR-007 §7). - Сетевое имя
appдоступно потому что оба сервиса в одной default-сети docker-compose.
2.2 Конфликт с production API во время прогона
- Pipeline пишет в
data/gps_tracks.sqliteв WAL-mode (ADR-005 §5). API читает ту же БД — видит снэпшот checkpoint'а; конкуренция не блокирует читателей. - CPU/RAM: pipeline ограничен через docker-cgroup limits (см. §9 ниже). Параллельный API не деградирует.
3. Сеть
| Аспект | Требование |
|---|---|
| Новые серверные порты на mva154 | Нет |
Изменения reverse proxy (/enduro/ в nginx) |
Минимальные. Новые routes /api/gps-tracks/* уже попадают под существующий location /api/ proxy_pass. Дополнительных правил не нужно |
| Внутренние DNS / docker-сеть | Стандартная default-сеть docker-compose. Service-name app резолвится в адрес API-контейнера; используется pipeline для cache-clear |
Endpoint POST /api/gps-tracks/cache/clear |
Ограничен docker-internal: блок RealIPFromTrustedProxy в nginx (proxy mva154) не пропускает POST на этот endpoint извне. Деталь: в nginx-конфиге location = /api/gps-tracks/cache/clear { allow 172.0.0.0/8; deny all; } — допуск только из docker-сетей |
| Новые исходящие HTTPS-вызовы из mva154 | Да. Из контейнера gps-collector:• api.openstreetmap.org (ADR-009) — всегда;• enduro-russia.ru (ADR-010) — пока accepted;• ttrails.ru (ADR-011) — пока accepted |
| Firewall mva154 | Исходящие HTTPS уже разрешены (BRD §7); правил не добавляется |
| Внешние входящие | Только существующий /enduro/ через nginx — без изменений |
3.1 Ограничение cache-clear
Cache-clear endpoint должен быть закрыт от внешнего интернета (он сбрасывает производительный кэш, потенциальный DoS-вектор). Реализация:
# /etc/nginx/sites-available/openclaw — добавляется в существующий server { } для /enduro/
location = /enduro/api/gps-tracks/cache/clear {
allow 172.16.0.0/12; # docker default networks
allow 127.0.0.1;
deny all;
proxy_pass http://app:5556/api/gps-tracks/cache/clear;
}
Pipeline дёргает endpoint напрямую через docker-сеть (http://app:5556/...), не через nginx → реальный путь обходит правило allow/deny и работает. Snippet выше защищает только публичный путь через /enduro/.
4. Хранилища данных
| Аспект | Требование |
|---|---|
| Новая БД | data/gps_tracks.sqlite (SQLite + Spatialite extension) |
| Расположение на хосте | /home/slin/enduro-trails/data/gps_tracks.sqlite (./data в docker-compose.yml) |
| Расположение в контейнерах | /app/data/gps_tracks.sqlite |
| Создание | Pipeline создаёт при первом запуске; миграция migrations/gps_tracks_001_init.sql применяется автоматически (см. §4.2) |
| Размер | Ожидаемо ≤ 500 МБ для ЦФО+Чувашии при 5000 треков; верхний предел операционный — 2 ГБ (REQ-NF-03). Алерт > 2 ГБ — см. 10-tech-risks.md R-4 |
| Spatialite-extension | Уже доступен в python-образе через pysqlite3-binary? Нет: текущий образ использует stdlib sqlite3. Нужно установить системный пакет libsqlite3-mod-spatialite (см. §4.3) |
Изменения схемы существующей centralfederal.sqlite |
Нет |
| Миграции существующих таблиц | Нет |
4.1 Зачем отдельная БД
См. ADR-005 §«Решение D-A». Изоляция backup-цикла, ротации, риска повреждения, write-конкуренции.
4.2 Миграция
migrations/gps_tracks_001_init.sql — IDempotent CREATE TABLE IF NOT EXISTS + R-tree creation. Применяется автоматически из src/api/gps_tracks/db.py::ensure_schema() при первом коннекте (ленивая инициализация). Никакого alembic или внешнего раннера миграций.
4.3 Установка Spatialite в Docker-образе
Изменение Dockerfile:
FROM python:3.12-slim
WORKDIR /app
# ET-008: Spatialite extension для slot.api.gps_tracks.db
RUN apt-get update && apt-get install -y --no-install-recommends \
libsqlite3-mod-spatialite \
&& rm -rf /var/lib/apt/lists/*
COPY src/api/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/api/ ./src/api/
COPY src/web/ ./src/web/
COPY scripts/ ./scripts/ # ET-008
COPY config/ ./config/ # ET-008
ENV STATIC_DIR=/app/src/web
ENV PORT=5556
EXPOSE 5556
CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "5556"]
Образ увеличится на ≈ 30 МБ (модуль Spatialite). На размер production-нагрузки не влияет.
4.4 Backup
- Ежедневный snapshot через cron на mva154:
0 5 * * * root sqlite3 /home/slin/enduro-trails/data/gps_tracks.sqlite ".backup /home/slin/enduro-trails/backups/gps_tracks-$(date +\%F).sqlite" - Retention 14 дней — отдельный
find ... -mtime +14 -delete. - Pipeline-running во время backup допустим:
.backupв sqlite3 — атомарный, использует WAL. - Восстановление: остановить
gps-collectorзапуски,cpsnapshot вdata/gps_tracks.sqlite, перезапустить API (cache-clear автоматически).
4.5 Клиентское хранилище
| Ключ localStorage | Значение | Default |
|---|---|---|
gps-tracks-enabled |
"true" | "false" |
"false" |
gps-tracks-activities |
JSON-array | все ACTIVITY_TYPES |
gps-tracks-sources |
JSON-array | все enabled source IDs |
gps-tracks-color-mode |
"source" | "activity" |
"source" |
Суммарный объём ≤ 256 байт. Конвенция имён согласуется с существующими (enduro-theme-mode, terrain-*, trails-*, map-base-layer).
Подробности — 08-data-requirements.md §4.
5. Конфигурация и секреты
| Аспект | Требование |
|---|---|
| Новые env-переменные API-контейнера | GPS_TRACKS_DB_PATH=/app/data/gps_tracks.sqlite |
| Новые env-переменные gps-collector | GPS_TRACKS_DB_PATH, GPS_SOURCES_CONFIG=/app/config/gps_sources.yaml, GPS_REGIONS_CONFIG=/app/config/gps_regions.yaml |
| Новые секреты / API-ключи | Нет — все источники без авторизации (см. ADR-009, ADR-010, ADR-011 — outside source без ключа; платные API явно out of scope BRD §3) |
| Новые конфиг-файлы в репозитории | config/gps_sources.yaml, config/gps_regions.yaml — оба под git-контролем |
| Изменения reverse-proxy / nginx | Только cache-clear защита (§3.1) |
| Изменения OSRM | Нет |
6. Зависимости
| Аспект | Требование |
|---|---|
Python-пакеты (src/api/requirements.txt) — добавить |
defusedxml==0.7.1 (безопасный XML-парсинг GPX), pyyaml==6.0.1 (конфиги pipeline) |
| Python-пакеты — НЕ добавлять | lxml (упомянут в BRD §7 как опция; для GPX-парсинга достаточно defusedxml.ElementTree; экономит ≈ 8 МБ образа). tenacity — реализуем backoff inline (≈ 30 строк, TRZ §6.3) чтобы не вводить ещё один пакет |
| Системные библиотеки в Dockerfile | libsqlite3-mod-spatialite (см. §4.3) |
| Версия Python | 3.12, без изменений |
| Новые third-party runtime-зависимости (внешние сервисы) | • api.openstreetmap.org — OSM API (ADR-009)• enduro-russia.ru — после ADR-010 accepted• ttrails.ru — после ADR-011 accepted |
| Альтернативные источники / fail-over | Не закладывается; каждый source изолирован (ADR-007 §I-A); падение одного не валит других |
7. Сборка и деплой
-
Pipeline CI: существующий Gitea Actions (
make lint+make test+make build). Новые backend-tests (tests/api/test_gps_tracks_*.py) добавляются в существующий pytest. Новые frontend-tests — в существующий ESLint и JS-test pipeline. -
Артефакт: Docker-образ. После ET-008 один образ запускается двумя сервисами (
appиgps-collectorчерезprofiles). Это стандартный паттерн docker-compose. -
Деплой шаг-за-шагом:
git pull origin mainна mva154.docker compose build(пересобирает образ сlibsqlite3-mod-spatialite).docker compose up -d --no-deps app(перезапускает только API;gps-collectorprofile-disabled).- Установить cron-запись (см. §8).
- Первый ручной запуск pipeline в dry-run:
docker compose --profile batch run --rm gps-collector python -m scripts.gps_collect --dry-run - Проверить
/api/gps-tracks/health— БД создана, пуста. - Запустить production-сбор:
docker compose --profile batch run --rm gps-collector(≤ 6 часов). - Smoke: открыть
/enduro/, включить чекбокс «Публичные треки», убедиться что слой виден.
-
Время простоя API: ≤ 5 секунд на шаге 3.
-
Время простоя pipeline: не применимо — pipeline не daemon.
7.1 Cron-запись
/etc/cron.d/enduro-gps (root-owned, 0644):
# ET-008: GPS Tracks Pipeline
# Mon + Thu 03:00 UTC — full collection
0 3 * * 1,4 root cd /opt/enduro-trails && /usr/bin/docker compose --profile batch run --rm gps-collector >> /var/log/enduro-trails/gps-collect.log 2>&1
# 1-е число каждого месяца 04:00 UTC — GC stale tracks
0 4 1 * * root cd /opt/enduro-trails && /usr/bin/docker compose --profile batch run --rm gps-collector python -m scripts.gps_collect --gc >> /var/log/enduro-trails/gps-gc.log 2>&1
Никаких отдельных flock / lockfile — cron-окно (3 дня) > длительности прогона (≤ 6 ч).
7.2 Rollback
| Откат | Действие | Время |
|---|---|---|
| Откат кода (revert + redeploy) | git revert <commit> && docker compose up -d --build app |
≈ 2 мин |
| Откат БД (повреждение / неверная схема) | Остановить gps-collector cron, cp backups/gps_tracks-<date>.sqlite data/gps_tracks.sqlite, рестарт API |
≈ 1 мин |
| Полный отказ от фичи (kill switch) | Закомментировать cron-строки, удалить gps-tracks-cb checkbox в UI через display:none |
≈ 1 мин |
| Откат от pipeline без отката API | Закомментировать cron-строки — API продолжает отдавать собранное | мгновенно |
Скрипт scripts/disable_gps_pipeline.sh (TODO в 04-test-plan.yaml) автоматизирует «kill switch».
8. Cron / scheduled jobs
См. §7.1.
Мониторинг cron:
- При сбое cron-job отправляется email на адрес администратора через стандартный
cron MAILTO=(mva154 уже настроен). Опционально — алерт в Telegram, но это outside scope (если в проекте уже есть алерт-канал — используется он). /api/gps-tracks/healthотдаётlast_pipeline_run.sources_error— оператор видит при ручной проверке/мониторинге.
9. Ресурсы (CPU / RAM / диск)
9.1 API-контейнер
- CPU: +5% от текущего baseline за счёт MVT-генерации нового слоя. На существующем mva154 (по BRD §1 одиночный сервер) — не критично.
- RAM: +50 МБ baseline (новые модули) + до 64 МБ LRU-кэш MVT-тайлов (1024 × ~64 КБ). Итого +120 МБ. Текущий API использует ≈ 200 МБ; после ET-008 — ≈ 320 МБ.
- Network egress: +0 (внутри сервера; клиент скачивает с того же mva154).
9.2 gps-collector контейнер (во время прогона)
- CPU: ограничен docker-compose cgroup
cpus: "1.0"(один логический CPU) — pipeline не вытесняет API. - RAM: ограничен
mem_limit: 512m. На практике pipeline + asyncio + httpx + shapely + спарс одного парсера ≤ 200 МБ; запас 2.5×. - Network egress (mva154 → external): для OSM ≈ 100 МБ за прогон (≤ 5000 треков × ≤ 20 КБ), для скрейпинга — порядок 10–100 МБ. Полная стоимость cron-прогона ≈ 200 МБ / неделю — пренебрежимо.
- Network ingress: не применимо.
# docker-compose.yml фрагмент
services:
gps-collector:
# ...
cpus: "1.0"
mem_limit: 512m
pids_limit: 256
9.3 Диск
data/gps_tracks.sqlite— ≤ 2 ГБ.- Лог-файлы
/var/log/enduro-trails/*.log— ротация через logrotate, default 14 дней × ≤ 50 МБ = ≤ 700 МБ. - Backup-снапшоты — ≤ 14 × 2 ГБ = ≤ 28 ГБ (с retention; см. §4.4).
- Сумма: + ≈ 30 ГБ на текущий disk-budget mva154.
10. Наблюдаемость
| Артефакт | Источник | Использование |
|---|---|---|
GET /api/gps-tracks/health |
API (читает pipeline_runs из БД) |
Оператор проверяет вручную или через monitoring |
/var/log/enduro-trails/gps-collect.log |
Cron stdout/stderr | Лог cron-выполнений: успех/код возврата/исключения |
/var/log/enduro-trails/pipeline-<run_id>.jsonl |
Pipeline structured log | Per-run JSON-lines: source, region, статус, tracks_new |
pipeline_runs в БД |
Pipeline-side | Историческая трассировка для health-эндпоинта |
Docker docker compose logs app |
API stdout | Запросы /api/gps-tracks/*, ошибки SQL |
10.1 Алерты
- Cron MAILTO при ненулевом exit code прогона — стандартный механизм.
- 2 неудачных прогона подряд для одного source —
pipeline_runsсобирает; алерт не автоматический (out of MVP), оператор увидит при ручной проверке/healthили в weekly review. Алерт-канал — отдельный work item. - db_size_mb > 2 ГБ — health отдаёт значение; внешний мониторинг (если есть) пинает.
- Ошибка лицензионного guard'а (
status: "skipped_license") — оператор видит вpipeline_runs; не алерт-кейс, нормальное поведение до accepted-ADR.
10.2 Logrotate
# /etc/logrotate.d/enduro-gps
/var/log/enduro-trails/*.log {
daily
rotate 14
compress
missingok
notifempty
}
/var/log/enduro-trails/pipeline-*.jsonl {
weekly
rotate 8
compress
missingok
notifempty
}
11. Безопасность
- Парсинг XML на сервере (GPX) — через
defusedxml.ElementTree(защита XXE / billion laughs).lxmlне используется. - Endpoint
POST /api/gps-tracks/cache/clear— ограничен docker-internal сетью на уровне nginx (§3.1). Pipeline ↔ API остаются связаны через docker-сеть. - Скрейпинг — только outgoing с mva154. Никаких open ports.
- Атаки на pipeline через подделанные GPX (источник вернул malformed XML, exploding XML) — митигируется
defusedxmlи timeouthttpx.get(timeout=30). Per-track exception isolated в pipeline-loop. - CSP-заголовок — в проекте отсутствует (см. ET-007 §3.2). ET-008 ничего не меняет.
12. Влияние на C4 / архитектурную документацию
Изменения состава компонентов:
- Новый компонент в стеке mva154: docker-compose service
gps-collector(batch). - Новая БД
data/gps_tracks.sqlite. - Новые внешние зависимости рантайма: 1–3 платформы (OSM всегда + 0/1/2 после ADR-010/011).
- Новые scheduled-jobs: 2 cron-записи.
docs/architecture/README.md обновляется новым разделом «GPS Tracks Pipeline (ET-008)» с описанием компонента, БД, внешних зависимостей и расписания.
docs/architecture/adr/README.md пополняется записями ADR-005..ADR-011.
C4 mmd-диаграмм в проекте нет — текстовое описание (по прецеденту ADR-004 §8).
13. Вывод
ET-008 — major-change на инфра-уровне:
- Новый docker-compose service.
- Новый файл БД.
- Первые scheduled jobs (cron) на mva154.
- Новые исходящие сетевые соединения с обязательными licensing-ADR.
Все элементы — расширение существующего стека (не новый stack). Реверсная процедура и rollback — однострочные операции.
Эскалация: лейбл arch:major-change выставлен на ADR-005 и ADR-007. Архитектурный approve обязателен перед merge.