Files
lidar_rendu/docs/DEPLOY_WEBAPP.md
Antoine Jacquin 23969c9e14 Add Leaflet interactive map, tile generator compose, auto-sync cache, deploy docs
index.py rewritten as a continuous Leaflet map: rotated L93 tiles, stackable
visualization layers (per-layer opacity, drag-reorder persisted in
localStorage), tile info panel, live rebuild after each tile during a run.
Leaflet is vendored in assets/vendor/ so the map works fully offline;
georeferencing falls back rasterio -> pyproj -> affine so the lightweight
webapp (no GDAL) is supported.

docker-compose.worker.yml adds the tile generator service (full image + GPU)
that remote webapps call via LIDAR_GENERATION_URL, plus a one-shot process
profile. webapp.py gains LIDAR_AUTO_SYNC_SECONDS periodic cache refresh and
LIDAR_REGEN_CIDR restricting generation to the local network. run.sh
--serve-webapp now mounts ~/.ssh read-only so the rsync sync works.

docs/DEPLOY_WEBAPP.md completed for Raspberry Pi deployment: prerequisites,
git clone install, SSH key setup, first sync, update procedure and
troubleshooting.
2026-09-04 21:36:14 +02:00

11 KiB
Raw Blame History

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

Architecture deux machines : la webapp (carte interactive, vignettes, 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 code (lidar_pipeline.webapp), dans deux images Docker différentes.

Navigateur ──HTTP──▶ Raspberry Pi (image légère, Dockerfile.webapp)
                       │  sert carte + vignettes (régénérées localement)
                       │  /api/generate, /api/preview, /api/status
                       │     └─ transmis à ──▶ machine de traitement
                       │  /api/sync : rsync output/ ──◀── machine puissante
                       ▼
                  Machine de traitement (image complète,
                  docker-compose.worker.yml service `worker`) :
                  téléchargement IGN + pipeline GPU (générateur de tuiles)

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 webapp distantes (téléchargement IGN + traitement PDAL/GPU) et expose les tuiles produites pour le rsync.

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.5,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 (webapp 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.webapp : 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).
  • rsync/ssh côté client : inutile sur l'hôte — ils sont dans l'image — mais la clé SSH de l'hôte est montée dans le conteneur.
  • Espace disque : prévoir la taille du cache de tuiles (compter la taille de output/ sur la machine de traitement, ~quelques dizaines de Mo par dalle et par résolution).

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

Le clone SSH utilise la même clé que le rsync (~/.ssh, voir ci-dessous) ; si le serveur git n'est pas encore connu, faire une première connexion pour accepter son empreinte. output/ et input/ sont ignorés par git : le dépôt ne contient que le code, le cache de tuiles se remplit ensuite par rsync (premier chargement, section 4).

2. Accès SSH pour le rsync (la Pi tire les tuiles traitées)

ssh-keygen -t ed25519                     # si pas encore de clé
ssh-copy-id lidar@<ip-machine>            # compte lecture sur output/
ssh lidar@<ip-machine> exit               # 1re connexion : enregistre known_hosts

La dernière commande évite le prompt « authenticity of host » pendant le rsync (le conteneur ne peut pas répondre interactivement).

Sur la machine puissante, le dossier output/ doit être lisible par ce compte (ex. /srv/lidar/output si vous préférez un chemin dédié — adaptez LIDAR_SYNC_CMD).

Avec run.sh, ~/.ssh (clé + known_hosts) est monté automatiquement en lecture seule dans le conteneur ; avec docker compose, le montage équivalent est à décommenter dans docker-compose.webapp.yml (voir l'option b ci-dessous).

3. Configurer et lancer

Deux façons, au choix :

a. run.sh (le plus simple) — conteneur webapp seul, cache local des tuiles ; les variables se passent depuis l'environnement hôte, ~/.ssh est monté automatiquement :

LIDAR_GENERATION_URL=http://192.168.1.50:8973 \
LIDAR_REMOTE_TOKEN=un-secret-à-partager \
LIDAR_SYNC_CMD="rsync -a --delete --exclude=*.tif --exclude=.generation* --exclude=index_thumbs --exclude=index_subtiles lidar@192.168.1.50:/srv/lidar/output/ /data/output/" \
LIDAR_AUTO_SYNC_SECONDS=600 \
./run.sh --serve-webapp        # port 8973, ou --serve-webapp 9000

LIDAR_AUTO_SYNC_SECONDS entretient le cache tout seul : toutes les N secondes, rsync ramène les nouvelles tuiles et les vignettes manquantes sont régénérées (les mtimes évitent tout recalcul inutile). Sans cette variable, le cache se rafraîchit à la demande : bouton ↻, fin d'un run, ou POST /api/sync.

LIDAR_REGEN_CIDR restreint le lancement des générations (POST /api/generate : zones, complétion, régénération) aux clients dont l'IP est dans le réseau indiqué — défaut 192.168.1.0/24, 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. Les autres clients consultent la carte normalement : les boutons de génération sont masqués et l'API répond 403.

b. docker compose — éditer docker-compose.webapp.yml :

  • LIDAR_GENERATION_URL : http://<ip-machine>:8973
  • LIDAR_REMOTE_TOKEN : la valeur de LIDAR_API_TOKEN de la machine (inutile si aucun token là-bas)
  • LIDAR_SYNC_CMD : la commande rsync qui copie output/ distant vers /data/output/ local, ex :
- LIDAR_SYNC_CMD=rsync -a --delete --exclude=*.tif --exclude=.generation* --exclude=index_thumbs --exclude=index_subtiles lidar@192.168.1.50:/srv/lidar/output/ /data/output/

Décommenter aussi le montage de la clé SSH (rsync) :

    volumes:
      - ./output:/data/output
      - ~/.ssh:/home/lidar/.ssh:ro

Les vignettes (index_thumbs/, index_subtiles/) ne se synchronisent pas : elles sont régénérées sur place par le Pi (/api/sync → build_index), c'est le seul travail lourd qu'il fait. Exclure *.tif évite de copier des intermédiaires éventuels ; les sidecars output/DTM/*_method.txt servent au panneau d'infos et sont synchronisés.

mkdir -p output
docker compose -f docker-compose.webapp.yml up -d --build

La carte est sur http://<ip-pi>:8973/.

run.sh lance le conteneur au premier plan (Ctrl-C arrête tout) : pratique pour tester. Pour une installation permanente, préférer docker compose (restart: unless-stopped : la carte revient après un reboot du Pi).

4. Premier chargement

Si aucune tuile n'a encore été synchronisée, déclencher une première fois :

curl -X POST http://<ip-pi>:8973/api/sync

(puis attendre la fin : curl http://<ip-pi>:8973/api/sync → "running": false.) Le bouton ↻ de la carte fait la même chose (sync + vignettes).

Mise à jour

Sur le Pi, après un git pull (ou rsync du code) :

docker compose -f docker-compose.webapp.yml up -d --build   # rebuild + redémarrage

Avec run.sh --serve-webapp, l'image est reconstruite automatiquement à chaque lancement ; il suffit de relancer la commande. Le cache de tuiles (output/) n'est pas affecté par les mises à jour d'image.

Dépannage

  • État d'une sync : curl http://<ip-pi>:8973/api/sync → running, phase (sync puis index) et error (dernière erreur, ex. fin de journal rsync en cas d'échec). Les logs du conteneur : docker logs lidar-webapp (ou docker compose -f docker-compose.webapp.yml logs -f webapp).
  • Permission denied (publickey) pendant la sync : refaire ssh-copy-id lidar@<ip-machine> et une connexion manuelle pour renseigner known_hosts ; vérifier que ~/.ssh est monté dans le conteneur (automatique avec run.sh, à décommenter en compose).
  • Boutons de génération masqués / 403 sur /api/generate : l'IP du client n'est pas dans LIDAR_REGEN_CIDR (défaut 192.168.1.0/24). Adapter la variable ou la vider pour lever la restriction.
  • « Machine de traitement injoignable » : vérifier le service worker (docker compose -f docker-compose.worker.yml logs -f worker) et LIDAR_GENERATION_URL (IP + port 8973). La consultation de la carte reste possible avec le cache local.
  • Carte vide après sync : attendre la fin du rebuild (GET /api/sync → "running": false) puis recharger la page ; le bouton ↻ de la carte enchaîne sync + vignettes automatiquement.
  • Port 8973 déjà pris : --serve-webapp 9000 avec run.sh, ou changer le mapping ports: en compose (le conteneur écoute toujours sur 8973).

Fonctionnement

  • Dessiner une zone (bouton « + Zone ») sur la carte du Pi envoie la demande à la machine de traitement, qui télécharge les dalles IGN puis les traite. La file de génération (progression tuile par tuile) est lue depuis la machine distante en direct.
  • À la fin du run, le navigateur appelle /api/sync : rsync ramène les images, le Pi régénère vignettes/sous-tuiles/index.html, puis recharge la carte. Le bouton ↻ relance le même cycle à tout moment. Avec LIDAR_AUTO_SYNC_SECONDS, le conteneur webapp seul maintient aussi son cache périodiquement (utile quand personne ne consulte la carte).
  • Sans machine de traitement configurée, ./run.sh --serve-webapp sert la carte en lecture seule depuis output/ (cache figé, aucun traitement possible depuis l'interface).
  • Autonomie : une fois les tuiles chargées dans le cache local, la webapp fonctionne seule — Leaflet est vendorisé dans assets/vendor/ (aucun CDN), tuiles/vignettes/infos sont lues dans output/, et un backend injoignable masque simplement les boutons de génération sans affecter la consultation.
  • Machine de traitement seule (sans Pi) : rien ne change, --serve / docker compose up -d --build serve fonctionne comme avant en local (tout-en-un), et docker-compose.worker.yml installe le générateur seul.
  • Si la machine de traitement est éteinte, la carte du Pi reste consultable (données synchronisées) ; seuls les nouveaux traitements sont indisponibles (message « machine de traitement injoignable »).

Références

  • lidar_pipeline/webapp.py : proxy distant (LIDAR_GENERATION_URL), /api/sync, cache local périodique (LIDAR_AUTO_SYNC_SECONDS), jeton LIDAR_API_TOKEN/LIDAR_REMOTE_TOKEN, restriction des générations au réseau local (LIDAR_REGEN_CIDR).
  • lidar_pipeline/index.py : vignettes/index sans GDAL (pyproj ou repli affine), recharge après sync.
  • docker-compose.worker.yml : générateur de tuiles (machine puissante).
  • Dockerfile.webapp, docker-compose.webapp.yml : webapp légère ARM64 (FastAPI + Pillow AVIF natif + pyproj, ~200 Mo).