Watermaps-Produktion
Dieser Stack hostet die Watermaps-App und die lokalen Fahrrouten für Deutschland und die Niederlande. Die sichtbaren Kartenkacheln bleiben externe Dienste. Öffentlich gebunden werden ausschließlich TCP 80 und 443; die Watermaps-App ist nur im internen Docker-Netz erreichbar.
Konfiguration
cp deploy/.env.production.example deploy/.env.production
editor deploy/.env.production
Mindestens WATERMAPS_ACME_EMAIL muss angepasst werden. Außerdem müssen
WATERMAPS_REGISTRY und WATERMAPS_REGISTRY_OWNER auf die Gitea Container
Registry zeigen. Der Hetzner-API-Token gehört nicht in diese Datei. Er
bleibt lokal in der ignorierten Datei infra/opentofu/terraform.tfvars
(alternativ kann der Provider TF_VAR_hcloud_token lesen).
Die private SSH-Keydatei wird ebenfalls nicht gespeichert. Sie kann beim
Deployment mit --identity oder über WATERMAPS_SSH_KEY angegeben werden.
Falls OpenTofu keinen Output ssh_private_key_path bereitstellt und der Key
nicht bereits über den SSH-Agenten verfügbar ist, ist eine dieser beiden
Angaben erforderlich. Der vorbereitete SSH-Benutzer heißt standardmäßig
deploy; die privilegierten Installationsschritte laufen über dessen
passwortloses sudo.
Gitea Actions und Container Registry
Der Workflow .gitea/workflows/container-images.yml führt Typechecks und Tests
aus. Nach einem erfolgreichen Push auf main baut und veröffentlicht er zwei
OCI-Images unter der vollständigen Git-Commit-SHA:
gitea.incoso.eu/kevin_janssen/watermaps:<commit-sha>
gitea.incoso.eu/kevin_janssen/watermaps-route-data:<commit-sha>
In Gitea müssen Repository Actions aktiviert und ein Docker-fähiger
ubuntu-latest-Runner registriert sein. Unter
Repository → Settings → Actions → Secrets werden benötigt:
REGISTRY_USERNAME: Gitea-Benutzer, dem die Packages gehörenREGISTRY_TOKEN: Personal Access Token mitpackage: Read and Write
Der separate PAT ist derzeit nötig, weil Giteas eingebauter Job-Token OCI-Pakete noch nicht zuverlässig veröffentlichen kann. Für Pull Requests werden nur Tests ausgeführt; Registry-Secrets werden dabei nicht verwendet.
Deployment
Nach einem erfolgreichen Image-Build liest das Skript standardmäßig den
OpenTofu-Output server_ipv4. Es erlaubt ausschließlich einen sauberen,
vollständig committeten Git-Stand und überträgt nur den kleinen Ordner
deploy/, nicht den Anwendungsquellcode. Die Image-Tags entsprechen exakt
git rev-parse HEAD.
Ist die Registry privat, werden einmalig beziehungsweise nach Tokenwechsel lokale Pull-Zugangsdaten mitgegeben:
WATERMAPS_REGISTRY_USERNAME=kevin_janssen \
WATERMAPS_REGISTRY_TOKEN='<package-read-token>' \
./deploy/scripts/upload-and-deploy.sh \
--identity ~/.ssh/watermaps_hetzner_ed25519
Der Token benötigt auf dem Produktionsserver nur package: Read. Er wird per
SSH an docker login --password-stdin übergeben, nicht in
.env.production, einem Container oder dem Image gespeichert. Docker legt die
Anmeldung root-lesbar in seiner lokalen Client-Konfiguration ab. Bei späteren
Deployments muss der Token nicht erneut angegeben werden, solange die
Anmeldung gültig ist:
./deploy/scripts/upload-and-deploy.sh \
--identity ~/.ssh/watermaps_hetzner_ed25519
Das Serverskript lädt App, Routingdaten-Builder, Nginx und Certbot mit
docker compose pull. Die beiden commitgenauen Gitea-Tags werden anschließend
in ihre unveränderlichen Registry-Digests aufgelöst. Erst danach werden die
Container mit --no-build gestartet. Der Produktionsserver benötigt deshalb
weder Git noch Node/npm oder den Quellcode.
Der erste Datenaufbau lädt die Geofabrik-Extrakte für Deutschland und die Niederlande herunter und kann entsprechend der Serverleistung längere Zeit dauern. Der produktive Index liegt auf dem Server unter:
/srv/watermaps-data/local/germany-netherlands-fairways.json
Der Upload wartet zuerst auf SSH und den Abschluss von Cloud-init. Anschließend
startet und prüft er watermaps-volume-setup.service. Ohne tatsächlich unter
/srv/watermaps-data eingehängtes Volume wird kein Download gestartet, damit
die großen PBF-Dateien nicht versehentlich auf dem Root-Dateisystem landen.
Für den kurzzeitigen Speicherpeak beim kombinierten Indexaufbau richtet der
Bootstrap zusätzlich die über WATERMAPS_SWAP_SIZE_GB konfigurierte,
persistente Swap-Reserve ein.
Vor dem Livegang antwortet Nginx nur für ACME-Challenges. Alle anderen HTTP-Anfragen erhalten 404.
Rollback
Jedes erfolgreiche Release wird mit Commit-SHA und den aufgelösten
Image-Digests unter /srv/watermaps-runtime/deployments/ gespeichert. Scheitert
ein Deployment nach dem Containerwechsel, startet deploy.sh automatisch das
vorherige Release und führt die Health- und Routentests erneut aus.
Das unmittelbar vorherige Release lässt sich auch manuell aktivieren:
./deploy/scripts/remote-rollback.sh \
--identity ~/.ssh/watermaps_hetzner_ed25519
Oder ein bestimmter, bereits erfolgreich deployter Commit:
./deploy/scripts/remote-rollback.sh \
--revision 0123456789abcdef0123456789abcdef01234567 \
--identity ~/.ssh/watermaps_hetzner_ed25519
Der Rollback verwendet gespeicherte Digests, nicht einen beweglichen Tag.
DNS und SSL-Livegang
Sobald der OpenTofu-Output bekannt ist, kann folgender DNS-Eintrag gesetzt werden:
A watermaps.incoso.eu <server_ipv4>
Nach der DNS-Propagation wird der Livegang lokal ausgelöst:
./deploy/scripts/remote-go-live.sh --identity ~/.ssh/watermaps_hetzner_ed25519
Das Serverskript prüft, dass sämtliche A-Records ausschließlich auf die erwartete IPv4 zeigen, testet den Routingindex mit je einer Route in Deutschland und den Niederlanden, prüft den ACME-Webroot, fordert das Zertifikat an und aktiviert erst anschließend HTTPS. HTTP leitet danach auf HTTPS um.
Automatik
bootstrap-server.sh installiert zwei systemd-Timer:
watermaps-route-update.timer: täglich neue Deutschland- und Niederlande-Daten; bei Build- oder Routentestfehler bleibt der vorherige Index aktiv.watermaps-certbot-renew.timer: zweimal täglich Certbot-Prüfung mit anschließendem Nginx-Reload.
Status und Logs:
systemctl list-timers 'watermaps-*'
journalctl -u watermaps-route-update.service
journalctl -u watermaps-certbot-renew.service
docker compose \
--project-directory /opt/watermaps \
--env-file /opt/watermaps/deploy/.env.production \
--env-file /opt/watermaps/deploy/.env.images \
-f /opt/watermaps/deploy/compose.production.yml ps