leistungsbilanz-ts/docs/deployment.md
Julian Appel fa96be2d42 Fix crash handling and log noise in logging feature
Review of the logging work from the previous commit turned up a real
correctness issue: the uncaughtException/unhandledRejection handlers
logged the error but let the process keep running, which silently
disabled Node's default crash-on-fatal-error behavior and could leave
a zombie process serving broken requests instead of restarting. Both
processes now log and then exit(1); restart: unless-stopped is added
to both services so Docker actually brings them back up.

Also: harden the logger against JSON.stringify throwing on
non-serializable meta, log aborted (closed-before-finished) requests
in the API access log, and stop the Next.js proxy from logging the
Docker healthcheck's request to "/" every few seconds so real
navigation events aren't drowned out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 19:58:31 +02:00

4.8 KiB
Raw Permalink Blame History

Deployment und Betrieb

Aktueller Status

Es gibt derzeit kein unterstütztes Produktionsdeployment.

compose.yaml ist ausschließlich für lokale Entwicklung vorgesehen. Es startet tsx watch und next dev, bindet Quellcode vom Host ein und enthält weder TLS, Authentifizierung, Reverse Proxy, Prozesshärtung noch ein zentral betriebenes Datenbanksystem. Der Stack darf deshalb nicht als produktionsreif bezeichnet oder öffentlich erreichbar gemacht werden.

Entwicklungs-Topologie

Komponente Port Healthcheck Persistenz
Next.js Web 3001 GET / keine
Express API 3000 GET /health ./data:/app/data
SQLite Datei Integritäts-/FK-Prüfung via Backup und Skript data/leistungsbilanz.db

Verwendete Umgebungsvariablen:

  • PORT API-Port, Standard 3000
  • API_INTERNAL_URL internes API-Ziel des Next.js-Rewrites, im Compose-Netz http://api:3000
  • NEXT_TELEMETRY_DISABLED=1
  • CHOKIDAR_USEPOLLING=true und WATCHPACK_POLLING=true für lokale Dateibeobachtung in Docker
  • LOG_LEVEL steuert für beide Dienste die Ausgabestufe des strukturierten JSON-Loggers (error, warn, info, verbose, debug), Standard info. Setzbar über eine .env-Datei neben compose.yaml oder LOG_LEVEL=verbose docker compose up.

Beim API-Start laufen zuerst npm run db:migrate und npm run db:verify:circuit-schema.

Logging

Beide Dienste schreiben strukturierte, einzeilige JSON-Log-Zeilen nach stdout/stderr (docker compose logs --follow). Jede Zeile enthält timestamp, level, scope und message. compose.yaml konfiguriert für beide Dienste den json-file-Treiber mit Rotation (max-size: 20m, max-file: 10, also bis zu 200 MB je Dienst); ohne diese Einstellung würde Docker mit der Standardkonfiguration unbegrenzt in eine einzelne Datei unter /var/lib/docker/containers/<container-id>/ schreiben. Die Logs überleben einen Container-Neustart (docker compose restart), aber nicht das Entfernen des Containers (docker compose down gefolgt von up erzeugt neue Container und damit neue, leere Logdateien); für ein echtes Langzeitarchiv über Rebuilds hinweg müssten die Zeilen zusätzlich in eine Datei im gemounteten ./data-Verzeichnis oder an ein externes Log-System geschrieben werden. Die Express-API protokolliert jede abgeschlossene Anfrage (Methode, Pfad, Status, Dauer; /health wird nicht mitgeloggt) sowie unbehandelte Exceptions/Promise-Rejections. Das Next.js-Frontend protokolliert Seitenanfragen (Navigation) über src/proxy.ts und unbehandelte Fehler über src/instrumentation.ts. Beide Prozesse schreiben zusätzlich alle fünf Minuten einen verbose-Heartbeat mit Laufzeit und Speicherverbrauch nützlich, um Speicherlecks oder Hänger vor einem 502 über einen längeren Zeitraum nachzuvollziehen. Für die Detailsuche LOG_LEVEL=debug setzen; das protokolliert zusätzlich den Start jeder API-Anfrage und macht damit hängende (nie abgeschlossene) Requests sichtbar. Ein close-Ereignis ohne vorheriges finish wird als request aborted before response finished (warn) geloggt und zeigt damit vom Client oder einem vorgeschalteten Proxy abgebrochene Verbindungen.

Eine unbehandelte Exception oder Promise-Rejection wird geloggt und beendet den jeweiligen Prozess anschließend bewusst (process.exit(1)), statt in einem unbekannten Zustand weiterzulaufen. Beide Dienste laufen deshalb mit restart: unless-stopped, damit Docker sie danach automatisch neu startet; ohne diese Policy würde ein Crash den Dienst dauerhaft unerreichbar lassen.

Voraussetzungen für ein späteres Produktionssetup

Vor einer produktiven Installation werden mindestens benötigt:

  • reproduzierbare Produktionsimages und ein Next.js-Produktionsstartskript
  • TLS-Termination und Reverse Proxy
  • Authentifizierung, Autorisierung und Benutzer-/Rollenmodell
  • definierte Secrets- und Konfigurationsverwaltung
  • persistenter, gesicherter Datenbankbetrieb
  • kontrollierter einmaliger Migrationsschritt vor dem API-Rollout
  • Monitoring, strukturierte Logs und Alarmierung
  • getestete Backup-, Restore- und Rollback-Prozeduren
  • Entscheidung, ob SQLite für einen einzelnen Prozess genügt oder PostgreSQL für Mehrbenutzerbetrieb erforderlich ist

Bis diese Punkte umgesetzt und getestet sind, besteht die „Installation“ aus dem lokalen Entwicklungsstart in der README.

Backup und Wiederherstellung

npm run db:backup erzeugt unter data/backups/ ein konsistentes SQLite-Backup, öffnet es unabhängig und prüft Integrität sowie Fremdschlüssel. Diese Dateisicherungen sind von zukünftigen logischen Projektversionen getrennt.

Eine produktive Restore-Anweisung wird erst zusammen mit einem unterstützten Produktionsbetrieb veröffentlicht. Lokale Wiederherstellung darf nur bei gestoppter API, gegen eine separate Datei und nach erneuter Integritätsprüfung erfolgen.