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

191 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
```bash
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/` :
```bash
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` :
```yaml
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](https://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)
```bash
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é) :
```yaml
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
```
```bash
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
```bash
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 :
```bash
#!/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) :
```bash
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.