# 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) ```bash # 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) ```bash # 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) ```bash # 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) ```bash # 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 ```bash # Wildcard DNS Record erstellen # Dein DNS Provider (Cloudflare, Hetzner, etc.): # Typ: A Record # Name: *.example.com # Value: # 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) ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```yaml # 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 ```bash # 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 ```javascript // 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 ```nginx # 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: ; rel=preload; as=script ``` ## Monitoring & Debugging ### Logs prüfen ```bash # 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 ```python # 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 ```bash # Docker Container Stats docker stats inventarsystem-app # Prüfe Speicher pro Instance docker inspect | 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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. ```bash ./manage-tenant.sh add ``` ### 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. ```bash ./manage-tenant.sh restart-tenant ``` ### 3. Tenant sicher entfernen Löscht die dedizierte MongoDB-Datenbank des gewählten Tenants vollständig (erfordert Bestätigung). ```bash ./manage-tenant.sh remove ``` ### 4. Globale Operationen ```bash # 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.