rd13_tile_server/docs/ADMIN.md
Conrad Schulz 9df190aca8 feat: add Go marker-api service for MediaWiki Kartographer pin support
- services/marker-api/: Go HTTP service (0 external deps, ~8 MB RAM)
  - Mathematically correct teardrop pin shape (Google Maps style)
  - Tangent-based outline: circle + straight sides meeting at tip
  - White border + white inner dot, in-memory cache (max 500 entries)
- .forgejo/workflows/build-marker-api.yml: CI builds & pushes to
  Forgejo registry on push to main (image: rd13_tile_server-marker-api)
- docker-compose.yml: add marker-api service on port 9984
- Caddyfile (rd13_system_proxy): route /styles/v4/marker/* -> :9984
- docs/ADMIN.md + MAINTAINER.md: marker-api dokumentiert

Fixes: /styles/v4/marker/pin-m+{color}(@{scale}x).png was 404, now 200
2026-07-01 11:59:00 +00:00

449 lines
13 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.

# Administrator-Handbuch rd13_tile_server
> Zielgruppe: **Administratoren** Menschen, die das System betreiben, deployen und konfigurieren.
> Voraussetzung: Grundkenntnisse in Linux, Docker und der eingesetzten Stack (Martin, tileserver-gl).
---
## Systemvoraussetzungen
| Komponente | Mindestanforderung | Empfohlen |
|---|---|---|
| OS | Linux 5.10+ (oder macOS 12+) | Ubuntu 22.04 LTS |
| RAM | 1 GB | 2-4 GB |
| Disk | 5 GB (OS + Containers) | 20 GB (mit Cache-Warmup) |
| Dependencies | Docker 20.10+, docker-compose 2.0+ | Docker Desktop / Compose v2 |
| Network | Port 9982 (Martin), Port 8080 (tileserver-gl) internal | Public domain via NPM reverse proxy |
---
## Deployment
### Erstes Aufsetzen
```bash
# Repository klonen
git clone ssh://git@192.168.178.6:2222/cschulz/rd13_tile_server.git /mnt/services-data/rd13_tile_server
cd /mnt/services-data/rd13_tile_server
# Daten-Verzeichnis initialisieren (falls nicht vorhanden)
mkdir -p data/{fonts,sprites,styles}
# Services starten
docker compose up -d
# Health-Checks verifizieren
docker compose ps
curl -s http://localhost:9982/health | jq .
curl -s http://localhost:8080/health
```
### Updates einspielen (Tile-Daten)
**Szenario: osm.mbtiles aktualisieren (24×/Jahr)**
```bash
# 1. Neue MBTiles herunterladen (z.B. von OpenFreeMap)
cd /mnt/services-data/rd13_tile_server
python3 scripts/download-data.sh data/osm.mbtiles.new
# 2. Alte Datei backup-en
mv data/osm.mbtiles data/osm.mbtiles.$(date +%Y%m%d)
# 3. Neue Datei aktivieren
mv data/osm.mbtiles.new data/osm.mbtiles
# 4. Container neu starten (warm caches)
docker compose restart martin tileserver
# 5. Health-Check + Sampling testen
curl -s http://localhost:9982/osm | jq .
curl -s "http://localhost:9983/osm-intl/0/0/0.png" > /tmp/test.png && file /tmp/test.png
```
### Höhere Zoom-Level bauen (auf Host-Rechner, nicht auf dem Server)
Planetiler läuft in einem eigenen Container auf dem **Host-Rechner** (MacBook, Desktop-PC
etc.) mit genug RAM. Das Ergebnis wird per `rsync` auf den Server übertragen.
**Europa z16 (empfohlen):** ~280 GB Host-Disk, ~812h, 12 CPUs / 28 GB RAM
```bash
# Auf dem HOST-RECHNER ausführen (im geklonten Repo-Verzeichnis):
# Europa z0-16 (~80 GB Output, ~8-12h, ~280 GB Disk nötig)
AREA=europe MAX_ZOOM=16 RAM=28g BUILD_CPUS=12 BUILD_MEM=30g \
docker compose -f docker-compose.build.yml up
# Germany z0-16 (kleiner Test: ~12 GB Output, ~2h, ~50 GB Disk)
AREA=germany MAX_ZOOM=16 RAM=8g \
docker compose -f docker-compose.build.yml up
# Fertiges MBTiles auf Server übertragen (resume-fähig):
rsync -avh --progress --partial \
./build/output/europe.mbtiles \
rd13server@192.168.178.6:/mnt/services-data/rd13_tile_server/data/osm-europe.mbtiles
# Martin auf dem Server neu starten:
ssh rd13server@192.168.178.6 \
"cd /mnt/services-data/rd13_tile_server && docker compose restart martin"
# Neue Source prüfen:
curl https://tiles.rd13server.de/catalog | python3 -m json.tool | grep osm-europe
```
Build-Output liegt lokal unter `./build/output/<AREA>.mbtiles` (gitignored).
Sources-Cache unter `./build/sources/` bei Rebuild nicht löschen (spart Re-Download).
### Planetiler Build-Steuerung (Cleanup/Resume)
Der Build trennt jetzt bewusst in kurzlebige und wiederverwendbare Daten:
- `build/output/` fertige MBTiles
- `build/sources/` Download-Cache (OSM + Zusatzdaten)
- `build/tmp/work/` laufzeitbezogene Temp-Daten (wegwerfbar)
- `build/tmp/reuse/` wiederverwendbare Feature-DB fuer `--reuse_featuredb`
Steuerung ueber Script:
```bash
# Status + Reuse-Readiness anzeigen
./scripts/planetiler-build-control.sh status
# Resume-Build: work leeren, reuse/sources behalten, dann starten
./scripts/planetiler-build-control.sh resume
# Fresh-Build: output+work+reuse leeren, sources behalten, dann starten
./scripts/planetiler-build-control.sh fresh
# Vollstaendig neu: output+sources+work+reuse leeren, dann starten
./scripts/planetiler-build-control.sh fresh-all
```
Hinweis: `reuse` ist nur aktiv, wenn diese Artefakte existieren:
- `build/tmp/reuse/feature.db/`
- `build/tmp/reuse/feature.db/feature.db.strings`
- `build/tmp/reuse/feature.db/feature.db.manifest`
### Satellit-Tiles herunterladen (einmalig, ~38 GB, ~24h)
Der Download läuft in einem eigenen Container vollständig unabhängig von
SSH-Sessions und VS Code. Resume-fähig: bei Unterbrechung einfach neu starten.
**Ressourcen:** 16 Threads, 1 GB RAM-Limit (~40 MB effektiv), 4 CPUs. `restart: on-failure`.
**Robustheit:** Schreibt in `satellite.mbtiles.part`, Umbenennung nach Abschluss. Resume-fähig.
EOX liefert trotz `.jpg`-URL einzelne PNG-Tiles. Der Download-Container konvertiert
PNG-Antworten direkt zu JPEG, damit Martin die fertige MBTiles-Datei als einheitliche
Rasterquelle laden kann. Wichtige Tuning-Variablen: `SAT_CHUNK` und `SAT_DB_BATCH`
begrenzen RAM/SQLite-Transaktionsgröße, `SAT_JPEG_QUALITY` steuert die JPEG-Qualität.
Falls eine bereits fertige Satellit-MBTiles-Datei gemischte JPEG/PNG-Tiles enthält,
erst eine vollständige Sicherung erstellen und dann nur die PNG-Tiles konvertieren.
Das Script ist resume-fähig: bereits konvertierte JPEG-Tiles werden bei erneutem Start
übersprungen, maximal der letzte nicht committete Batch wird wiederholt.
```bash
cd /mnt/services-data/rd13_tile_server
docker compose stop martin
cp data/satellite.mbtiles data/satellite.mbtiles.backup-$(date +%Y%m%d-%H%M%S)
docker run --rm \
-v ./data:/data \
-v ./scripts/convert-satellite-to-jpeg.py:/convert.py:ro \
-e SAT_CONVERT_BATCH=500 \
-e SAT_CONVERT_SLEEP=0.02 \
-e SAT_JPEG_QUALITY=90 \
python:3.12-slim \
sh -c "pip install --no-cache-dir Pillow && python3 /convert.py /data/satellite.mbtiles"
# Nach Konvertierung verifizieren
docker run --rm -v ./data:/data python:3.12-slim python3 -c \
"import sqlite3; conn=sqlite3.connect('/data/satellite.mbtiles'); \
fmt=conn.execute('SELECT value FROM metadata WHERE name=\"format\"').fetchone()[0]; \
total=conn.execute('SELECT COUNT(*) FROM tiles').fetchone()[0]; \
quick=conn.execute('PRAGMA quick_check').fetchone()[0]; \
conn.close(); print(f'✓ format={fmt} tiles={total} quick_check={quick}')"
# Martin neu starten
docker compose up -d martin
curl -s http://localhost:9982/health
```
**Download-Szenario (normal, neu empfohlen):**
```bash
cd /mnt/services-data/rd13_tile_server
# Starten (läuft im Hintergrund, résumé-fähig, konvertiert PNG→JPEG on-the-fly)
docker compose -f docker-compose.download.yml up -d
# Status verfolgen
docker compose -f docker-compose.download.yml logs -f
# Pausieren
docker compose -f docker-compose.download.yml stop
# Fortsetzen (bereits heruntergeladene und konvertierte Tiles werden übersprungen)
docker compose -f docker-compose.download.yml up -d
# Nach Fertigstellung (check für ~5.6M Tiles, nur JPEG)
docker compose -f docker-compose.download.yml logs | tail -5
docker compose ps
curl -s http://localhost:9982/catalog | jq '.[] | select(.id == "satellite")'
```
Wenn fertig: `data/satellite.mbtiles` (~20 GB nach PNG→JPEG-Konvertierung) ist bereit.
Martin lädt sie automatisch beim nächsten Restart als Source `satellite`.
### Service Restart / Update Images
```bash
# Nur Image-Updates (Martin, tileserver-gl)
docker compose pull
docker compose up -d
# Bei Compose-Konfiguration-Änderungen
docker compose down
docker compose up -d
# Logs ansehen
docker compose logs -f martin # Martin Vektor-API
docker compose logs -f tileserver # Raster-Rendering
```
---
## Konfiguration
### Tile-Server-Komponenten
**Martin (Vektor-Tiles)**
- Konfiguration: `config/martin.yaml`
- API Root: `http://localhost:9982`
- Catalog: `http://localhost:9982/catalog`
- Datenquelle: `data/osm.mbtiles`
**tileserver-gl (Raster-PNG-Rendering)**
- Konfiguration: Styles unter `data/styles/`
- API Root: `http://localhost:9983` (exposed port)
- Style `osm-intl`: `data/styles/osm-intl.json`
- Fonts: `data/fonts/`
- Sprites: `data/sprites/`
**marker-api (Marker-PNG-Generierung)**
- Code: `services/marker-api/` (Go, 0 externe Dependencies)
- Image: `192.168.178.6:8083/cschulz/rd13_tile_server-marker-api:latest` (Forgejo Registry)
- API Root: `http://localhost:9984`
- Endpunkt: `/styles/v4/marker/{type}+{hexcolor}(@{scale}x).png`
- Typen: `pin-s` (24px), `pin-m` (32px), `pin-l` (40px)
- Image neu bauen: `docker compose build marker-api && docker compose up -d marker-api`
- CI: Forgejo baut + pusht automatisch bei Änderungen in `services/marker-api/`
### Reverse Proxy (NPM) Öffentliche URLs
**Konfiguration (manuell im NPM WebUI):**
1. **Neuer Proxy-Host:** `tiles.rd13server.de`
- Forward Hostname/IP: `martin` (Docker-DNS)
- Forward Port: `3000`
- Schema: `http`
- Websockets Support: ☑
2. **Advanced Tab Custom Nginx Config:**
```nginx
# Rate Limiting
limit_req_zone $binary_remote_addr zone=tiles:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=tile_conn:10m;
location / {
limit_req zone=tiles burst=60 nodelay;
limit_conn tile_conn 20;
limit_req_status 429;
limit_conn_status 429;
# Cache-Header für Tiles
add_header Cache-Control "public, max-age=86400, stale-while-revalidate=3600" always;
add_header Vary "Accept-Encoding" always;
}
# Raster-Routing zu tileserver-gl
location ~ ^/osm-intl/ {
proxy_pass http://localhost:9983;
proxy_ssl_server_name on;
add_header Cache-Control "public, max-age=86400" always;
}
```
---
## Betrieb & Monitoring
### Health-Checks manuell
```bash
# Martin Vektor-Tiles
curl -s http://localhost:9982/health
# tileserver-gl Raster
curl -s http://localhost:9983/health
# Sample-Tile abrufen (PNG)
curl -s "http://localhost:9983/osm-intl/0/0/0.png" -o /tmp/tile.png
file /tmp/tile.png
```
### Performance-Baseline
**Erwartete Antwortzeiten (ohne externen Upstream):**
- Martin `/osm/{z}/{x}/{y}.pbf`: <50ms (cache warm)
- tileserver-gl `/osm-intl/{z}/{x}/{y}.png`: <100ms (cache cold), <20ms (cache warm)
**Caches (auto-warm nach Start):**
- Martin: in-memory (fast)
- tileserver-gl: Disk unter `/tmp/pmtiles-cache` (wächst mit Nutzung)
### Logs
```bash
# Alle Services
docker compose logs
# Nur Fehler
docker compose logs --tail=100 | grep -i error
# Real-time verfolgen
docker compose logs -f
```
### Nextcloud + Home Assistant Integration (Quick Rollout)
1. Endpunkte lokal pruefen:
```bash
curl -I "http://localhost:9983/styles/osm-intl/14/8802/5373.png"
curl -I "http://localhost:9983/styles/europa-detail/16/35210/21493.png"
curl -I "http://localhost:9983/styles/satelite-world/13/4401/2686.png"
```
2. Nextcloud Maps setzen:
```bash
# Im Nextcloud-Container ausfuehren
php occ config:app:set maps tileserverUrl \
--value="https://tiles.rd13server.de/styles/europa-detail/{z}/{x}/{y}.png"
```
3. Home Assistant Lovelace Tile-Layer auf einen der verifizierten Endpunkte setzen:
- `https://tiles.rd13server.de/styles/osm-intl/{z}/{x}/{y}.png`
- `https://tiles.rd13server.de/styles/europa-detail/{z}/{x}/{y}.png`
- `https://tiles.rd13server.de/styles/satelite-world/{z}/{x}/{y}.png`
4. Externen Test aus Browser oder HA/Nextcloud Host:
```bash
curl -I "https://tiles.rd13server.de/styles/europa-detail/16/35210/21493.png"
```
---
## Troubleshooting
| Problem | Ursache | Lösung |
|---|---|---|
| `tiles.rd13server.de` liefert 502 | Martin-Container nicht erreichbar | `docker compose ps` + `docker compose logs martin` |
| PNG-Tiles sind grau/leer | tileserver-gl kann Martin nicht erreichen | Docker-Network prüfen: `docker network inspect rd13_tile_server_default` |
| Lange Antwortzeiten | Tile-Cache kalt, erste Render | Normal; nach 1020 Requests warm; monitoring via logs |
| Out of Memory | Cache wächst unbegrenzt | tileserver-gl Speicher-Limit setzen oder Cache-TTL anpassen |
---
## Wartungsplan
| Task | Frequenz | Aufwand |
|---|---|---|
| Tile-Daten aktualisieren | 24×/Jahr | 15 min (Script + Restart) |
| Container-Images patchen | Wöchentlich | 5 min (`docker compose pull && up -d`) |
| Logs archivieren | Monatlich | 5 min (via logrotate) |
| Disk-Space überwachen | Wöchentlich | 2 min (du/df) |
---
## Rollback-Procedure
**Falls neue osm.mbtiles kaputt ist:**
```bash
cd /mnt/services-data/rd13_tile_server
docker compose stop martin tileserver
# Alte Version zurückbringen
rm data/osm.mbtiles
mv data/osm.mbtiles.YYYYMMDD data/osm.mbtiles
docker compose up -d
```
<!-- TODO: Alle relevanten Env-Vars dokumentieren -->
| Variable | Pflicht | Default | Beschreibung |
|---|---|---|---|
| `EXAMPLE_VAR` | ✓ | | Beschreibung |
### Persistente Daten (`/data/`)
Alle persistenten Daten liegen unter `/data/<service>/` im Repo-Root:
<!-- TODO: Alle Service-Unterordner und was drin liegt -->
| Pfad | Inhalt | Backup-Priorität |
|---|---|---|
| `data/<service>/` | … | hoch/mittel/niedrig |
**Backup-Hinweise:**
- `/data/` komplett sichern vor jedem Update
- Empfehlung: tägliches Backup via Cron / Restic / rsync
---
## Monitoring & Logs
<!-- TODO: Wie überwacht man das System? Wo sind die Logs? -->
```bash
# Logs ansehen
docker compose logs -f <service>
# Status prüfen
docker compose ps
```
---
## Sicherheit
<!-- TODO: Firewall-Regeln, Portfreigaben, TLS-Setup etc. -->
- Offene Ports: …
- TLS: …
- Zugriffskontrolle: …
---
## Troubleshooting
| Symptom | Ursache | Lösung |
|---|---|---|
| … | … | … |
---
## Disaster Recovery
<!-- TODO: Was tun wenn es brennt? -->
1. Dienst stoppen: `docker compose down`
2. Backup einspielen: …
3. Dienst neu starten: `docker compose up -d`