# 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://: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://: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 && 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.