Ansible-Setup zum lokalen Provisionieren und Konfigurieren einer kleinen Uranus-Umgebung auf einem KVM/libvirt-Host.
Das Repository baut mehrere Ubuntu-VMs, verteilt Rollen je Host und konfiguriert darauf:
- PostgreSQL/PostGIS auf
db01 - Uranus Backend und Frontend auf
dev01undprod01 - Monitoring auf
mon01 - Nominatim mit Schleswig-Holstein-Extrakt auf
nom01 - VersaTiles mit Schleswig-Holstein-Extrakt auf
vers01
Die Umgebung ist auf einen lokalen Einzelhost mit libvirt-NAT ausgelegt. Aktuell sind die VM-RAM-Werte auf einen Host mit 16 GB RAM abgestimmt.
Die Umgebung besteht standardmäßig aus sechs VMs:
| VM | Rolle | RAM | Disk | CPU |
|---|---|---|---|---|
db01 |
PostgreSQL/PostGIS | 2048 MB | 80 GB | 4 |
dev01 |
Dev-Frontend + Dev-Backend | 2048 MB | 80 GB | 4 |
prod01 |
Prod-Frontend + Prod-Backend | 4096 MB | 120 GB | 4 |
mon01 |
Monitoring | 2048 MB | 40 GB | 2 |
nom01 |
Nominatim | 4096 MB | 200 GB | 6 |
vers01 |
VersaTiles | 4096 MB | 80 GB | 4 |
VM-Definitionen stehen in vars.yml.
Verwendet werden lokale Domains unter home.arpa:
| Host | Domain(s) |
|---|---|
dev01 |
dev.uranus.home.arpa, api.dev.uranus.home.arpa |
prod01 |
uranus.home.arpa, api.uranus.home.arpa, mail.uranus.home.arpa |
mon01 |
monitoring.uranus.home.arpa |
nom01 |
nominatim.uranus.home.arpa |
vers01 |
tiles.uranus.home.arpa |
Die Basiswerte stehen in group_vars/all.yml, die host-spezifischen Zuweisungen in host_vars/.
Damit diese Domains vom Laptop oder einem anderen Gerät im LAN erreichbar sind, zeigt die neue KVM-Ingress-Konfiguration alle Domains auf den KVM-Host. Auf dem Laptop brauchst du passende Einträge in /etc/hosts oder lokales DNS, die auf 192.168.1.118 zeigen:
192.168.1.118 dev.uranus.home.arpa api.dev.uranus.home.arpa
192.168.1.118 uranus.home.arpa api.uranus.home.arpa
192.168.1.118 monitoring.uranus.home.arpa
192.168.1.118 nominatim.uranus.home.arpa
192.168.1.118 tiles.uranus.home.arpaDer KVM-Host terminiert dabei TLS mit lokalen Test-Zertifikaten und leitet die Requests intern an die jeweils aktuellen VM-IP-Adressen aus inventory/runtime_hosts.yml weiter.
Das Provisioning läuft lokal auf dem KVM-Host über uvtool und libvirt:
playbooks/provision.ymlinstalliert KVM/libvirt, erzeugt das libvirt-NAT-Netz und erstellt die VMs- die Provisionierung markiert die libvirt-Domains außerdem als
autostart, damit sie nach einem Host-Neustart wieder hochkommen - anschließend werden die per DHCP vergebenen IPs in
inventory/runtime_hosts.ymlgeschrieben
Wichtige Templates:
templates/libvirt-bridge.xml.j2templates/user-data.config.j2templates/runtime-inventory.yml.j2
Die Konfiguration läuft in playbooks/configure.yml:
- Host-spezifische Variablen werden aus
host_vars/<hostname>.ymlgeladen - je nach
vm_rolewerden Rollen eingebunden
Reihenfolge grob:
postgresqlaufdb01commonufwlogrotatebackupaufdb01,dev01undprod01certbotnginxapp_backendapp_frontendpostfixmonitoringnominatimversatiles
Auf mon01 läuft jetzt die vollständige Kette Icinga2 -> InfluxDB -> Grafana.
Icinga2übernimmt Status-Checks und schreibt deren Perfdata über denInfluxdbWriternach InfluxDBInfluxDBspeichert sowohl Icinga-Perfdata als auch die separaten Pluto-Metriken aus den Backend-VMsGrafanaliest aus InfluxDB und wird unterhttps://monitoring.uranus.home.arpa/grafana/per Nginx ausgeliefert
Provisionierte Dashboards:
- pro gespeicherter Icinga-Metrik ein eigenes Grafana-Dashboard
Stand der aktuellen
show series-Liste: 142 einzelne Icinga-Metrik-Dashboards Uranus Pluto Imagesfür die Measurementpluto_image_files
Zusätzliche Pluto-Metriken:
dev01undprod01zählen stündlich die Dateien unter/opt/uranus/backend/pluto/images- die Werte werden als Measurement
pluto_image_filesnach InfluxDB aufmon01geschrieben - Beispielabfrage auf
mon01:influx -database uranus_metrics -execute 'SELECT last(file_count) FROM pluto_image_files GROUP BY host'
Die frühere Test-Rolle ist absichtlich nicht mehr im Hauptablauf eingebunden.
.
├── ansible.cfg
├── group_vars/
├── host_vars/
├── inventory/
├── playbooks/
├── roles/
├── templates/
├── uranus.sql
└── vars.yml
Wichtige Dateien:
ansible.cfg: zentrale Ansible-Konfigurationvars.yml: VM-Layout, Provisioning-Defaults, lokaler Testmodusgroup_vars/all.yml: globale Domänen, Versionsstände und nicht-sensitive Default-Variablengroup_vars/vault.yml: lokale Secrets und Passwörter, nicht im Repo versionierengroup_vars/vault.yml.example: Beispielstruktur für Secretshost_vars/db01.yml: Datenbank-Setuphost_vars/dev01.yml: Dev-App-Setuphost_vars/prod01.yml: Prod-App-Setuphost_vars/vers01.yml: VersaTiles-Setupuranus.sql: importierter Datenbank-Dump
Auf dem Host:
- Linux-System mit KVM/libvirt
- Virtualisierung im BIOS/UEFI aktiviert
- Ansible installiert
- SSH-Key unter
~/.ssh/id_rsaund~/.ssh/id_rsa.pub sudofür lokale Installation und Remote-Konfiguration
Das Repository geht davon aus, dass du lokal als libvirt/KVM-Admin arbeitest und die VMs über das Standard-libvirt-Netz 192.168.122.0/24 laufen.
Sensitive Werte sollen nicht mehr in group_vars/all.yml, host_vars/ oder vars.yml im Klartext gepflegt werden.
Stattdessen:
group_vars/vault.yml.examplenachgroup_vars/vault.ymlkopieren- echte Werte in
group_vars/vault.ymlsetzen - optional
group_vars/vault.ymlmitansible-vault encryptverschlüsseln
group_vars/vault.yml ist in .gitignore eingetragen und wird von playbooks/provision.yml sowie playbooks/configure.yml optional geladen.
Beispiel:
cp group_vars/vault.yml.example group_vars/vault.yml
ansible-vault encrypt group_vars/vault.ymlTypischer Ablauf:
- Datei aus der Vorlage erzeugen
- Secrets eintragen
- Datei mit
ansible-vaultverschlüsseln - Playbooks mit Vault-Passwort ausführen
Befehle:
cp group_vars/vault.yml.example group_vars/vault.yml
ansible-vault edit group_vars/vault.yml
ansible-vault encrypt group_vars/vault.ymlWenn die Datei bereits verschlüsselt ist:
ansible-vault edit group_vars/vault.yml
ansible-vault view group_vars/vault.yml
ansible-vault decrypt group_vars/vault.ymlPlaybooks mit Vault-Passwort starten:
ansible-playbook -i inventory.ini playbooks/db01.yml --ask-vault-pass
ansible-playbook -i inventory.ini playbooks/site.yml --ask-vault-passBackups werden aktuell lokal auf den jeweiligen VMs unter backup_root geschrieben. Standard ist:
backup_root: /var/backups/uranusAuf db01 legt die Rolle tägliche PostgreSQL-Dumps unter db/ ab:
/var/backups/uranus/db/uranus_dev_<timestamp>.sql/var/backups/uranus/db/uranus_prod_<timestamp>.sql
Auf dev01 und prod01 legt die Rolle tägliche Archiv-Backups unter app/ ab:
/var/backups/uranus/app/dev01_<timestamp>.tar.gz/var/backups/uranus/app/prod01_<timestamp>.tar.gz
Gesichert werden dabei standardmäßig:
/etc/nginx/etc/uranus/opt/uranus/frontend/dist/opt/uranus/backend/bin/opt/uranus/backend/profile_images/opt/uranus/backend/pluto/images/opt/uranus/backend/pluto/cache
Die Aufbewahrung steuert backup_retention_days in group_vars/all.yml. Standard ist 14 Tage.
Die Backups laufen per Systemd-Timer:
uranus-db-backup.timeraufdb01uranus-app-backup.timeraufdev01undprod01
Manuell auslösen:
sudo systemctl start uranus-db-backup.service
sudo systemctl start uranus-app-backup.serviceWenn du die Sicherungen nicht nur lokal auf den VMs, sondern zentral auf db01 oder off-host ablegen willst, musst du zusätzlich einen zweiten Kopierschritt mit rsync, restic oder einem NAS/SFTP-Ziel ergänzen.
dev01 und prod01 nutzen Postfix nur als ausgehenden SMTP-Relay. Ein vollständiger Mailserver mit Mailboxen, IMAP/POP3 oder eingehender MX-Zustellung ist nicht eingerichtet.
Die Postfix-Rolle zieht ihre Relay-Daten standardmäßig aus diesen Variablen:
backend_smtp_hostbackend_smtp_portbackend_smtp_loginbackend_smtp_password
Darauf aufbauend werden automatisch gesetzt:
postfix_relay_hostpostfix_relay_portpostfix_relay_loginpostfix_relay_password
Wenn du für Postfix andere Zugangsdaten als für das Backend verwenden willst, kannst du die postfix_relay_*-Werte separat überschreiben.
Die sensiblen SMTP-Passwörter gehören in group_vars/vault.yml.
Wenn der Relay konfiguriert ist, richtet die Rolle zusätzlich rsyslog ein und schreibt Postfix-Logs nach:
/var/log/mail.log
Nützliche Befehle auf dev01 oder prod01:
sudo tail -f /var/log/mail.log
sudo journalctl -u postfix -f
postqueue -pDie UFW-Basis ist auf allen VMs aktiv mit deny incoming und allow outgoing.
Zusätzlich gilt aktuell:
- SSH ist auf die Netze in
ssh_allowed_networksbegrenzt - HTTP und HTTPS sind nur auf Web-VMs mit Rollen aus
web_exposed_vm_rolesoffen db01öffnet PostgreSQL nur fürpostgresql_allowed_networks- der Backend-Port
9090ist nur lokal auf127.0.0.1freigegeben - eingehendes NTP ist nicht freigeschaltet
Die wichtigsten Härtungs-Variablen stehen in group_vars/all.yml:
ssh_allowed_networksweb_exposed_vm_rolesufw_logging
Auf db01 ist PostgreSQL zusätzlich enger gebunden und lauscht nicht mehr auf allen Interfaces, sondern nur auf localhost und der internen VM-IP.
Alternativ mit Passwortdatei:
ansible-playbook -i inventory.ini playbooks/site.yml --vault-password-file .vault_pass.txtWichtig:
group_vars/vault.ymlwird optional geladen, muss für echte Deployments aber vorhanden seingroup_vars/vault.ymlsollte nicht unverschlüsselt im Dateisystem liegen bleiben- die Beispiel-Datei
group_vars/vault.yml.exampledient nur als Strukturvorlage
Besonders relevant:
local_test_modecreate_vmsrecreate_existing_vmsssh_public_key_filessh_private_key_fileuvt_releasevms
local_test_mode: true bedeutet aktuell:
- VMs werden lokal mit
uvt-kvmerstellt - die Nginx-VHosts für Frontend/Backend leiten HTTP auf HTTPS um
Besonders relevant:
uranus_domainsfrontend_repo_url,frontend_repo_versionbackend_repo_url,backend_repo_versionbackend_go_versionbackend_db_schemanominatim_*
Secrets in dieser Datei sind jetzt nur noch Referenzen auf vault_*-Variablen.
Hier wird die Mehrfach-DB-Konfiguration definiert:
app_databases:
- name: uranus_dev
user: uranus_dev
password: "{{ vault_db_password_dev }}"
- name: uranus_prod
user: uranus_prod
password: "{{ vault_db_password_prod }}"Zusätzlich:
db_schema: "uranus"postgresql_listen_addresses: "*"postgresql_allowed_networks
Dev und Prod verwenden jeweils eigene Datenbanken:
dev01->uranus_dev/uranus_devprod01->uranus_prod/uranus_prod
Die zugehörigen Passwörter kommen aus group_vars/vault.yml.
Aktuell werden auf db01 getrennte Datenbanken für Dev und Prod angelegt:
uranus_devuranus_prod
Die PostgreSQL-Rolle:
- legt die Benutzer an
- legt die Datenbanken an
- aktiviert PostGIS
- importiert den SQL-Dump in jede Datenbank
- setzt Grants auf Schema, Tabellen und Sequenzen
- schreibt
pg_hba.conffür die internen Netze
Wichtig: Für Nominatim gibt es zusätzlich eine separate Datenbank:
nominatim
Diese wird nicht durch die generische PostgreSQL-Rolle importiert, sondern von der Nominatim-Rolle vorbereitet und während des Nominatim-Imports verwendet.
nom01 ist auf den Schleswig-Holstein-Extrakt von Geofabrik ausgelegt:
- Quelle:
https://download.geofabrik.de/europe/germany/schleswig-holstein-latest.osm.pbf - Importstil:
address - API-Host:
nominatim.uranus.home.arpa
Wichtiges Verhalten:
- Nominatim läuft auf
nom01 - die Datenbank dafür liegt auf
db01 - PostgreSQL-Port
5432ist nur im internen VM-Netz freigeschaltet, nicht öffentlich
Die Backup-Rolle auf db01 erzeugt ein tägliches Dump-Backup aller in app_databases gelisteten Datenbanken nach:
/var/backups/uranus
Gesichert werden aktuell:
uranus_devuranus_prod
ansible-playbook -i inventory.ini playbooks/site.ymlDas läuft nur sauber, wenn Provisioning und nachgelagerte Rollen keine Zwischenfehler erzeugen.
ansible-playbook -i inventory.ini playbooks/db01.yml
ansible-playbook -i inventory.ini playbooks/dev01.yml
ansible-playbook -i inventory.ini playbooks/prod01.yml
ansible-playbook -i inventory.ini playbooks/mon01.yml
ansible-playbook -i inventory.ini playbooks/nom01.ymlSobald inventory/runtime_hosts.yml geschrieben ist, ist dieses Inventory für Konfigurationsläufe meist praxisnäher:
ansible-playbook -i inventory/runtime_hosts.yml playbooks/db01.yml
ansible-playbook -i inventory/runtime_hosts.yml playbooks/dev01.ymlgroup_vars/vault.yml.examplenachgroup_vars/vault.ymlkopieren und echte Secrets eintragen.- Optional
/etc/hostsfür die*.home.arpa-Domains ergänzen. db01provisionieren und konfigurieren.dev01undprod01provisionieren und konfigurieren.mon01provisionieren und konfigurieren.nom01provisionieren und konfigurieren.
Pragmatisch:
ansible-playbook -i inventory.ini playbooks/db01.yml
ansible-playbook -i inventory/runtime_hosts.yml playbooks/dev01.yml
ansible-playbook -i inventory/runtime_hosts.yml playbooks/prod01.yml
ansible-playbook -i inventory/runtime_hosts.yml playbooks/nom01.ymlDie IPs stehen nach dem Provisionieren in inventory/runtime_hosts.yml.
Beispiel:
ssh -i ~/.ssh/id_rsa ubuntu@192.168.122.22
ssh -i ~/.ssh/id_rsa ubuntu@192.168.122.237
ssh -i ~/.ssh/id_rsa ubuntu@192.168.122.249Mit funktionierender Host-Auflösung auch per Domain, z. B.:
ssh -i ~/.ssh/id_rsa ubuntu@dev.uranus.home.arpaBei local_test_mode: true erzwingen die Nginx-VHosts für Frontend und Backend HTTPS.
Typische URLs:
https://dev.uranus.home.arpa/https://api.dev.uranus.home.arpa/https://uranus.home.arpa/https://api.uranus.home.arpa/https://monitoring.uranus.home.arpa/https://nominatim.uranus.home.arpa/
Die Rolle roles/app_backend/tasks/main.yml patcht nach dem Checkout den Upstream-Backend-Code, um einen fehlschlagenden CheckAllDatabaseConsistency(...)-Startup-Check zu deaktivieren.
Grund:
- der aktuelle Dump und der erwartete Tabellenzustand des Backends passen nicht sauber zusammen
- ohne diesen Patch crasht der Dienst beim Start
Das ist eine bewusste pragmatische Deploy-Korrektur, kein idealer Upstream-Fix.
Die PostgreSQL-Rolle normalisiert den Dump vor dem Import:
CREATE SCHEMA uranus;->CREATE SCHEMA IF NOT EXISTS uranus;- alle
uranus.-Referenzen ->uranus. OWNER TO oklab-Statements werden entfernt
Grund:
- der Dump stammt offenbar aus einer anderen Umgebung
- Schema- und Ownership-Metadaten passen nicht 1:1 auf das Zielsystem
Die frühere Test-Rolle ist absichtlich nicht mehr in playbooks/configure.yml eingebunden, weil sie den Hauptlauf zu häufig blockiert hat.
Wenn ein späterer Task im Playbook fehlschlägt, kann es passieren, dass ein zuvor notifizierter nginx-Reload erst einmal nicht ausgeführt wird. In der Praxis reicht dann oft ein erneuter Lauf oder ein manueller sudo systemctl reload nginx auf der betroffenen VM.
Prüfen:
getent hosts dev.uranus.home.arpaWenn kein Treffer kommt, fehlen /etc/hosts-Einträge auf dem Host.
Auf der Ziel-VM prüfen:
sudo nginx -t
sudo systemctl reload nginx
sudo ls -la /etc/nginx/sites-enabledAuf der Ziel-VM prüfen:
sudo systemctl status uranus-backend.service --no-pager -l
sudo journalctl -u uranus-backend.service -n 100 --no-pager
curl -I http://127.0.0.1:9090/Ein 502 bedeutet fast immer: Nginx läuft, aber der Backend-Prozess auf localhost:9090 nicht.
Auf db01 prüfen:
sudo ss -ltnp | grep 5432
sudo ufw status numberedDie Freigabe ist absichtlich auf interne Netze begrenzt.
Das Repo setzt in ansible.cfg bereits:
remote_tmp = /tmpDas ist wichtig, weil mehrere Rollen per become_user auf andere Unix-User wechseln.
Die Platzhalterwerte liegen jetzt gesammelt in group_vars/vault.yml.example.
Vor echtem Einsatz solltest du mindestens setzen:
vault_cloudinit_passwordvault_db_password_devvault_db_password_prodvault_backend_jwt_secretvault_backend_secret_keyvault_nominatim_db_passwordvault_monitoring_web_db_passwordvault_monitoring_ido_db_passwordvault_monitoring_admin_passwordvault_monitoring_api_password
Das Repository ist kein generisches Produktions-Framework, sondern ein pragmatisch gewachsenes lokales Infrastruktur-Setup. Mehrere Stellen enthalten bewusste Anpassungen an den aktuellen Stand von:
- Upstream-Uranus-Backend
- bereitgestelltem SQL-Dump
- lokaler libvirt-Testumgebung
Die README dokumentiert deshalb den tatsächlichen Zustand des Repos und nicht nur den idealen Zielzustand.