Monitoring & Alerting (P7)¶
Ziel: Live-Betrieb mit minimal überwachten Risiken. Diese Datei ist Ops-Runbook für die GCP-Console-Setup-Schritte. Alle Alerts sollen pro Tenant-Projekt eingerichtet werden (Single-Tenant-Architektur).
1. Daily Firestore Backup¶
- Job:
scheduledFirestoreBackup(Cloud Functioneurope-west3) - Schedule:
0 2 * * *UTC (override viasystem_settings/backup_settings.schedule) - Bucket:
gs://${PROJECT_ID}-firestore-backup(override übersystem_settings/backup_settings.bucketOverride)
One-time Setup pro Projekt¶
PROJECT_ID="<tenant-projekt>"
# Bucket anlegen (EU multi-region)
gsutil mb -l eu -p "$PROJECT_ID" "gs://${PROJECT_ID}-firestore-backup"
# Lifecycle: Backups älter als 35 Tage automatisch löschen
cat > /tmp/lifecycle.json <<'JSON'
{
"lifecycle": {
"rule": [
{ "action": { "type": "Delete" }, "condition": { "age": 35 } }
]
}
}
JSON
gsutil lifecycle set /tmp/lifecycle.json "gs://${PROJECT_ID}-firestore-backup"
# IAM für den Default-Compute-SA
SA="$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')-compute@developer.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:${SA}" \
--role="roles/datastore.importExportAdmin"
gsutil iam ch "serviceAccount:${SA}:roles/storage.admin" \
"gs://${PROJECT_ID}-firestore-backup"
Restore¶
# Letztes Backup-Verzeichnis ermitteln
gsutil ls "gs://${PROJECT_ID}-firestore-backup/exports/" | tail -1
# Import (Achtung: schreibt in dieselbe DB!)
gcloud firestore import \
"gs://${PROJECT_ID}-firestore-backup/exports/2024-12-31/2024-12-31T02:00:00_12345/"
1.2 Full System Backup (fullBackup Job)¶
Neu seit Mai 2026: Vollständiges tägliches Backup aller Systemartefakte.
- Job:
fullBackup(System-Job, Cloud Scheduler) - Schedule:
0 2 * * *UTC (täglich 02:00 Uhr) - Bucket:
gs://${PROJECT_ID}-firestore-backup - Artefakte:
- Firestore-Export (alle Collections via Management API)
- Firebase Auth-User (JSON-Export mit PII)
- Firestore Rules (
core/firestore.rules) - Storage Rules (
core/storage.rules) - Firestore Indexes (
core/firestore.indexes.json) - CORS-Konfiguration (
core/cors.prod.json,core/cors.dev.json) - Hosting-Headers (
core/hosting_headers_core.json) - Remote Config (falls genutzt)
Cloud Scheduler Job registrieren¶
Der Job wird über das Seed-Skript in Firestore angelegt, aber der Cloud Scheduler Job muss separat registriert werden:
cd core/functions
# Einmalig pro Projekt: Cloud Scheduler Jobs für System-Jobs anlegen
node -e "
process.env.GCLOUD_PROJECT = '${PROJECT_ID}';
const schedulerUtils = require('./src/utils/scheduler_utils');
async function main() {
await schedulerUtils.createOrUpdateSchedulerJob(
'fullBackup',
{ type: 'cron', cronExpression: '0 2 * * *' },
'system'
);
console.log('✅ fullBackup Scheduler Job angelegt');
}
main().catch(e => console.error(e)).finally(() => process.exit(0));
"
Oder über die ERP-UI:
- Job Management → fullBackup auswählen
- Schedule bearbeiten (ohne Änderung speichern) → triggert createOrUpdateSchedulerJob
Backup-Struktur¶
gs://${PROJECT_ID}-firestore-backup/
manual/
20260609_140000/
firestore/ # Firestore-Export
auth/users_20260609_140000.json
config/
firestore.rules
storage.rules
firestore.indexes.json
cors.prod.json
hosting_headers_core.json
exports/ # Legacy scheduledFirestoreBackup
Restore (manuell)¶
Siehe BACKUP & RESTORE COMMAND SYSTEM in .github/copilot-instructions.md:
# Alle Backups auflisten
backup:list
# Spezifisches Backup wiederherstellen
restore <backup-id>
# Nur Firestore
restore:firestore gs://.../manual/20260609_140000/firestore
# Nur Config-Dateien aus Git
restore:config <git-ref>
1.3 GCS Object Versioning¶
Pflicht seit Juni 2026: Aktiviert für alle Firebase Storage Buckets.
Object Versioning schützt vor versehentlichem Überschreiben oder Löschen von Dateien im Firebase Storage Bucket.
Aktivierung¶
Via Google Cloud Console:
- Cloud Storage Browser öffnen
- Bucket
${PROJECT_ID}.firebasestorage.app(oder.appspot.com) auswählen - Tab Protection → Object versioning
- Enable versioning aktivieren
- Empfohlene Lifecycle-Regeln aktivieren:
- ☑️ Checkbox "Empfohlene Lebenszyklusregeln zum Verwalten der Versionskosten hinzufügen"
- Max. Anzahl von Versionen pro Objekt:
3 - Nicht aktuelle Versionen löschen nach:
30 Tage
Via gsutil CLI:
# Versioning aktivieren
gsutil versioning set on gs://${PROJECT_ID}.firebasestorage.app
# Status prüfen
gsutil versioning get gs://${PROJECT_ID}.firebasestorage.app
# Erwartete Ausgabe: Enabled
# Lifecycle-Regel für noncurrent Versions setzen
cat > /tmp/storage-lifecycle.json <<'JSON'
{
"lifecycle": {
"rule": [
{
"action": { "type": "Delete" },
"condition": {
"numNewerVersions": 3
}
},
{
"action": { "type": "Delete" },
"condition": {
"daysSinceNoncurrent": 30,
"isLive": false
}
}
]
}
}
JSON
gsutil lifecycle set /tmp/storage-lifecycle.json gs://${PROJECT_ID}.firebasestorage.app
Wiederherstellung einer älteren Version¶
# Alle Versionen eines Objekts auflisten
gsutil ls -a gs://${PROJECT_ID}.firebasestorage.app/path/to/file.pdf
# Spezifische Version wiederherstellen
gsutil cp gs://${PROJECT_ID}.firebasestorage.app/path/to/file.pdf#<generation> \
gs://${PROJECT_ID}.firebasestorage.app/path/to/file.pdf
Kosten¶
- Noncurrent Versions werden nach 7 Tagen automatisch in Nearline/Coldline Storage verschoben (günstigere Klasse)
- Nach 30 Tagen oder bei > 3 Versionen: automatische Löschung
- Geschätzte Mehrkosten: < 5 €/Monat bei typischer Nutzung
1.4 Backup Health Monitoring¶
Neu seit Mai 2026: Automatische tägliche Prüfung aller Backup-Mechanismen.
- Job:
backupHealthCheck(System-Job, Cloud Scheduler) - Schedule:
0 4 * * *UTC (täglich 04:00 Uhr, 2h nach fullBackup) - Status-Dokument:
_system/backup_health_state(Firestore)
Geprüfte Komponenten¶
| Komponente | Status Grün | Status Gelb | Status Rot |
|---|---|---|---|
| fullBackup Job | Letzter Lauf < 26h + success | Letzter Lauf 26–50h | > 50h oder failed |
| Firestore PITR | Aktiviert | API-Fehler | Nicht aktiviert |
| GCS Object Versioning | Aktiviert | Nicht aktiviert | n/a |
Aggregierter Status: - 🔴 Rot: Mindestens ein Check = red - 🟡 Gelb: Kein red, mindestens ein yellow - 🟢 Grün: Alle checks = green
Backup-Ampel im ERP-Dashboard¶
SuperAdmins sehen im Dashboard eine Backup-Status-Ampel neben der System-Health-Ampel:
- Grün: Alle Backup-Mechanismen funktionieren
- Gelb: GCS Versioning nicht aktiviert (nicht kritisch, aber empfohlen)
- Rot: fullBackup läuft nicht oder PITR deaktiviert (kritisch!)
- Grau (Unbekannt): Job noch nie gelaufen (nur beim ersten Setup)
Tap auf Ampel: öffnet Detail-Modal mit Status aller drei Checks.
Cloud Scheduler Job registrieren¶
Analog zu fullBackup:
cd core/functions
node -e "
process.env.GCLOUD_PROJECT = '${PROJECT_ID}';
const schedulerUtils = require('./src/utils/scheduler_utils');
async function main() {
await schedulerUtils.createOrUpdateSchedulerJob(
'backupHealthCheck',
{ type: 'cron', cronExpression: '0 4 * * *' },
'system'
);
console.log('✅ backupHealthCheck Scheduler Job angelegt');
}
main().catch(e => console.error(e)).finally(() => process.exit(0));
"
Firestore Rules¶
// _system/backup_health_state: nur SuperAdmins können lesen
match /_system/backup_health_state {
allow read: if isSuperAdmin();
allow write: if false; // Nur Cloud Functions via Admin SDK
}
2. Cloud Monitoring – Alert-Policies¶
Alle Alerts: Notification-Channel security-alerts@<kunde> (E-Mail) + optional
PagerDuty/Slack. In der GCP-Console unter Monitoring → Alerting → Create Policy.
2.1 Function Error Rate > 5 %¶
- Resource type:
cloud_run_revision(Functions v2 laufen auf Cloud Run) - Metric:
run.googleapis.com/request_countmit Filterresponse_code_class != "2xx" - Aggregation:
aligner=ALIGN_RATE,reducer=REDUCE_SUMper Service - Condition: Verhältnis (5xx + 4xx ohne 401/403) zu Gesamt > 0.05 für 5 min
- Auto-close: 30 min
2.2 Auth-Failure-Spike > 50 / min¶
- Resource: Logs-based metric auf
_rateLimits-Schreibzugriffe oder Cloud Logging Filter:resource.type="cloud_function" AND jsonPayload.event="auth_failure" - Threshold: count > 50 in 1 min
- Hinweis: Quelle ist
AuditLogger.logFailure({ action: "auth_*" }).
2.3 RateLimit-Sättigung¶
- Logs-based metric: Filter
jsonPayload.event="rate_limit_exceeded"(vonrate_limiter.js). - Alert: > 100 Hits / 5 min für eine Action → Hinweis auf Bot/Brute-Force.
2.4 AuditLog – CRITICAL Events¶
- Logs-based metric: Firestore-Collection
auditLogs, Fieldseverity == "CRITICAL". Praktischer ist eine Logs-based Metric direkt aus den Function-Logs (secureLogger.errorschreibt strukturiert). - Alert: count > 0 im 5-min-Fenster → sofortige Mail.
2.5 SMTP-Send-Failures¶
- Filter:
resource.type="cloud_function" AND jsonPayload.fn IN ("sendCustomerInvitationEmail","sendPriceListEmail") AND severity>=ERROR - Threshold: > 5 Fehlschläge / 10 min
- Hinweis: typische Ursachen: Strato-Quota, falsches Secret, App-Passwort abgelaufen.
3. Operational Logging-Conventions¶
secureLogger.error/warn/infoschreibt strukturierte Felder; PII wird viasecureLogger-Redactor entfernt.- Custom-Felder, die in Alerts genutzt werden:
event– z. B.rate_limit_exceeded,auth_failurefn– Funktionsname (für SMTP-Filter)connectorId,customerId– Routing in Dashboards- Niemals raw Tokens/Passwörter loggen. Code-Review wird durch ESLint-Regel
no-pii-loggingautomatisiert.
4. Pre-Go-Live-Check¶
| Check | Verantwortlich |
|---|---|
| Bucket + Lifecycle in jedem Tenant-Projekt vorhanden | DevOps |
scheduledFirestoreBackup deployed & 1× manuell getriggert |
DevOps |
| 5 Monitoring-Alerts aktiv mit Notification-Channel | DevOps |
| Restore-Probe in Staging dokumentiert | DevOps |
system_settings/backup_settings.enabled=true (oder Default) |
Operations |