Files
lidar_rendu/docs/DEPLOY_WEBAPP.md
Antoine Jacquin 0a2ccee958 Porter la génération de tuiles dans lidar-maps et retirer l'ancienne webapp
La carte à tuiles XYZ devient la seule interface : l'API de génération
(preview, generate, status, stop, file, cell), le job unique + file
persistante, la délégation LIDAR_GENERATION_URL et les garde-fous
(token, LIDAR_REGEN_CIDR) passent de webapp.py à mapserve.py ; l'interface
gagne les boutons + Zone / ⤒ Compléter / régénération à la dalle, les
options de run et la progression cadre par cadre. mapserve sert aussi
l'inventaire /api/tiles et les dalles en statique versionné : le worker
image complète remplace la webapp sur le 8973 du générateur, le Pi
n'exécute plus que lidar-maps. index.py se réduit aux registres partagés
+ vignettes/sous-tuiles/inventaire (l'UI HTML/JS et les mosaïques
d'overview partent avec export.py et les compose/scripts de la webapp).
2026-09-23 23:21:05 +02:00

7.6 KiB
Raw Blame History

Déploiement carte légère + machine de traitement

Architecture deux machines : la carte (interface, pyramide de tuiles XYZ, API) tourne sur un Raspberry Pi ; la génération de tuiles (téléchargement IGN, PDAL, GPU) tourne sur une machine puissante. Les deux exécutent le même serveur (lidar_pipeline.mapserve, image lidar-maps), la machine puissante en version complète (pipeline inclus).

Navigateur ──HTTP──▶ Raspberry Pi (image légère lidar-maps, port 8975)
                       │  sert la carte + la pyramide (cache seule + maintenance
                       │  de fond, LIDAR_TILE_CACHE_ONLY / LIDAR_TILE_BACKGROUND)
                       │  /api/generate, /api/preview, /api/status
                       │     └─ transmis à ──▶ machine de traitement
                       │  dalles rapatriées du worker (LIDAR_SOURCE_URL :
                       │     inventaire /api/tiles + statiques versionnées)
                       ▼
                  Machine de traitement (image complète,
                  docker-compose.worker.yml service `worker`) :
                  téléchargement IGN + pipeline GPU (générateur de tuiles)
                  + pyramide de tuiles + inventaire des dalles

Machine de traitement (générateur de tuiles)

Le service worker joue le rôle de générateur : il accepte les demandes de génération envoyées par les cartes distantes (téléchargement IGN + traitement PDAL/GPU), sert sa propre pyramide de tuiles et l'inventaire des dalles (/api/tiles + visualisations/, index_thumbs/, index_subtiles/ en statique) que les machines légères rapatrient à la demande.

docker compose -f docker-compose.worker.yml up -d --build   # API sur http://<ip-machine>:8973
docker compose -f docker-compose.worker.yml logs -f worker

Traitement batch ponctuel des dalles déjà présentes dans input/ :

docker compose -f docker-compose.worker.yml run --rm --build process [-r 0.2 | --force]

Sur une machine sans GPU : retirer les lignes gpus: all (traitement CPU, plus lent). Optionnel mais recommandé si le réseau n'est pas de confiance : protéger l'API avec un jeton partagé — décommenter dans docker-compose.worker.yml :

environment:
  - LIDAR_API_TOKEN=un-secret-à-partager

Raspberry Pi (carte légère)

0. Prérequis sur le Pi

  • Architecture : image construite nativement sur la machine (ARM64 ou x86_64, vérifier avec uname -m). Le build se fait sur le Pi lui-même (Dockerfile.maps : base python:3.12-slim, ~200 Mo, sans PDAL/GDAL).
  • Docker + plugin compose : installation officielle docs.docker.com/engine/install (tester avec docker compose version).
  • Espace disque : prévoir la taille du cache (dalles rapatriées + tuiles rendues ; compter la taille de output/ sur la machine de traitement).

1. Copier le code sur le Pi (git clone)

git clone ssh://git@git.example.fr:2222/code_public/lidar_rendu.git lidar
cd lidar

output/ et input/ sont ignorés par git : le dépôt ne contient que le code, le cache se remplit à la demande depuis la machine de traitement.

2. Lancer la carte

docker-compose.maps.yml (versionné) sert de base ; la configuration locale du Pi vit dans un override docker-compose.maps.override.yml (non versionné) :

name: lidar-maps
services:
  maps:
    volumes: !override            # Compose >= 2.24 (remplace ./output)
      - /srv/lidar/output:/data/output
    mem_limit: 1g
    memswap_limit: 1g
    environment:
      - TZ=Europe/Paris
      - LIDAR_SOURCE_URL=http://192.168.1.50:8973      # worker (dalles + inventaire)
      - LIDAR_GENERATION_URL=http://192.168.1.50:8973  # worker (génération déléguée)
      # - LIDAR_REMOTE_TOKEN=un-secret-à-partager     # si LIDAR_API_TOKEN côté worker
      - LIDAR_TILE_WORKERS=1
      - LIDAR_TILE_SOURCE_CACHE_MB=64
      - LIDAR_TILE_CACHE_ONLY=1        # la navigation ne rend RIEN
      - LIDAR_TILE_BACKGROUND=1        # la pyramide est entretenue en tâche de fond
      - LIDAR_TILE_BACKGROUND_PAUSE=1.0
    networks: [webapp, proxy]
    labels:                           # routage Traefik éventuel
      - "traefik.enable=true"
      - "traefik.http.routers.lidar-maps.rule=Host(`lidar.example.fr`)"
      - "traefik.http.routers.lidar-maps.entrypoints=websecure"
      - "traefik.http.routers.lidar-maps.tls.certresolver=myresolver"
      - "traefik.http.services.lidar-maps.loadbalancer.server.port=8975"

networks:
  webapp:
    name: lidar_rendu_default
    external: true
  proxy:
    name: proxy
    external: true
mkdir -p /srv/lidar/output   # appartenant à l'uid 1000 (conteneur)
docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build

La carte est sur http://<ip-pi>:8975/ (et via Traefik si configuré).

LIDAR_TILE_CACHE_ONLY=1 + LIDAR_TILE_BACKGROUND=1 inversent la charge sur un petit Pi : la navigation ne rend rien (tuile absente = transparente, X-Tile-Pending), une tâche de fond surveille les dalles nouvelles ou régénérées et entretient la pyramide à basse priorité — cf. docs/MAPS.md § « Cache seule + maintenance de fond ».

3. Génération de tuiles depuis la carte

Les boutons + Zone (dessiner un rectangle → téléchargement IGN + run sur le worker), ⤒ Compléter (dalles déjà téléchargées incomplètes) et ↻ Générer cette dalle (fiche d'infos au clic) sont masqués si :

  • le navigateur vient d'une IP hors LIDAR_REGEN_CIDR (défaut : boucle locale + plages privées RFC1918 ; liste de CIDR séparés par virgules, chaîne vide pour lever la restriction). L'IP est celle de la connexion (conservée par le DNAT Docker pour les clients du LAN) ; derrière un reverse proxy local (dans le réseau autorisé), X-Forwarded-For désigne le client réel ;
  • le worker est injoignable ET aucun pipeline local n'existe.

Une demande lancée pendant un run part en file d'attente côté worker (jamais de coupure du travail en place) ; la progression s'affiche dalle par dalle (cadres orange/bleu/rouge sur la carte) et le bouton Arrêter envoie un SIGTERM au pipeline. À la fin du run, la carte se rafraîchit et la maintenance de pyramide repart automatiquement.

4. Centrer la carte sur la position GPS (téléphone)

Les navigateurs n'exposent l'API Geolocation qu'en contexte sécurisé (HTTPS) : servir la carte derrière Traefik (TLS) comme ci-dessus, ou en dernier recours en HTTPS direct (l'entrée autonome python -m lidar_pipeline.mapserve honore LIDAR_SSL_CERTFILE / LIDAR_SSL_KEYFILE).

Mise à jour

Machine de traitement

cd <checkout> && git pull && docker compose -f docker-compose.worker.yml up -d --build

Pi (depuis le poste de pilotage)

Le poste de pilotage interdit l'appel ssh direct : le wrapper ssh fournit l'accès autorisé avec transfert d'agent (-A) — les git pull distants utilisent la clé locale :

#!/bin/bash
set -euo pipefail
HOST="${1:-192.168.3.10}"   # machine carte par défaut (le Pi)
shift || true
exec ssh -A -o BatchMode=yes -o ConnectTimeout=5 \
    -o StrictHostKeyChecking=accept-new "$HOST" "$@"

Mise à jour complète du Pi (checkout git en /srv/lidar_rendu, avec l'override Traefik) :

ssh 192.168.3.10 "cd /srv/lidar_rendu && git pull && \
  docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build"

Le code de l'interface est bâché dans l'image (Dockerfile.maps) : TOUT changement d'interface exige le rebuild (--build), sans lui l'ancien code tourne.