4.4 KiB
Watermaps auf Hetzner Cloud
Diese OpenTofu-Konfiguration erstellt einen einzelnen Ubuntu-24.04-Server mit fester IPv4-Adresse, vorgeschalteter Hetzner-Firewall und einem persistenten ext4-Volume für Routingdaten, Geofabrik-Rohdaten und die PostGIS-Datenbank mit Häfen, Schleusen, Brücken und Kontaktdaten. Kartenkacheln, DNS-Einträge und konkrete Anwendungsversionen sind bewusst nicht Teil dieses Infrastrukturmoduls. Die Anwendung wird anschließend als commitgenaues Container-Image aus der Gitea Registry deployt.
Konfiguration
Die lokale Datei terraform.tfvars wird aus der versionierten Vorlage angelegt
und durch die lokale .gitignore ausgeschlossen:
cp terraform.tfvars.example terraform.tfvars
Vor dem ersten Plan müssen dort mindestens folgende Werte ersetzt werden:
hcloud_token: Read/Write-API-Token des richtigen Hetzner-Cloud-Projektsssh_public_key: vollständiger Inhalt des öffentlichen SSH-Schlüsselsadmin_cidrs: öffentliche Administrator-IP mit/32beziehungsweise ein bewusst gewähltes Netz
terraform.tfvars.example bleibt als geheimnisfreie Vorlage versioniert.
Da terraform.tfvars den Token im Klartext enthält, sollte sie nur für den
lokalen Benutzer lesbar sein:
chmod 600 terraform.tfvars
Auch terraform.tfstate bleibt lokal und wird nicht nach Git oder auf den
Anwendungsserver übertragen. Nach jedem Import oder Apply muss die State-Datei
mit Dateimodus 0600 verschlüsselt gesichert werden, weil OpenTofu sie für
spätere Änderungen an denselben Ressourcen benötigt. Ist ein älterer State
verloren, werden vorhandene Ressourcen vor jedem Apply anhand ihrer
Hetzner-IDs importiert; ein paralleles Neuerzeugen gleichnamiger Ressourcen ist
nicht zulässig.
Infrastruktur erzeugen
cd infra/opentofu
tofu init
tofu fmt -check
tofu validate
umask 077
tofu plan -out=watermaps.tfplan
tofu apply watermaps.tfplan
Gespeicherte Plan-Dateien enthalten trotz als sensibel markierter Variablen
unter Umständen den Hetzner-Token. Sie müssen deshalb wie terraform.tfvars
und der State immer Modus 0600 haben und dürfen nicht weitergegeben werden.
Die feste IPv4-Adresse und den erforderlichen manuellen DNS-Eintrag zeigt OpenTofu anschließend an:
tofu output server_ipv4
tofu output dns_a_record
tofu output ssh_command
Der A-Record watermaps.incoso.eu kann direkt nach dem Apply auf
server_ipv4 gesetzt werden. Das eigentliche SSL-Livegehen erfolgt erst durch
das separate Deployment-Skript, nachdem DNS propagiert ist und die Routingdaten
bereitstehen.
Auf dem Server
Cloud-init installiert Docker einschließlich Compose v2, curl, jq und
rsync. Der Benutzer deploy erhält Zugriff per SSH und auf Docker. Es wird
kein Git-Repository auf den Server geklont. Das
persistente Volume wird nach dem Anhängen unter /srv/watermaps-data gemountet
und enthält die vom Produktions-Stack verwendeten Verzeichnisse geofabrik,
local und certbot. Ein systemd-Drop-in lässt Docker bei jedem Start auf den
erfolgreichen Volume-Mount warten, bevor bestehende Container neu gestartet
werden.
Der Status des ersten Starts lässt sich so prüfen:
ssh deploy@"$(tofu output -raw server_ipv4)" \
"cloud-init status --wait && systemctl status watermaps-volume-setup --no-pager"
Cloud-init bindet ausschließlich das von OpenTofu erzeugte Volume über den
stabilen Hetzner-Gerätepfad
/dev/disk/by-id/scsi-0HC_Volume_<volume-id> ein. Es wird nicht heuristisch
das erste Blockgerät mit dem Modellnamen Volume ausgewählt. Nach dem Mount
wird zusätzlich die UUID des tatsächlich eingehängten Geräts geprüft. Ein
Volume ohne lesbares Dateisystem wird standardmäßig nicht formatiert; nur bei
einem garantiert neuen, leeren Volume darf
initialize_empty_routing_volume = true für die einmalige Initialisierung
gesetzt werden. Bei Importen und Serverwechseln bleibt der Wert false.
Der Löschschutz ist getrennt konfiguriert:
enable_server_protectionfür den austauschbaren Host,enable_primary_ip_protectionfür die feste öffentliche Adresse,enable_volume_protectionfür Routing-, Zertifikats- und PostGIS-Daten.
Bei einem kontrollierten Servertausch bleiben IP- und Volume-Schutz immer
aktiv. Ein Plan, der hcloud_primary_ip.main oder
hcloud_volume.routing_data ersetzen beziehungsweise löschen will, darf nicht
angewendet werden. Zusätzlich verhindert OpenTofu für beide Ressourcen mit
prevent_destroy eine versehentliche Löschung durch den IaC-Plan.