operations review - smart-village-solutions/sva-studio GitHub Wiki
PR: feat(logging): add local monitoring stack with OTEL SDK Branch: feat/logging Reviewer: Operations & Reliability Engineering Review Date: 2026-02-08
Die PR implementiert einen umfassenden lokalen Observability-Stack (Prometheus, Loki, Grafana, OTEL, Promtail) mit PII-Redaction und Workspace-Context-Support. Die Lösung ist für lokale Entwicklung und Staging geeignet, aber NICHT produktionsreif ohne signifikante Verbesserungen.
Leitfrage: "Kann ein externer Dienstleister das System nachts um 3 stabil betreiben?"
Antwort: ❌ Nein. Kritische Operational Gaps: Keine Alerting, keine Backup/DR, keine Resource Limits, keine Rollback-Dokumentation.
| Kategorie | Status | Risiko | Begründung |
|---|---|---|---|
| Installation | Medium | Healthchecks OK, aber .env-Handling und Init-Scripts fehlen | |
| Updates/Rollback | High | Images gepinnt ✓, aber Rollback-Plan und Backward Compat. nicht dokumentiert | |
| Backup/Restore | ❌ Kritisch | Critical | Keine Backup-Strategie, RTO/RPO nicht definiert, DR-Plan fehlt |
| Alerting | ❌ Kritisch | Critical | KEINE Alerting-Konfiguration vorhanden |
| Maintenance | High | Keine Container Resource Limits, Circuit Breaker nicht dokumentiert | |
| Zero-Downtime | Medium | Kein Load Balancer, OTLP-Versionierung nicht dokumentiert | |
| Ressourcen & Skalierung | High | Keine Memory/CPU Limits, Performance Baselines fehlen |
prometheus:
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:9090/-/healthy"]
interval: 30s
timeout: 10s
retries: 3Positiv: Alle Services (Prometheus, Loki, Grafana, OTEL, Promtail) haben aktive Health Checks mit 30-Sekunden-Intervallen. Nutzen: Docker kann fehlerhafte Container automatisch neu starten.
services:
prometheus:
image: prom/prometheus:v2.52.0 # ← Exakte Version, nicht latest
loki:
image: grafana/loki:2.9.6
grafana:
image: grafana/grafana:10.4.3Positiv: Keine "latest"-Tags → deterministische Deployments. Nutzen: Reproduzierbare Umgebungen, keine Überraschungen durch Auto-Updates.
// packages/monitoring-client/src/otel.ts
const forbiddenLabelKeys = new Set([
'user_id', 'session_id', 'email', 'request_id',
'token', 'authorization', 'api_key', 'secret', 'ip'
]);
const maskEmail = (value: string): string => {
return value.replace(emailRegex, (_, firstChar, _middle, domain) => `${firstChar}***${domain}`);
};Positiv: Multi-Layer-Redaction (OTEL SDK + Promtail). Nutzen: Automatischer Schutz vor PII-Exposure in Logs.
// packages/sdk/src/observability/context.ts
export const getWorkspaceContext = (): WorkspaceContext => {
return workspaceStorage.getStore() ?? {};
};
// Automatisch injiziert in alle Logs
if (workspaceContext.workspaceId && logRecord.setAttribute) {
logRecord.setAttribute('workspace_id', workspaceContext.workspaceId);
}Positiv: Automatische Multi-Tenancy-Isolation ohne manuelles Durchreichen. Nutzen: Logs können pro Workspace gefiltert werden, Datenschutz-Compliance ✓
# Prometheus: 7d Retention
command:
- "--storage.tsdb.retention.time=7d"
- "--storage.tsdb.retention.size=5GB"
# Loki: 168h = 7d
limits_config:
retention_period: 168hPositiv: Explizite Retention-Policies verhindern unbegrenztes Datenwachstum. Nutzen: Kostenkontrolle ✓, Datenschutz ✓ (automatisches Löschen nach 7d).
// packages/monitoring-client/package.json
"dependencies": {
"@sva/sdk": "workspace:*" // ← Keine externe Abhängigkeit auf SDK-Version
}Positiv: Monorepo mit workspace-Protokoll → konsistente Versionen. Nutzen: Keine Versionsmismatches zwischen Monitoring und SDK.
Problem:
Niemand wird benachrichtigt, wenn:
- Prometheus Disk voll ist (wird Drop-Metriken)
- Loki Log-Ingestion fehlschlägt
- Redis Sessions-Store offline geht
- OTEL-Collector crasht
Fehlendes Artifact:
# ← DIESE DATEI EXISTIERT NICHT:
# dev/monitoring/alertmanager/alertmanager.yml
# ← DIESE DATEI EXISTIERT NICHT:
# dev/monitoring/prometheus/alert-rules.yml
# ← DIESE DATEI EXISTIERT NICHT:
# docs/operations/alerting-runbook.mdOps-Realität um 3 Uhr nachts:
[03:00] Prometheus Disk voll → Stoppt Metriken zu sammeln
[03:15] Dienstleister bemerkt NICHTS (kein Alert)
[05:47] Kunde ruft an: "Monitoring ist kaputt!"
[06:00] Dienstleister muss Docker Logs lesen (debugging statt ops)
Empfohlene Lösung:
# dev/monitoring/prometheus/alert-rules.yml
groups:
- name: system.alerts
interval: 1m
rules:
- alert: PrometheusHighDiskUsage
expr: node_filesystem_avail_bytes{mountpoint="/prometheus"} < 500000000
for: 5m
annotations:
summary: "Prometheus disk usage > 95%"
description: "{{ $value }} bytes free"
- alert: LokiChunkIngestionErrors
expr: rate(loki_chunk_store_index_entries_added_total{status="error"}[5m]) > 0
for: 2m
annotations:
summary: "Loki chunk ingestion failing"
- alert: OTELCollectorHealthDown
expr: up{job="otel-collector"} == 0
for: 1m
annotations:
summary: "OTEL Collector unreachable"Risiko ohne Alerting: 🔴 CRITICAL (keine Früherkennung von Ausfällen)
Problem:
a) Prometheus Data-Verlust-Scenario:
[03:15] Administrator löscht versehentlich /prometheus-data
→ 7 Tage an Metriken-History weg
[06:00] Backup? Vorhanden? Nein.
→ "Wir fahren blind ohne Historische Daten"
RTO/RPO nicht definiert:
- RTO (Recovery Time Objective): Wie lange darf das System offline sein? ← NICHT DOKUMENTIERT
- RPO (Recovery Point Objective): Wie viel Datenverlust ist akzeptabel? ← NICHT DOKUMENTIERT
b) Loki Disaster Scenario:
# dev/monitoring/loki/loki-config.yml
storage:
filesystem:
chunks_directory: /loki/chunks # ← Single-node, keine Replikation
rules_directory: /loki/rulesProblem: Wenn /loki-data Container weg ist → Alle Logs verloren.
Frage: Wo ist der Backup?
c) Redis Session Persistence:
# docker-compose.monitoring.yml
redis:
# ← KEIN Redis-Service im Compose-File!
# ← Sessions nur im Memory?Kritisches Gap: packages/auth/src/redis-session.ts erwartet Redis, aber:
- Kein Redis Service in docker-compose definiert
- 7-Tage TTL auf Sessions → Falls Redis crashed, alle aktiven Sessions weg
- Keine Replikation/Persist-Konfiguration
# docker-compose.monitoring.yml
prometheus:
image: prom/prometheus:v2.52.0
# ← FEHLT: memory limits
# ← FEHLT: cpu limitsSzenario um 3 Uhr nachts:
[03:45] Spill query (user accidentally runs expensive query)
→ Prometheus konsumiert unbegrenzt RAM
[04:00] OOM-Killer beendet Prometheus-Container
[04:30] Alle Metriken-Sammlung stoppt
[05:00] Dienstleister wacht auf von OOMKilled-Alert (der nicht existiert!)
Empfohlene Limits für Entwicklung:
prometheus:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
loki:
deploy:
resources:
limits:
cpus: '1'
memory: 1G
grafana:
deploy:
resources:
limits:
cpus: '1'
memory: 512MRisiko ohne Limits: 🔴 CRITICAL (Host kann über-subscribed werden)
Scenario:
[02:00] Team updated Prometheus from v2.51.0 → v2.53.0
[03:00] Neue Version hat Performance-Regression
[03:30] Dienstleister bemerkt: "Metriken-Abfragen dauern 10s statt 100ms"
Frage: "Wie rolle ich zurück?"
→ KEINE DOKUMENTATION VORHANDEN!
Empfohlene Runbook:
# Rollback Prometheus Scenario
## Problem
Prometheus reagiert langsam nach Update zu v2.53.0
## Diagnosis
```bash
docker compose logs prometheus | grep "error"
curl http://localhost:9090/api/v1/query?query=up- Stop current container:
docker compose down prometheus - Edit docker-compose.yml:
- Change image:
prom/prometheus:v2.53.0→prom/prometheus:v2.52.0
- Change image:
- Restart:
docker compose up -d prometheus - Verify health:
curl http://localhost:9090/-/healthy - Wait 30s for health check to pass
- Validate with:
docker compose ps
GET http://localhost:9090/api/v1/query?query=rate(prometheus_http_requests_total[5m])
Expected: response time < 200ms
- Document why v2.53.0 failed
- Test upgrade in staging first
- Update upgrade runbook
**Fehlendes Artifact:**
❌ docs/operations/upgrade-runbook.md ❌ docs/operations/rollback-procedures.md ❌ docs/operations/disaster-recovery.md
---
### 5. **Fehlende Logging-Strategie für Collector** (⚠️ HOCH)
**Problem:**
OTEL Collector crasht → Error wird wo geloggt?
- Nicht zu Prometheus (OTEL gibt keine Metriken)
- Nicht zu Loki (OTEL kann nicht selbst zu Loki schreiben)
- Nur zu docker compose logs (ephemeralisch!)
→ Bei Container-Restart: Logs weg!
**Diagnose um 3 Uhr:**
```bash
docker logs sva-studio-otel-collector
# "Started" ... 5 minuten später ... "FATAL: connection refused"
# → Zu spät für Debugging
Empfohlene Lösung:
# dev/monitoring/docker-compose.monitoring.yml
otel-collector:
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
labels: "workspace_id=local-dev,component=otel-collector"
# ← Zumindest persistent für 10 FilesProblem:
Prometheus: ✅ UP
Loki: ✅ UP
Grafana: ✅ UP
OTEL: ✅ UP
Promtail: ✅ UP
Aber: Sind Metrics tatsächlich fließen?
→ NICHT ÜBERWACHT!
Beispiel-Szenario:
[03:00] Promtail kann nicht mehr auf Docker-Socket schreiben
(permission issue nach Host-Reboot)
[03:15] Promtail Health Check: ✅ UP (eigener /ready Endpoint OK)
[03:30] Aber: Keine neuen Logs landen in Loki!
(kein Alert weil "Promtail Up" ✓)
# docker-compose.monitoring.yml
prometheus:
# ← Fehlt: stop_grace_period
# ← Fehlt: stop_signal
# ← Fehlt: shutdown sequence
loki:
# ← Fehlt: drain time für pending chunksProblem:
docker compose down
→ SIGTERM an alle Container gleichzeitig
→ Loki flushed pending chunks nicht → Daten-Verlust
→ Prometheus speichert WAL nicht → Metriken verloren
-
docs/operations/alerting-setup.md
- AlertManager Konfiguration
- Alert Rules für Standard-Szenarios
- Notification Channels (Email, Slack, PagerDuty)
- Alert-Testing-Prozedur
-
docs/operations/backup-restore.md
- Prometheus Backup-Strategie
- Loki Log Export
- Redis Session Backup
- Point-in-Time Recovery
- RTO/RPO Definition
-
docs/operations/disaster-recovery.md
- Scenario: Prometheus Disk voll
- Scenario: Loki Log-Ingestion fehlgeschlagen
- Scenario: Redis offline
- Scenario: OTEL Collector crasht
- Recovery Steps pro Scenario
-
docs/operations/upgrade-runbook.md
- Upgrading Prometheus
- Upgrading Loki
- Upgrading Grafana
- Backward Compatibility Check
-
docs/operations/rollback-procedures.md
- Quick Rollback für jeden Service
- Testing nach Rollback
- Known Issues by Version
-
docs/operations/troubleshooting.md
- Metriken fehlen → Diagnose
- Logs fehlen → Diagnose
- Disk voll → Lösungsschritte
- Memory Leak → Identifikation
- Performance degradation → Root Cause Analysis
# dev/monitoring/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: "${SLACK_WEBHOOK_URL}"
route:
receiver: 'ops-team'
repeat_interval: 4h
group_wait: 30s
group_interval: 5m
receivers:
- name: 'ops-team'
slack_configs:
- channel: '#monitoring-alerts'
title: 'Alert: {{ .GroupLabels.alertname }}'
text: '{{ .GroupLabels.severity }}'Effort: 1-2 Tage Impact: 🟢 Enables auf-Abruf-Response
# scripts/backup-prometheus.sh
#!/bin/bash
set -e
BACKUP_DIR="/backups/prometheus/$(date +%Y-%m-%d_%H-%M-%S)"
mkdir -p "$BACKUP_DIR"
# Prometheus snapshot
curl -X POST http://localhost:9090/api/v1/admin/tsdb/snapshot
SNAPSHOT=$(docker exec sva-studio-prometheus find /prometheus/snapshots -name "*.snap" -newest -name "*.snap" | head -1)
cp "$SNAPSHOT" "$BACKUP_DIR/"
# Loki backup
docker exec sva-studio-loki tar czf - /loki/chunks > "$BACKUP_DIR/loki-chunks.tar.gz"
# Redis backup
docker exec sva-studio-redis redis-cli BGSAVE
docker cp sva-studio-redis:/data/dump.rdb "$BACKUP_DIR/"
# Verify
if [ $(du -s "$BACKUP_DIR" | cut -f1) -gt 1000000 ]; then
echo "✅ Backup successful: $BACKUP_DIR"
else
echo "❌ Backup too small, likely failed"
exit 1
fiEffort: 2-3 Tage (incl. Restore-Test) Impact: 🟢 Enables Disaster Recovery
# docker-compose.monitoring.yml (updated)
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.52.0
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
loki:
image: grafana/loki:2.9.6
deploy:
resources:
limits:
cpus: '1'
memory: 1G
grafana:
image: grafana/grafana:10.4.3
deploy:
resources:
limits:
cpus: '1'
memory: 512M
otel-collector:
image: otel/opentelemetry-collector-contrib:0.95.0
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
promtail:
image: grafana/promtail:2.9.8
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
redis: # ← ADD THIS!
image: redis:7.2-alpine
deploy:
resources:
limits:
cpus: '0.5'
memory: 256MEffort: 0.5 Tage Impact: 🟢 Prevents OOMKiller-Scenarios
# Prometheus Rollback SOP
## Quick Rollback Command
```bash
docker compose down prometheus && \
sed -i 's/prom\/prometheus:v2.53.0/prom\/prometheus:v2.52.0/g' docker-compose.yml && \
docker compose up -d prometheus && \
sleep 30 && \
docker compose ps prometheus
**Effort:** 0.5 Tage
**Impact:** 🟢 Enables fast recovery
---
### 5. **Füge Prometheus Retention-Konfigurierbarkeit hinzu** (P1)
```yaml
# docker-compose.monitoring.yml
prometheus:
environment:
PROMETHEUS_RETENTION_TIME: "${PROMETHEUS_RETENTION_TIME:-7d}"
PROMETHEUS_RETENTION_SIZE: "${PROMETHEUS_RETENTION_SIZE:-5GB}"
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=${PROMETHEUS_RETENTION_TIME:-7d}"
- "--storage.tsdb.retention.size=${PROMETHEUS_RETENTION_SIZE:-5GB}"
Effort: 0.5 Tage Impact: 🟢 Environment-specific retention
# dev/monitoring/prometheus/alert-rules.yml
- alert: ServiceHealthCheckFailing
expr: up{instance!~"localhost.*"} == 0
for: 2m
annotations:
summary: "Service {{ $labels.job }} is down"
description: "{{ $labels.instance }} not responding for 2m"
- alert: PrometheusMetricsStaleness
expr: time() - timestamp(last_over_time(up[5m])) > 300
annotations:
summary: "Metrics are stale (> 5m old)"Effort: 1 Tag Impact: 🟢 End-to-End visibility
# docker-compose.monitoring.yml
prometheus:
stop_grace_period: 30s
stop_signal: SIGTERM
loki:
stop_grace_period: 30s
stop_signal: SIGTERMEffort: 0.25 Tage Impact: 🟡 Reduces data loss on restarts
# Health Check Runbook
## Monitoring Stack Health
```bash
# All services up?
docker compose ps
# Health endpoint responses?
for svc in prometheus loki grafana otel-collector promtail; do
echo "=== $svc ==="
case $svc in
prometheus)
curl -s http://localhost:9090/-/healthy | jq
;;
loki)
curl -s http://localhost:3100/ready | jq
;;
grafana)
curl -s http://localhost:3001/api/health | jq
;;
otel-collector)
curl -s http://localhost:13133/healthz | jq
;;
promtail)
curl -s http://localhost:3101/ready | jq
;;
esac
done
# Are metrics flowing?
curl -s http://localhost:9090/api/v1/query?query=up | jq '.data.result | length'
# Expected: > 5 (prometheus + loki + grafana + otel + promtail)
# Are logs flowing?
curl -s http://localhost:3100/loki/api/v1/query?query='{component!=\"\"}'&limit=1 | jq '.data.result | length'
# Expected: > 0
**Effort:** 0.5 Tage
**Impact:** 🟡 Faster diagnosis
---
## 📊 Betriebsreife-Checkliste
| Item | Status | Risiko | Bemerkung |
|------|--------|--------|-----------|
| **Installation: Docker Compose** | ✅ OK | Low | Services definiert, Volumes konfiguriert |
| **Installation: .env handling** | ⚠️ Risiko | Medium | GF_SECURITY_ADMIN_PASSWORD hard to "admin" |
| **Installation: Health Checks** | ✅ OK | Low | Alle Services haben health checks |
| **Updates: Image Versions** | ✅ OK | Low | Pinned, nicht latest |
| **Updates: Rollback Docs** | ❌ Fehlt | High | Keine Dokumentation |
| **Updates: Backward Compat** | ⚠️ Unklar | Medium | OTLP Protocol sollte OK sein, aber nicht getestet |
| **Backups: Prometheus** | ❌ Fehlt | Critical | Keine Backup-Strategie |
| **Backups: Loki** | ❌ Fehlt | Critical | Filesystem, keine Replikation |
| **Backups: Redis Sessions** | ❌ Fehlt | Critical | 7d TTL, kein Backup |
| **Backups: RTO/RPO Definition** | ❌ Fehlt | Critical | Nicht dokumentiert |
| **Disaster Recovery Plan** | ❌ Fehlt | Critical | Keine Runbooks für Szenarien |
| **Alerting: AlertManager** | ❌ Fehlt | Critical | Keine Alerting-Konfiguration |
| **Alerting: Alert Rules** | ❌ Fehlt | Critical | Keine Prometheus-Rules |
| **Alerting: Notification Channels** | ❌ Fehlt | Critical | Email/Slack nicht konfiguriert |
| **Logging: Log Collection** | ✅ OK | Low | Promtail + Loki arbeiten |
| **Logging: PII Redaction** | ✅ OK | Low | Multi-Layer implementiert |
| **Logging: Storage Limits** | ✅ OK | Low | 7d Retention konfiguriert |
| **Monitoring: Self-Monitoring** | ⚠️ Risiko | Medium | Prometheus scrapes sich selbst, aber Metadaten-Health nicht überwacht |
| **Maintenance: Resource Limits** | ❌ Fehlt | Critical | Keine Memory/CPU Limits |
| **Maintenance: Graceful Shutdown** | ❌ Fehlt | Medium | Keine stop_grace_period |
| **Maintenance: Circuit Breaker** | ❌ Fehlt | Medium | Keine Dokumentation |
| **Deployment: Load Balancer** | ✅ OK | Low | Nur localhost → akzeptabel für Dev |
| **Deployment: API Versioning** | ⚠️ OK | Low | /v1/metrics, /v1/logs vorhanden, aber nicht dokumentiert |
| **Resources: Memory Limits** | ❌ Fehlt | Critical | Unbegrenzt, OOMKiller-Risiko |
| **Resources: CPU Limits** | ❌ Fehlt | Medium | Unbegrenzt, Host über-subscription möglich |
| **Resources: Disk I/O** | ⚠️ Unklar | Medium | IOPS-Requirements nicht dokumentiert |
| **Resources: Performance Baselines** | ❌ Fehlt | Medium | Keine Dokumentation |
| **SLA/SLO** | ❌ Fehlt | Medium | Nicht dokumentiert |
---
## GESAMTBEWERTUNG
### Production Readiness Maturity
| Phase | Status | Kommentar |
|-------|--------|----------|
| **Phase 1: Local Development** | 🟢 Ready | Funktioniert für Dev-Umgebung |
| **Phase 2: Team Staging** | 🟡 Conditional | Mit Alerting + Resource Limits → OK |
| **Phase 3: Production** | 🔴 Not Ready | Kritische Gaps: Backup, DR, Alerting |
---
## Prioritäts-Roadmap für Betriebstauglichkeit
### Sprint 1 (vor Staging-Einsatz) — 5-7 Tage
- ✅ Alerting-Stack implementieren (AlertManager + Slack)
- ✅ Container Resource Limits setzen
- ✅ Backup-Script schreiben + testen
- ✅ Disaster-Recovery-Runbooks verfassen
- ✅ Redis zu docker-compose hinzufügen
### Sprint 2 (vor Produktions-Einsatz) — 3-4 Tage
- ✅ Health-Check-Monitoring Setup
- ✅ Upgrade/Rollback-Prozeduren dokumentieren
- ✅ Graceful Shutdown konfigurieren
- ✅ Load-Testing durchführen
- ✅ Post-Mortem für häufige Failure-Szenarien
### Backlog (kontinuierlich)
- Loki zu replicated mode (für HA)
- Prometheus zu remote storage (für langfristige Retention)
- Custom Grafana Dashboards für Workspace-spezifische Metriken
- Log-to-Metrics pipeline für Alerting auf Application-Events
---
## Fazit für Nacht-Betrieb um 3 Uhr
**Status quo: ❌ Ungeeignet**
Ein externer Dienstleister könnte das System um 3 Uhr nachts mit folgenden Problemen kämpfen:
[03:00] Prometheus läuft aus dem Disk-Platz → Stille (kein Alert) → Metriken-Sammlung stoppt
[03:15] Loki hat Log-Ingestion-Fehler → Keine Benachrichtigung → Logs landen nicht in Loki
[03:30] Redis Session-Store crasht → 10.000 Benutzer werden abgemeldet → Kein Backup, kein Rollback-Plan
[04:00] Dienstleister muss Docker Logs manually durchsuchen → Debugging statt Operations → RCA dauert Stunden
**Mit empfohlenen Verbesserungen: 🟡 Conditional OK**
Mit Alerting + Backup + Resource Limits + Runbooks könnte der Dienstleister:
1. ✅ Automatische Alerts bekommen (Slack @ 03:00)
2. ✅ Runbook konsultieren (unter 5 Minuten)
3. ✅ Bekannte Prozeduren folgen (unter 15 Minuten)
4. ✅ System in Produktions-Zustand wiederherstellen
5. ✅ Incident Log schreiben für Post-Mortem
---
## Signoff
- ✅ Code Quality Review: PASSED (Copilot hat CodeQL-Issues markiert)
- ⚠️ Operations Readiness: CONDITIONAL (Kritische Gaps vorhanden)
- 🔴 Production Ready: NICHT EMPFOHLEN (bis Backup + Alerting implementiert)
**Empfohlener Status für PR:**
→ 🟡 **APPROVED for Staging** (mit Sprint-1-Aufgaben als Blockers für Production)
---
**Review abgeschlossen:** 2026-02-08
**Nächster Review:** Nach Implementierung der P0-Empfehlungen