gendesign/docs/osrm-routing.md
Light1YT 26f9605c06
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
feat(devops): self-hosted OSRM routing engine for site-finder (#39)
Stand up an internal-only OSRM container so /analyze can later use real
road/walking distances to POI instead of straight-line ST_Distance. Infra
only — the /analyze integration is a separate follow-up (A2/A3).

- docker-compose.prod.yml: osrm service (osrm/osrm-backend, --algorithm mld,
  mem_limit 1.5g, default network, no public port — backend reaches it at
  http://osrm:5000). TCP /dev/tcp healthcheck (image has no curl). backend does
  NOT depend_on osrm, so a crash-loop before the graph is built won't block deploy.
- scripts/build_osrm.sh: idempotent MLD build/refresh (extract→partition→customize,
  not contract). Defaults to Свердл-clip из Ural-FO via osmium (lighter RAM);
  CLIP=0 zero-clip and EKB_TIGHT=1 / WALK=1 options documented. Monthly-refresh note.
- docs/osrm-routing.md: region/pbf decision, RAM table, VPS run-commands, verify curl.
- .gitignore data/osrm/* (don't commit ~1.5GB build output); .gitkeep holds the dir.

Refs #39
2026-06-27 00:13:41 +05:00

6.3 KiB
Raw Blame History

OSRM routing engine (self-hosted) — #39

Self-hosted OSRM для site-finder /analyze: реальные дорожные / пешие расстояния до POI вместо straight-line ST_Distance(geography).

Scope этого PR — INFRA ONLY. Здесь стоит контейнер + build-скрипт + healthcheck. Интеграция в /analyze (замена ST_Distance на OSRM route/table) — отдельный follow-up (A2 изохроны, A3 per-category routing-decay). Backend пока к osrm НЕ ходит.

Архитектура

backend ──http://osrm:5000──▶ osrm (osrm-routed --algorithm mld)
                                 │ volume ./data/osrm:/data
                                 └─ /data/sverdlovsk.osrm*  (MLD-граф, ~ХХХ MB)
  • Compose service osrm в docker-compose.prod.yml, сеть default (project gendesign).
  • Наружу НЕ публикуется — нет ports:. Достижим только из backend по http://osrm:5000. Caddy его не проксирует (routing — internal API, не должен быть публичным).
  • mem_limit: 1.5g, restart: unless-stopped.
  • Граф (/data/*.osrm*) НЕ в git (.gitignoredata/osrm/*). Строится offline на VPS.

Build / refresh графа

Скрипт: scripts/build_osrm.sh. Запускается вручную на VPS (НЕ часть deploy.yml — download+build тяжёлые).

cd /opt/gendesign
bash scripts/build_osrm.sh            # car-профиль, Свердл-clip (default)

Источник pbf и решение по региону

Geofabrik не публикует Свердловскую область отдельно — есть только Уральский ФО (≈600 MB). Скрипт по умолчанию (CLIP=1) скачивает Ural-FO и обрезает по bbox Свердл. обл. через osmium extract → легче граф, меньше RAM в runtime.

Режим Команда pbf Граф в RAM (warm) Когда
Свердл-clip (default) bash scripts/build_osrm.sh clip из Ural-FO ~0.81.2 GB рекоменд., extensibility на обл
ЕКБ-tight EKB_TIGHT=1 bash scripts/build_osrm.sh узкий bbox ЕКБ ~0.40.6 GB если RAM на VPS критична
Ural-FO zero-clip CLIP=0 bash scripts/build_osrm.sh весь Ural-FO ~1.5 GB+ если нужен запас на соседние регионы

bbox Свердл (с запасом): lon 57.2..66.2, lat 56.0..62.0. ЕКБ-tight: lon 59.0..62.5, lat 56.2..58.2.

Pipeline (MLD, не CH)

--algorithm mld в compose требует pipeline extract → partition → customize (скрипт делает это через тот же osrm/osrm-backend образ). osrm-contract — это CH-алгоритм, он несовместим с --algorithm mld, поэтому НЕ используется.

Walking-профиль (Phase-2, опционально)

Issue хочет оба профиля (car + foot) eventually. Этот PR car-first. Пеший граф:

WALK=1 bash scripts/build_osrm.sh     # дополнительно соберёт sverdlovsk-foot.osrm*

Для пешего routing нужен второй compose-service (он не добавлен в этом PR, чтобы не плодить idle-контейнер пока /analyze не интегрирован). Блок для будущего:

  osrm-walk:
    image: osrm/osrm-backend:latest
    restart: unless-stopped
    command: osrm-routed --algorithm mld --max-table-size 8000 /data/${OSRM_REGION:-sverdlovsk}-foot.osrm
    volumes: [./data/osrm:/data]
    mem_limit: 1g
    healthcheck:
      test: ["CMD-SHELL", "timeout 3 bash -c '</dev/tcp/127.0.0.1/5000' || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 40s
    networks: [default]

Backend ходил бы к нему по http://osrm-walk:5000.

Monthly refresh

osm.pbf на Geofabrik обновляется ежедневно. Граф освежать ~ежемесячно:

0 4 1 * * cd /opt/gendesign && bash scripts/build_osrm.sh >> /var/log/osrm-build.log 2>&1 \
  && docker compose -p gendesign -f docker-compose.prod.yml up -d --force-recreate --no-deps osrm

Скрипт idempotent: wget -N качает только если на сервере новее, граф перестраивается overwrite.

Развёртывание (что выполняет оркестратор на VPS)

cd /opt/gendesign
git fetch origin main && git reset --hard origin/main     # подтянуть compose + скрипт

# 1. Build графа (~3050 мин, ~600MB download + ~3GB peak RAM на extract)
bash scripts/build_osrm.sh

# 2. Поднять контейнер
docker compose -p gendesign -f docker-compose.prod.yml up -d osrm

# 3. Verify (curl ВНУТРИ образа нет — используем sidecar curl в той же сети)
docker run --rm --network gendesign_default curlimages/curl:8 -fsS \
  'http://osrm:5000/route/v1/driving/60.6,56.84;60.65,56.86'
# ожидаем JSON: {"code":"Ok","routes":[{...}], ...}

Healthcheck

Compose-healthcheck — TCP-probe порта 5000 через bash /dev/tcp (образ osrm/osrm-backend НЕ содержит curl/wget, HTTP-probe падал бы на missing binary). Полный route-probe — verify-команда выше (sidecar curl).

Риски / open

  • RAM на Beget VPS: +~1 GB warm для Свердл-clip. Перед up -d osrm проверить свободную RAM (free -h). Если жмёт — EKB_TIGHT=1 (~0.40.6 GB) или upgrade plan.
  • Build на VPS медленный (50+ мин). Альтернатива: собрать локально и scp data/osrm/*.osrm* в /opt/gendesign/data/osrm/.
  • Размер pbf неточен: Ural-FO ≈600 MB на скачку; «1.5GB» из issue — оценка по всему ФО + intermediate-файлы на диске во время build (нужно ~5 GB свободного диска).