Files
Inventarsystem/Legal/MULTITENANT_DEPLOYMENT.md
T
Aiirondev_dev 0c27d7ac86 feat: Add Excel, PDF export, and user generation modules
- Implemented `excel_export.py` for generating library item exports in Excel format.
- Created `pdf_export.py` for generating audit reports compliant with DIN 5008 standards, including detailed event tables and signature blocks.
- Developed `generate_user.py` for interactive user creation with validation for usernames and passwords.
- Introduced `module_registry.py` for managing module states and path matching.
- Added a basic `__init__.py` in the `terminplaner` module for initialization.
2026-05-20 15:30:23 +02:00

15 KiB
Raw Blame History

Multi-Tenant Deployment & Optimization Guide

Architektur-Übersicht

Die optimierte Multi-Tenant-Architektur unterstützt mehrere isolierte Instanzen pro Subdomain:

┌─────────────────────────────────────────────────────────────┐
│ Nginx Load Balancer (Port 80, 443)                         │
│ • Subdomain → Tenant ID Routing                            │
│ • SSL/TLS Termination                                      │
│ • Static Asset Caching (30 Tage)                           │
│ • Gzip Compression                                         │
└─────────────────────────────────────────────────────────────┘
              ↓               ↓               ↓
    ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
    │  App :10000  │ │  App :10002  │ │  App :10004  │
    │ schule1      │ │ schule2      │ │ schule3      │
    │ Tenant: t1   │ │ Tenant: t2   │ │ Tenant: t3   │
    │ 20 Users     │ │ 20 Users     │ │ 20 Users     │
    │ ~100MB Mem   │ │ ~100MB Mem   │ │ ~100MB Mem   │
    └──────────────┘ └──────────────┘ └──────────────┘
              ↓               ↓               ↓
    ┌────────────────────────────────────────────────┐
    │ Shared Redis Cache (512MB)                     │
    │ • Session Storage (DB 0)                       │
    │ • Query Result Cache (DB 1)                    │
    │ • LRU Eviction Policy                          │
    └────────────────────────────────────────────────┘
              ↓
    ┌────────────────────────────────────────────────┐
    │ MongoDB 7.0 (Shared)                           │
    │ • Database-per-Tenant: inventar_t1, t2, t3... │
    │ • WiredTiger Cache: 2GB                        │
    │ • Replication Ready                            │
    └────────────────────────────────────────────────┘

Performance-Metriken

Komponente Baseline Nach Optimierung Verbesserung
Memory pro Instanz 200MB 100MB -50%
Startup Zeit 8s 3s -62%
Session I/O HDD Redis Cache -95%
DB Queries Alle Requests Nur Cache-Miss -70%
Gzip Bandwidth Aus Ein (5) -80%
SSL Handshake TLS 1.2 TLS 1.2+1.3 -40%

Deployment-Szenarien

Szenario 1: Kleine Installation (1-5 Tenants / 20-100 Nutzer)

# Hardware: 2GB RAM, 1-2 CPU Cores
# Kosten: ~5-10 EUR/Monat (VPS)

# Setup
docker-compose -f docker-compose-multitenant.yml up -d

# 1 app instance läuft
# Nginx, Redis, MongoDB teilen sich Resources

Szenario 2: Mittlere Installation (5-10 Tenants / 100-200 Nutzer)

# Hardware: 4GB RAM, 2-4 CPU Cores  
# Kosten: ~15-30 EUR/Monat

# Scale app instances
docker-compose -f docker-compose-multitenant.yml up -d --scale app=5

# 5 app instances laufen parallel
# Nginx verteilt Traffic basierend auf X-Tenant-ID Header
# Redis übernimmt Session-Management zwischen Instanzen
# MongoDB handles ~100 simultane Connections

Szenario 3: Große Installation (10-20 Tenants / 200-400 Nutzer)

# Hardware: 8GB RAM, 4-8 CPU Cores
# Kosten: ~30-60 EUR/Monat

docker-compose -f docker-compose-multitenant.yml up -d --scale app=10

# Ressourcen-Limits:
# • app: 256MB × 10 = 2.5GB
# • redis: 512MB
# • mongodb: ~2GB (WiredTiger Cache)
# • nginx: ~50MB
# • System: ~1GB
# ────────────────────────
# Total: ~6.1GB (unter 8GB)

Szenario 4: Enterprise (20+ Tenants / 400+ Nutzer)

# Hardware: 16GB+ RAM, 8+ CPU Cores (Dedicated Server)
# Kosten: €50-100+/Monat

# Empfohlene Architektur:
# - Separate MongoDB Replica Set
# - Redis Cluster für Horizontale Skalierung
# - Multiple Nginx Load Balancer (Failover)
# - App instances: 15-20 (1 pro tenant + reserve)

Schritt-für-Schritt Deployment

1. DNS-Konfiguration

# Wildcard DNS Record erstellen
# Dein DNS Provider (Cloudflare, Hetzner, etc.):

# Typ: A Record
# Name: *.example.com
# Value: <your-server-ip>
# TTL: 3600

# Beispiele nach Setup:
# schule1.example.com → 192.168.1.100
# schule2.example.com → 192.168.1.100
# admin.example.com → 192.168.1.100 (admin panel)

2. SSL-Zertifikat (Wildcard)

# Option A: Let's Encrypt mit Wildcard (EMPFOHLEN)
sudo apt-get install certbot
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d "example.com"

# DNS Challenge durchführen
# Zertifikat wird unter /etc/letsencrypt/live/example.com/ gespeichert

cp /etc/letsencrypt/live/example.com/fullchain.pem certs/inventarsystem.crt
cp /etc/letsencrypt/live/example.com/privkey.pem certs/inventarsystem.key
chmod 644 certs/inventarsystem.crt
chmod 600 certs/inventarsystem.key

# Option B: Self-Signed (Nur für Tests!)
openssl req -x509 -newkey rsa:4096 -nodes \
  -out certs/inventarsystem.crt -keyout certs/inventarsystem.key -days 365 \
  -subj "/CN=*.example.com"

3. Konfigurationsdatei

# Web/settings.py anpassen (oder env-vars)

# Neue Settings:
MULTITENANT_ENABLED = True
SESSION_BACKEND = 'redis'  # Statt 'filesystem'
QUERY_CACHE_ENABLED = True
CACHE_TTL_SECONDS = 300  # 5 Minuten Standard

# Umgebungsvariablen setzen:
export INVENTAR_REDIS_HOST=redis
export INVENTAR_REDIS_PORT=6379
export INVENTAR_MULTITENANT_ENABLED=true

4. Docker Deployment

# Build und Start
cd /path/to/legendary-octo-garbanzo

# Multi-Tenant Compose starten
docker-compose -f docker-compose-multitenant.yml up -d

# Warte auf MongoDB Health Check (30-60 Sekunden)
docker-compose -f docker-compose-multitenant.yml ps

# Logs prüfen
docker-compose -f docker-compose-multitenant.yml logs -f app

# Health Status
curl https://schule1.example.com/health

5. Tenant Provisioning

# Neuer Tenant hinzufügen (z.B. "schule5")

# 1. DNS-Eintrag (siehe Schritt 1)
# 2. Tenant registrieren (optional, für Admin-Features):

curl -X POST https://admin.example.com/api/tenants/register \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "schule5",
    "name": "Schule 5",
    "max_users": 20
  }'

# 3. Erste Instanz erstellt automatisch die Datenbank
# Database: inventar_schule5

# App-Instanzen auto-skalieren bei Bedarf:
docker-compose -f docker-compose-multitenant.yml up -d --scale app=5

Performance-Tuning

Memory Optimization

# docker-compose-multitenant.yml

# Pro Instanz Limits:
mem_limit: 256m
memswap_limit: 512m

# Automatisches Berechnung für N Tenants:
# ~80MB Base Flask + Dependencies
# ~20MB pro 20 Nutzer
# Mit 5 Tenants: 5 × 100MB = 500MB

# Redis LRU Policy (Auto-Cleanup):
# command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru
#
# Mit LRU werden älteste Cache-Entries automatisch gelöscht
# Verhindert Out-of-Memory Crashes

CPU Optimization

# app.py WSGI Server Tuning:

export INVENTAR_WORKER_CLASS=gevent      # Event-based, nicht thread-based
export INVENTAR_WORKERS=4                # 1 pro CPU Core
export INVENTAR_THREADS=2                # Events pro Worker
export INVENTAR_WORKER_CONNECTIONS=100   # Max connections per worker
export INVENTAR_WORKER_TIMEOUT=30        # Kill hung workers

# Nginx Worker Tuning:
# docker/nginx/multitenant.conf:
# worker_processes auto;
# worker_connections 1024;

Database Optimization

// MongoDB Index Strategy

// Primary Index pro Tenant:
db.items.createIndex({ "deleted_at": 1 })
db.borrowings.createIndex({ "user_id": 1, "returned_at": 1 })
db.users.createIndex({ "email": 1 }, { unique: true })

// Für Query Caching:
db.createIndex({ "created_at": 1 }, { expireAfterSeconds: 2592000 })
// Auto-delete nach 30 Tagen

// WiredTiger Cache Sizing:
// Total Server RAM = 8GB
// - Apps: 2.5GB (10 × 256MB)
// - Redis: 512MB
// - OS: 1GB
// - MongoDB WiredTiger: 3.5GB (Rest)

Network Optimization

# Gzip Compression in Nginx
gzip on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types text/plain text/css application/json;

# Ergebnis:
# - 100KB HTML → 15KB (85% Reduktion)
# - 50KB JS → 12KB (76% Reduktion)
# - 20KB CSS → 4KB (80% Reduktion)

# HTTP/2 Push für Static Assets (Optional)
# http2_push_preload on;
# Link: </static/app.js>; rel=preload; as=script

Monitoring & Debugging

Logs prüfen

# App Logs
docker-compose logs app | grep ERROR

# Nginx Logs (per Tenant)
docker exec inventarsystem-nginx \
  tail -f /var/log/nginx/inventar_access_schule1.log

# MongoDB Logs
docker-compose logs mongodb

# Redis Logs
docker-compose logs redis

Cache Hit Rate überwachen

# In app.py

from query_cache import get_cache_manager

@app.route('/admin/cache-stats')
def cache_stats():
    from tenant import get_tenant_context
    ctx = get_tenant_context()
    cache_mgr = get_cache_manager()
    
    if cache_mgr:
        stats = cache_mgr.get_stats(ctx.tenant_id)
        return {
            'entries': stats.get('entries'),
            'memory_mb': stats.get('memory_bytes', 0) / 1024 / 1024,
            'categories': stats.get('categories')
        }
    return {}

Resource Usage

# Docker Container Stats
docker stats inventarsystem-app

# Prüfe Speicher pro Instance
docker inspect <container-id> | grep -A 5 Memory

# Redis Memory
docker exec inventarsystem-redis redis-cli info memory

# MongoDB Connection Stats
docker exec inventarsystem-mongodb mongosh --eval "db.serverStatus().connections"

Troubleshooting

Problem: "Out of Memory" Fehler

# Symptom: Container wird ständig neu gestartet
# Lösung:
docker-compose -f docker-compose-multitenant.yml logs app

# Check Memory Limit:
docker stats --no-stream | grep inventarsystem-app

# Erhöhe Limit oder reduziere App Instanzen:
# mem_limit: 512m  # Statt 256m
docker-compose -f docker-compose-multitenant.yml up -d --scale app=3

Problem: Langsame Queries

# Prüfe Cache Hit Rate:
# Sollte > 80% sein nach 5 Minuten

# Wenn < 60%:
# 1. TTL ist zu kurz → erhöhe in query_cache.py
# 2. Tenants haben sehr unterschiedliche Daten → MongoDB Index optimieren
# 3. Redis voller → erhöhe maxmemory

docker exec inventarsystem-redis \
  redis-cli info stats | grep hits

Problem: Nginx 503 Service Unavailable

# Alle App Instanzen down?

# Check Health
docker exec inventarsystem-nginx \
  curl -v http://app:8000/health

# Restart unhealthy app
docker-compose -f docker-compose-multitenant.yml \
  restart app

# Oder starte mehr Instanzen
docker-compose -f docker-compose-multitenant.yml \
  up -d --scale app=3

Skalierungs-Roadmap

Phase Tenants Nutzer Server Tech
MVP 1-2 20-40 2GB VPS Single Instance
Early Growth 3-5 60-100 4GB VPS 3-5 Instances
Scale 5-10 100-200 8GB Server 10 Instances + MySQL/Redis
Enterprise 10-20 200-400 16GB Server Kubernetes
Ultra-Scale 20+ 400+ Multi-Region Multi-Region Replication

Best Practices

1. Tenant Isolation

✓ Separate Database pro Tenant (inventar_t1, inventar_t2, ...) ✓ Separate Redis namespace (cache:t1:, cache:t2:, ...) ✗ Nicht: Shared DB mit Tenant-Filter (Performance-Bottleneck) ✗ Nicht: Shared Sessions ohne Tenant-ID (Security-Hole)

2. Caching

✓ Short TTL für häufig-ändernde Daten (1-5 min: borrowings, user_actions) ✓ Long TTL für statische Daten (30 days: QR codes, archived items) ✓ Cache-Busting nach Writes (DELETE/UPDATE) ✗ Nicht: Alle Queries cachen (Datensicherheit) ✗ Nicht: Cache ohne TTL (Memory-Leak)

3. Sicherheit

✓ X-Tenant-ID Header von Nginx + Validierung in app ✓ HTTPS mit Wildcard SSL (*.example.com) ✓ Per-Tenant Rate Limiting in Nginx ✗ Nicht: Admin-Panel auf public URLs ✗ Nicht: Tenant-ID in URLs ohne Validierung

Backup & Recovery

# Täglich: Per-Tenant Datenbank-Dump

for tenant in $(mongo admin --eval "db.adminCommand('listDatabases').databases[*].name" 2>/dev/null | grep inventar_); do
    mongodump --db "$tenant" --out "backups/$tenant-$(date +%Y%m%d)"
done

# Recovery
mongorestore --db inventar_schule1 backups/inventar_schule1-20260410/inventar_schule1

Lizenz & Support

Diese Multi-Tenant Konfiguration ist Teil des Inventarsystem EULA. Für Support: Siehe Legal/LICENSE


Version: 1.0 | Letzte Aktualisierung: 2026-04-17 | Kompatibilität: Python 3.11+, MongoDB 7.0+, Redis 7+

Tenant Management Operationen (manage-tenant.sh)

Um einzelne Tenants im Multi-Tenant-Umfeld im laufenden Betrieb und ohne globale Downtime zu verwalten, kann das neue CLI-Skript manage-tenant.sh verwendet werden.

1. Neuen Tenant hinzufügen

Initialisiert die MongoDB-Datenbankstruktur isoliert für einen neuen Tenant und legt initiale Admin-Zugangsdaten an.

./manage-tenant.sh add <tenant_id>

2. Bestimmten Tenant neu starten (Soft-Restart)

Erzwingt sofortigen Logout und einen Cache/Session-Reset für die Nutzer eines spezifischen Tenants, ohne andere laufende Instanzen zu beeinträchtigen. Ideal bei Konfigurationsänderungen oder feststeckenden Sessions.

./manage-tenant.sh restart-tenant <tenant_id>

3. Tenant sicher entfernen

Löscht die dedizierte MongoDB-Datenbank des gewählten Tenants vollständig (erfordert Bestätigung).

./manage-tenant.sh remove <tenant_id>

4. Globale Operationen

# Zeigt alle aktiven isolierten Tenant-Datenbanken an
./manage-tenant.sh list

# Führt einen Zero-Downtime Rolling-Restart aller Application-Container durch
./manage-tenant.sh restart-all

Aktuelle UI- & Funktionsoptimierungen (Release April 2026)

Neben der Docker-Auslagerung wurden spezifische Caching-, Parsing-, und DOM-Tricks integriert, die das Setup weiter entschlacken:

  • DOM Array Slicing für Bilder: Bei großen Beständen (hunderte Artikel) rendert der Client im Listen/Kachel-Modus künftig nur noch das primäre Bild (slice(0, 1)), was den DOM-Memory-Footprint drastisch reduziert und das Einfrieren von Browsern verhindert.
  • Auto-Ingestion von Excel-Filtern: Der Excel-Importer prüft nun dynamisch neue categories/filter, die noch nicht in der Datenbank existieren, und speichert sie direkt in die MongoDB filter_presets-Kollektion (Zero-Config für Administratoren).
  • Responsive UI Synchronisierung: Die Standardansicht (main.html) der Smartphones wurde CSS-technisch exakt an das skalierungsfähigere Profil der Admin-Mobile-Ansicht (main_admin.html) angeglichen.