Reverse-Proxy: schöne Hostnamen statt localhost:port

Zielgruppe: DevOps / jede:r, der/die sich localhost:8180 und Co. nicht merken will. Phase 12. Setzt enterprise-workspace/shared/enterprise-infrastructure voraus (liegt bewusst außerhalb dieses Repos, siehe dort das README für den vollen Kontext).

Das Problem

Jeder lokale Dienst dieses Projekts lief bisher nur unter localhost:<port> - Keycloak localhost:8180, Grafana localhost:3000, Jenkins localhost:8091, und so weiter (volle Tabelle in docker-compose-guide.md). Nachteile:

Die Lösung: Traefik + ein gemeinsames externes Container-Netzwerk

Traefik ist ein Reverse-Proxy, der Container über Labels automatisch erkennt und routet (via Podmans Docker-kompatible API - der Traefik-Provider heißt weiterhin "docker") - kein manuelles Nachpflegen einer separaten Proxy-Konfigurationsdatei bei jedem neuen Dienst, wie es z. B. bei klassischem nginx nötig wäre. Traefik selbst läuft zentral in enterprise-infrastructure (außerhalb dieses Repos, projektübergreifend), dieses Projekt "meldet" seine Dienste nur per Label an.

Wie die Verbindung zustande kommt

┌─────────────────────────────┐        ┌──────────────────────────────────┐
│ enterprise-workspace/shared/enterprise-infrastructure/            │        │ library-enterprise-platform/       │
│                              │        │ infra/docker-compose.yml           │
│  ┌────────────┐              │ proxy  │  ┌────────────┐                    │
│  │  Traefik   │◄─────────────┼────────┼──┤  keycloak  │ (+ 9 weitere)      │
│  │ Port 80    │  (externes,  │ Netz   │  └────────────┘                    │
│  └────────────┘  gemeinsames │        │        │                          │
│  ┌────────────┐  Netzwerk)   │        │   library-net (unveraendert,      │
│  │  Jenkins   │              │        │   Container erreichen sich intern │
│  └────────────┘              │        │   weiter ueber Container-Namen)   │
└─────────────────────────────┘        └──────────────────────────────────┘

Zwei Zutaten in infra/docker-compose.yml:

1) Das externe Netzwerk referenzieren (shared/enterprise-infrastructure/docker-compose.yml erzeugt es, dieses Projekt bindet es nur ein):

networks:
  library-net:
    driver: bridge
  proxy:
    external: true
    name: proxy

2) Pro Dienst mit Weboberfläche: dem proxy-Netzwerk zusätzlich beitreten (NICHT als Ersatz für library-net - additives Prinzip, wie schon beim dritten Kafka-Listener in Phase 11) und Traefik-Labels setzen. Beispiel keycloak:

keycloak:
  # ... unveraendert ...
  networks: [library-net, proxy]
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.library-keycloak.rule=Host(`keycloak.library.localhost`)"
    - "traefik.http.services.library-keycloak.loadbalancer.server.port=8080"
    - "traefik.docker.network=proxy"

Wichtig: die bestehenden ports:-Mappings bleiben unverändert bestehen. localhost:8180 funktioniert also weiterhin genau wie vorher - der Hostname ist ein zusätzlicher, gleichwertiger Zugriffsweg, kein Ersatz. Das library--Präfix in Router-/ Service-Namen und im Hostnamen selbst ist nötig, weil Traefik keine Projektgrenzen kennt: ein zweites angeschlossenes Projekt mit einem eigenen "keycloak"-Dienst würde sonst Namen überschreiben (siehe shared/enterprise-infrastructure/README.md für das allgemeine Muster). Das traefik.docker.network=proxy-Label ist nötig, sobald ein Container an ZWEI Netzwerken hängt - ohne es könnte Traefik das falsche wählen. (Der Label-Key heißt auch unter Podman traefik.docker.network - "docker" ist der Provider-Name, nicht die Laufzeit.)

Hostnamen-Übersicht

Dienst Neuer Hostname Weiterhin auch unter
Keycloak http://keycloak.library.localhost localhost:8180
pgAdmin http://pgadmin.library.localhost localhost:5050
Kafka UI http://kafka-ui.library.localhost localhost:8090
RabbitMQ (Management) http://rabbitmq.library.localhost localhost:15673
Prometheus http://prometheus.library.localhost localhost:9090
Grafana http://grafana.library.localhost localhost:3000
Zipkin http://zipkin.library.localhost localhost:9411
Eureka-Dashboard (discovery-server) http://discovery.library.localhost localhost:8761
Config-Server (REST-API) http://config.library.localhost localhost:8888
api-gateway (Anwendung) http://api.library.localhost localhost:8080
Jenkins (projektübergreifend, siehe unten) http://jenkins.localhost - (kein Host-Port mehr, siehe jenkins-pipeline.md)
Traefik-Dashboard http://traefik.localhost -

Die sechs Fachservices (catalog-service, member-service, ...) bekommen bewusst KEINE eigenen Hostnamen - sie werden im normalen Betrieb nicht direkt aufgerufen, sondern ausschließlich über api-gateway (api.library.localhost).

Warum .localhost und kein Eintrag in der hosts-Datei nötig ist

.localhost ist laut RFC 6761 eine reservierte Top-Level-Domain, die IMMER auf die lokale Maschine (127.0.0.1) zeigen MUSS. Chrome, Firefox und Edge lösen *.localhost-Namen deshalb automatisch auf - kein Eintrag in C:\Windows\System32\drivers\etc\hosts nötig, funktioniert direkt im Browser. Nur für Kommandozeilen-Werkzeuge außerhalb des Browsers (z. B. curl in einem Skript), die diese Auflösung nicht automatisch beherrschen, bräuchte es zusätzlich einen manuellen hosts-Eintrag (Administratorrechte nötig) - für den normalen Browser-Zugriff auf diese Dienste nicht erforderlich.

Alternativen (kurz - Details siehe tech-stack.md)

Zwei unabhängige Entscheidungen stecken in dieser Lösung, jede hätte auch anders ausfallen können:

Voraussetzung: Enterprise Infrastructure läuft

cd ../../shared/enterprise-infrastructure   # relativ zu diesem Projekt, siehe Repo-Layout in lernpfad.md
podman compose up -d

Läuft unabhängig von diesem Projekt im Hintergrund weiter (siehe shared/enterprise-infrastructure/README.md). Ohne laufende Enterprise Infrastructure funktionieren die .localhost-Hostnamen nicht (Traefik selbst läuft dort) - die klassischen localhost:<port>-Zugriffe sind davon unberührt.

Troubleshooting

Behobener, unabhängiger Bug: pgAdmin startete nicht mehr

Bei der Verifikation dieser Phase fiel auf, dass library-pgadmin bereits VOR dieser Änderung nicht mehr startete ('admin@library-platform.local' does not appear to be a valid email address) - eine neuere dpage/pgadmin4:8-Patch-Version validiert die PGADMIN_DEFAULT_EMAIL-Umgebungsvariable strenger und lehnt die .local-Endung ab. Betraf NUR pgAdmin selbst, nicht die hier beschriebene Traefik-Integration (der pgadmin.library.localhost- Router war korrekt konfiguriert, hatte aber keinen laufenden Container zum Routen) - im ersten Durchgang dieser Phase bewusst nicht mitgefixt (unabhängiges Problem), inzwischen behoben: PGADMIN_DEFAULT_EMAIL auf admin@library-platform.com geändert (besteht dieselbe Validierung), siehe docker-compose-guide.md.

Bewusst nicht umgesetzt (Vertiefungsaufgaben)

Siehe shared/enterprise-infrastructure/README.md, Abschnitt "Grenzen dieses Ansatzes" - insbesondere: kein HTTPS (nur Port 80), Jenkins' vorinstallierte Tool-Versionen sind auf DIESES Projekt zugeschnitten (ein zweites angeschlossenes Projekt mit anderen Versionsanforderungen bräuchte dedizierte Container-Build-Agents statt eines fest bestückten Jenkins-Images).

⌂ Cockpit