On-Premise-Betrieb¶
Upgrade-Verfahren¶
# 1. Datenbank und Speicher sichern (Ihre Verantwortung)
# 2. Neueste Änderungen pullen
cd kanap
git pull origin main
# 3. Die Images aus den geladenen Quellen bauen
docker compose -f infra/compose.onprem.yml build --pull
# 4. Container neustarten (Migrationen laufen automatisch)
docker compose -f infra/compose.onprem.yml up -d
# Der alte API-Container beendet zuerst die laufenden Anfragen, die in die Warteschlange
# gestellten E-Mails und seine laufenden Hintergrundjobs (bis zu 20 s), dann stoppt er.
Docker Compose baut die Images api und web selbst, aus den Quellen, die Sie gerade geladen haben. --pull lädt außerdem aktualisierte Basis-Images. Wer nur up -d ausführt, behält die alte Version, weil Compose die vorhandenen Images wiederverwendet. Führen Sie daher immer zuerst build aus.
Upgrade prüfen:
docker compose -f infra/compose.onprem.yml logs --tail=100 api
curl https://kanap.company.com/api/health
Das API-Log zeigt die Migrationen ([entrypoint] Migrations complete (N executed).) und danach den Start der API, ohne Fehler. Die Health-Adresse antwortet mit { "status": "ok" }.
Breaking Changes: Prüfen Sie CHANGELOG.md vor dem Upgrade.
Rollback: Datenbank aus Backup wiederherstellen. Migrationen sind nur vorwärtsgerichtet. Wechseln Sie danach mit git checkout <vorheriger Commit> zur vorherigen Version, führen Sie dieselben Befehle build --pull und up -d aus und prüfen Sie das Upgrade erneut. Führen Sie vor dem nächsten Upgrade git checkout main aus.
Versionsunterstützung¶
KANAP ist eine sich schnell weiterentwickelnde Lösung und wir empfehlen monatliche Upgrades. Für Kunden mit Supportvertrag kann vor der Bearbeitung einer Supportanfrage ein Upgrade auf die neueste Version erforderlich sein.
Backup & Wiederherstellung¶
- PostgreSQL: Verwenden Sie
pg_dump/pg_restoreoder verwaltete DB-Backups - S3-Speicher: Verwenden Sie Bucket-Versionierung, Replikation oder Provider-Backups
Empfehlung: Tägliche Datenbank-Backups, mindestens 30 Tage aufbewahren.
PostgreSQL-Einstellungen¶
Die Standardwerte von PostgreSQL sind für eine kleine Maschine ausgelegt. infra/postgres/kanap-pg-tune.sh gibt Einstellungen aus, die anhand des Arbeitsspeichers Ihres Servers dimensioniert sind (Speicher, SSD-Kosten, Protokoll langsamer Anweisungen, Anweisungsstatistiken). Führen Sie es auf dem PostgreSQL-Server aus und lesen Sie die Datei, bevor Sie sie anwenden: Der Kopf erklärt jeden Wert.
# Die von PostgreSQL bereits vorgeladenen Bibliotheken (oft keine): Das Skript behält sie bei.
CURRENT=$(sudo -u postgres psql -XAtc 'SHOW shared_preload_libraries')
# PostgreSQL auf demselben Server wie KANAP (fügen Sie --dedicated hinzu, wenn es den Server allein hat)
sh infra/postgres/kanap-pg-tune.sh --preload "$CURRENT" | sudo tee /etc/postgresql/16/main/conf.d/kanap.conf
sudo systemctl restart postgresql
sudo -u postgres psql -d kanap -c 'CREATE EXTENSION IF NOT EXISTS pg_stat_statements'
Zwei Prüfungen vor dem Neustart, beide vom Skript durchgeführt, das die Zeile shared_preload_libraries auskommentiert schreibt, wenn eine davon fehlschlägt:
- Die Liste der vorgeladenen Bibliotheken.
shared_preload_librariesist eine einzige Liste, und der Wert inkanap.confersetzt den inpostgresql.conf. Ohne--preloadfügen Sie den Wert vonSHOW shared_preload_librariesselbst voran (zum Beispiel'pg_cron,pg_stat_statements'), und entfernen Sie dann das#. - Die Bibliothek selbst. PostgreSQL startet nicht, wenn eine vorgeladene Bibliothek fehlt. Sie wird bei Debian und Ubuntu mit PostgreSQL mitgeliefert; bei RHEL und Derivaten installieren Sie das Contrib-Paket (
postgresql16-contrib). Prüfen Sie mitls "$(pg_config --pkglibdir)/pg_stat_statements.so".
Der Neustart ist einmalig nötig, für die Speichereinstellung und die Anweisungsstatistiken: Planen Sie ihn in einem Wartungsfenster, KANAP kann seine Datenbank während des PostgreSQL-Neustarts nicht erreichen. Anweisungen, die länger als 500 ms dauern, erscheinen danach im PostgreSQL-Protokoll, ohne ihre Parameter (log_parameter_max_length = 0: Sie können personenbezogene Daten enthalten). pg_stat_statements listet die teuersten Anweisungen:
SELECT calls, round(mean_exec_time) AS avg_ms, left(query, 80) AS query
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
Die Migrationen von KANAP lassen die Autovakuum-Bereinigung auf den beiden größten Tabellen (die Budgetbeträge) auch früher starten. Das erfordert weder Neustart noch Speicher.
Monitoring¶
Health-Endpunkt:
GET /api/health → { "status": "ok" }
Container-Status:
docker compose -f infra/compose.onprem.yml ps
docker compose -f infra/compose.onprem.yml logs -f api
Wichtige Kennzahlen:
- Container laufen (api, web)
- API-Speicher unter ~1 GB pro API-Prozess
- Datenbankverbindungen
- Speichernutzung
API-Metriken für ein Monitoring-Tool¶
Setzen Sie OPS_METRICS_TOKEN in .env (24 Zeichen oder mehr, zum Beispiel openssl rand -hex 32) und starten Sie die API neu. Ihr Monitoring-Tool kann dann lesen:
Die Antwort ist JSON. Ohne diese Einstellung antwortet die Adresse mit 404. Sie antwortet auch, wenn die API überlastet ist: Die Werte, die die Datenbank benötigen, sind dann mit db.statsStale markiert. Die zu beobachtenden Felder:
| Feld | Was es zeigt |
|---|---|
health.status |
ok, warn oder critical, anhand der Schwellenwerte unten. health.alerts listet, was nicht stimmt und was zu tun ist |
topRoutes |
Anfragen pro Route über 5 Minuten, mit Antwortzeiten p50, p95 und p99 in Millisekunden |
process.eventLoopLagMs.p95 |
Wie lange der Haupt-Thread der API Anfragen in der letzten Minute warten ließ |
db.pool.inUse, db.pool.inUseMax1m, db.pool.waitingCount |
Gerade genutzte Datenbankverbindungen, das Maximum der letzten Minute, auf eine wartende Anfragen |
db.pool.wait.p95Ms1m, db.pool.wait.failures5m |
Zeit, um eine Datenbankverbindung zu erhalten; Anfragen, die keine erhielten (Antwort „busy“) |
windows.5m.statusClasses |
Antworten pro Statusklasse über 5 Minuten |
processes, aggregate |
Bei mehreren API-Prozessen: jeder einzeln, und alle zusammen |
Alarmschwellen (health wendet sie an; bei mehreren API-Prozessen auf alle zusammen, und den Datenbankpool auf den am stärksten ausgelasteten). Ein Alarm löst oberhalb des Schwellenwerts aus:
| Alarm | Warnung | Kritisch | Was zu tun ist |
|---|---|---|---|
| Event-Loop p95 (1 min) | 100 ms | 500 ms | API-Prozesse hinzufügen (API_WORKERS), wenn der Server freie Kerne hat |
| Warten auf eine Datenbankverbindung, p95 (1 min) | 50 ms | 1 s | DB_POOL_MAX erhöhen, innerhalb von max_connections von PostgreSQL |
| Genutzte Verbindungen, Höchstwert über 1 min | 90 % des Pools | Dasselbe | |
| Anfragen ohne Verbindung (5 min) | beliebig | Prüfen, ob PostgreSQL läuft und max_connections nicht erreicht ist |
|
| Serverfehler (5 min, ab 20 Anfragen) | 1 % | 5 % | API-Protokoll lesen |
| p95 einer Route (5 min, ab 20 Anfragen) | 1 s | 3 s | Mit dem Routennamen melden; Import-, Export- und KI-Routen werden nicht gezählt |
| Speicher eines API-Prozesses | 1 GB | API neu starten; melden, wenn es wiederkehrt |
Fehlerbehebung¶
| Symptom | Prüfen | Lösung |
|---|---|---|
| Container starten nicht | docker compose logs api |
Auf Startfehler prüfen |
[DB] pool budget exceeded im API-Protokoll |
API_WORKERS, DB_POOL_MAX, PostgreSQL max_connections |
DB_POOL_MAX auf den im Meldungstext genannten Wert senken (oder API_WORKERS), oder max_connections erhöhen |
| "Database connection failed" | DATABASE_URL überprüfen |
PostgreSQL-Erreichbarkeit/Zugangsdaten prüfen |
| "S3 error" | S3_*-Variablen überprüfen | Sicherstellen, dass Bucket existiert und Berechtigungen korrekt sind |
| Migration fehlgeschlagen | PostgreSQL-Version prüfen | Muss 16+ sein, Erweiterungen verfügbar |
| 502 vom Reverse Proxy | docker compose ps |
Sicherstellen, dass api-Container auf Port 8080 läuft |
| Anmeldung nicht möglich | .env-Zugangsdaten überprüfen |
Passwortzurücksetzung unten verwenden |
Passwortzurücksetzung¶
Empfohlen: E-Mail konfigurieren (Resend API oder Single-Tenant SMTP) und den „Passwort vergessen"-Flow verwenden.
Fallback (SQL): Wenn E-Mail nicht konfiguriert ist, Passwörter direkt in der Datenbank zurücksetzen.
1) Passwort-Hash generieren:
# Mit Node.js und argon2
# (argon2 ist eine Produktionsabhängigkeit im API-Image)
docker compose -f infra/compose.onprem.yml exec api \
node -e "require('argon2').hash('NeuesPasswort123!').then(h => console.log(h))"
2) Benutzer in PostgreSQL aktualisieren:
UPDATE users
SET password_hash = '$argon2id$v=19$m=65536,t=3,p=4$...'
WHERE email = 'benutzer@firma.de';
Diese SQL-Methode ist ein letzter Ausweg für ausgesperrte Administratoren.