- 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.
15 KiB
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 MongoDBfilter_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.