Infrastruktur als Code für den Skalierungs-Test auf Hetzner: - deploy/terraform: privates Netz, Firewalls, 2 App-Nodes, DB-Node, Load Balancer (Health-Check /health); cloud-init bootet Docker + Stack je Node - deploy/compose/docker-compose.prod.yml + nginx.prod.conf: App-Node-Stack (PHP-FPM + Nginx) routet /api,/p,/t,/css,/health → Symfony, Rest → Vue-SPA - App-Anpassungen: HealthController (/health für LB), brand.css nach /css verschoben (kein Pfad-Clash mit SPA-Assets im Prod-Routing) - deploy/README.md: Anleitung inkl. JWT-Key-Verteilung & Cross-Node-Test - reference.php (auto-generiert) aus Versionierung entfernt Terraform validiert (terraform validate), Prod-Compose-Syntax geprüft. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| compose | ||
| terraform | ||
| README.md | ||
Deployment auf Hetzner Cloud (Terraform)
Provisioniert einen Multi-Node-Test mit nachgewiesener Skalierbarkeit:
Hetzner Load Balancer (lb11, :80 → /health)
/ \
vcard4-app-1 vcard4-app-2 (zustandslose App-Nodes)
\ /
┌─────────┴───────────┐ ┌─────┴──────────────────┐
vcard4-db (MariaDB, Volume) Hetzner Object Storage (S3, Assets)
App-Nodes sind zustandslos (Code + Docker), State liegt in DB (eigene VM) und Object Storage (Hintergrund-PDFs/Schriften). Dadurch beliebig horizontal skalierbar.
Voraussetzungen (einmalig, manuell)
- Terraform ≥ 1.6 + Hetzner API-Token (Projekt → Security → API Tokens, Read&Write).
- Object Storage in der Hetzner Console anlegen: Bucket + Access-Key/Secret (hcloud/Terraform verwalten Object Storage derzeit nicht).
- Git-Repo erreichbar für die Nodes (öffentlich oder Deploy-Token in der
repo_url). - JWT-Schlüsselpaar einmal erzeugen — auf allen Nodes identisch:
Inhalt der beiden PEM-Dateien + Passphrase in# lokal im backend/-Container oder mit openssl docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite # → backend/config/jwt/private.pem & public.pem + Passphrase aus .env (JWT_PASSPHRASE)terraform.tfvarseintragen. - DNS: nach
terraform applyeinen A-Recorddomain → load_balancer_ipsetzen (Hetzner DNS ist separat; A-Record auch für die Custom-Domain-Funktion, KONZEPT §11).
Deploy
cd deploy/terraform
cp terraform.tfvars.example terraform.tfvars # ausfüllen
terraform init
terraform plan
terraform apply
terraform output # load_balancer_ip etc.
cloud-init installiert auf jedem App-Node Docker, klont das Repo, schreibt
.env.prod.local + JWT-Keys, baut das SPA und startet
deploy/compose/docker-compose.prod.yml. Migrationen laufen nur auf app-1.
Deploy-Log auf dem Node: /var/log/vcard4-deploy.log.
Skalierbarkeit verifizieren
# 1) Login + Token holen (LB-IP oder Domain)
TOKEN=$(curl -s -X POST http://<LB-IP>/api/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@vcard4reseller.de","password":"admin"}' | jq -r .token)
# 2) Health über den LB (verteilt auf beide Nodes)
for i in $(seq 1 6); do curl -s http://<LB-IP>/health; echo; done
# 3) Cross-Node-Beweis: Hintergrund über den LB hochladen, dann
# auf BEIDEN Nodes rendern (per SSH) – identische PDFs aus Object Storage:
ssh root@<app-1-ip> 'cd /opt/vcard4 && docker compose -f deploy/compose/docker-compose.prod.yml exec -T php php bin/console app:render-card erika-mustermann'
ssh root@<app-2-ip> 'cd /opt/vcard4 && docker compose -f deploy/compose/docker-compose.prod.yml exec -T php php bin/console app:render-card erika-mustermann'
Horizontal skalieren: app_count erhöhen → terraform apply (LB-Targets werden automatisch ergänzt).
Noch offen / Hinweise
- TLS: aktuell HTTP am LB. Für HTTPS entweder Hetzner Managed Certificate am LB (DNS bei Hetzner) oder Caddy auf den Nodes (On-Demand-TLS) — letzteres ist auch der Weg für die Custom-Domains der Firmenkunden (§11).
- Trusted Proxies: für korrekte absolute URLs hinter dem LB
framework.trusted_proxiesauf%env(TRUSTED_PROXIES)%setzen. - Seed: optional einmalig
app:seedaufapp-1für Demo-Daten. - Updates: neuen Stand ausrollen = auf den App-Nodes
git pull+docker compose ... up -d --build(später per CI/Skript).