140 lines
8.9 KiB
Markdown
140 lines
8.9 KiB
Markdown
# Modul-15: Monitoring-Control
|
||
|
||
**Status: IMPLEMENTIERT & E2E-verifiziert (noch NICHT freigegeben)** · Container `Modul-15-Monitoring-Control` · Image `monitoring-control:0.1.0`
|
||
|
||
## Zweck
|
||
Zentrale **technische Überwachung und Sicherheits-/Kontrollebene** des Trading-Systems.
|
||
Überwacht die Dienste M03–M14 (Health/Readiness/Erreichbarkeit) plus RabbitMQ-Queues/Consumer
|
||
und PostgreSQL und leitet daraus deterministisch einen **globalen Trading-Status**
|
||
(`ENABLED` / `PAUSED` / `HALTED` / `UNKNOWN`) ab.
|
||
|
||
**Restriktionen (Nutzer-Vorgabe):**
|
||
- **Keine KI/ML**, **keine Trading-Entscheidungen**, **keine Brokerorders**, **keine Shell-/Docker-Control-Rechte**.
|
||
- **FAIL-CLOSED:** Bei unklarem/kritischem Zustand wird Trading sicher blockiert (`HALTED`).
|
||
- M15 unerreichbar → M09 behandelt den Zustand als `HALTED` (siehe Design-Vorschlag M15→M09, noch offen).
|
||
- **Keine Event-Schleifen:** Events nur bei Status-Änderung, dedupliziert.
|
||
- Container wird **nicht** automatisch neu gestartet/gekillt — M15 beobachtet und entscheidet Status.
|
||
- Port 55015 **nur Docker-intern** (`expose`, kein Host-Port).
|
||
|
||
## Architektur / Datenfluss
|
||
```
|
||
M03–M14 (health/ready) RabbitMQ (Queues) PostgreSQL
|
||
│ │ │
|
||
└──────────┬────────────┴──────────────┘
|
||
▼
|
||
Modul-15-Monitoring-Control
|
||
├── probe/ MonitoringProbe (HTTP-health + RMQ passive declare + PG)
|
||
├── rules/ MonitoringRules (deterministisch, FAIL-CLOSED, versioniert)
|
||
├── core/ MonitorService (Status-Engine, Single Source of Truth)
|
||
├── storage/ Persistenz (PostgreSQL: system_health/service_health/control_event)
|
||
├── publisher/ ControlPublisher (market.control, nur bei Status-Änderung)
|
||
└── api/main.py REST-API (intern, Port 55015)
|
||
```
|
||
Status → `system_health` (append), Services → `service_health` (upsert), Änderungen → `control_event` (append-only Audit).
|
||
|
||
## API (Docker-intern, Port 55015, kein öffentliches Port-Mapping)
|
||
```
|
||
GET /health liveness
|
||
GET /health/ready readiness (postgresql)
|
||
GET /monitoring/status aktueller System-Status + Trading-Status (internes State-Modell)
|
||
GET /monitoring/services aktueller Zustand aller überwachten Services/Queues
|
||
GET /control/status Control-Status (manual_override, control_api_enabled)
|
||
GET /control/trading-state autoritativer Trading-Status für M09
|
||
POST /control manuelle Control PAUSE/HALT/RESUME (nur wenn CONTROL_API_ENABLED=true)
|
||
```
|
||
|
||
## Deterministische Regeln (Rules-Engine, Version 0.1.0)
|
||
Präzedenz strikt von oben nach unten (FAIL-CLOSED):
|
||
1. **UNKNOWN** — PostgreSQL nicht erreichbar → nicht verlässlich → `HALTED`.
|
||
2. **UNHEALTHY/HALTED** — kritische Infrastruktur (PG/RabbitMQ) oder Execution-Pfad down.
|
||
3. **DEGRADED/PAUSED** — nicht-kritische Dienste down / stale.
|
||
4. **HEALTHY/ENABLED** — alles gut.
|
||
|
||
| Bedingung | System | Trading | Reason-Code |
|
||
|---|---|---|---|
|
||
| PostgreSQL nicht erreichbar | UNHEALTHY | HALTED | `POSTGRESQL_UNREACHABLE` (+ `PERSISTENCE_FAILURE`) |
|
||
| RabbitMQ nicht erreichbar | UNHEALTHY | HALTED | `RABBITMQ_UNREACHABLE` |
|
||
| Kritischer Service down (M03–M09) | UNHEALTHY | HALTED | `CRITICAL_SERVICE_DOWN:<id>` |
|
||
| Market Data stale (> max_age) | UNHEALTHY | HALTED | `MARKET_DATA_STALE` |
|
||
| Kritische Queue Consumer fehlt | — | HALTED | `NO_CONSUMER_<queue>` |
|
||
| Kritischer Queue-Backlog ≥1000 | — | HALTED | `QUEUE_BACKLOG_CRIT:<queue>:<n>` |
|
||
| Nicht-kritische Unhealthy (Analytics/Notification) | DEGRADED | PAUSED | `CRITICAL_SERVICE_DOWN`/`NO_CONSUMER_…` |
|
||
| Queue-Backlog ≥100 / Warning | DEGRADED | PAUSED | `QUEUE_BACKLOG:<queue>:<n>` |
|
||
| Alles healthy | HEALTHY | ENABLED | — |
|
||
|
||
**FAIL-CLOSED-Hinweis:** `UNKNOWN` Trading-Status wird in der API auf `HALTED` gemappt (nie ENABLED aus unklarem Zustand).
|
||
|
||
## Events (market.control Exchange, keine Event-Schleife)
|
||
Events werden **nur bei Status-Änderung** publiziert (Idempotenz) — nie bei jedem Poll:
|
||
|
||
| Event-Typ | Routing-Key | Anlass |
|
||
|---|---|---|
|
||
| `system.degraded` | `system.degraded` | System → DEGRADED |
|
||
| `system.halted` | `system.halted` | System → UNHEALTHY (HALT) |
|
||
| `system.recovered` | `system.recovered` | System → HEALTHY |
|
||
| `trading.state` | `trading.state` | Trading-Status-Änderung |
|
||
| `service.unhealthy` | `service.unhealthy` | Einzelner Service UNHEALTHY |
|
||
|
||
## Persistenz / Audit (PostgreSQL Modul-01)
|
||
- `system_health` — append-only Zeile pro Beobachtung (aktueller Status + reason_codes).
|
||
- `service_health` — Upsert pro `service_id` (aktueller Zustand jedes Services/Queue).
|
||
- `control_event` — **append-only Audit-Trail** jeder Status-/Trading-Änderung (`STATE_CHANGE`) und manuellen Control-Aktion (`MANUAL_*`). Idempotent — eine Änderung wird **genau einmal** auditiert.
|
||
|
||
## Manuelle Control (PAUSE/HALT/RESUME)
|
||
- **Nur wenn `CONTROL_API_ENABLED=true`** (Default `false` → manuelle Control über API deaktiviert, nur Regel-getrieben).
|
||
- **RESUME wird verweigert** (`RESUME_BLOCKED_CRITICAL_CAUSE`), solange eine kritische Ursache besteht.
|
||
- Jede manuelle Aktion wird append-only in `control_event` auditiert.
|
||
|
||
## Sicherheit
|
||
- **Keine Secrets in DB/Logs/Doku/Commits** — Zugangsdaten ausschließlich via Environment (Compose).
|
||
- **Kein Docker-Socket**, **keine Shell-/Docker-Control-Rechte** (CMD=`python main.py`).
|
||
- Port 55015 nur intern (`expose`), `restart: unless-stopped`, non-root (uid 1001).
|
||
- Env: `POSTGRES_*`, `RABBITMQ_*`, `CONTROL_API_ENABLED`.
|
||
|
||
## Compose / Betrieb
|
||
- Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL`, RabbitMQ `Modul-02-RabbitMQ`.
|
||
- Env per Compose, Zugangsdaten nur via ENV (`POSTGRES_*`, `RABBITMQ_*`), nie hart kodiert.
|
||
|
||
## Unit-Tests
|
||
- `tests/test_monitoring.py`: **18/18 grün** — deterministische Rules, FAIL-CLOSED (PG/RabbitMQ/Critical), stale, Queue-Consumer, Idempotenz, Audit genau einmal, Recovery, Reconnect, keine Secrets.
|
||
|
||
## E2E-Verifikation (VPS, 20.08.2026) — 17 Szenarien
|
||
Alle 17 verifiziert (System-/Trading-Status via Loop, API `/control/trading-state` + `/monitoring/status` konsistent):
|
||
1. **Normalzustand:** `HEALTHY/ENABLED` — API, State-Modell, Loop identisch.
|
||
2. **PG-Ausfall:** `HALTED`, `POSTGRESQL_UNREACHABLE`+`PERSISTENCE_FAILURE`, Audit genau einmal.
|
||
3. **RabbitMQ-Ausfall:** `HALTED`, `RABBITMQ_UNREACHABLE`.
|
||
4. **Risk-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-07-risk-manager`+`NO_CONSUMER_risk.input`.
|
||
5. **Portfolio-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-08-portfolio-manager`.
|
||
6. **Execution-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-09-execution-service`.
|
||
7. **Analytics-Ausfall:** `DEGRADED` + `PAUSED` (Trading nicht vollständig kill).
|
||
8. **Notification-Ausfall:** `DEGRADED` + `PAUSED`.
|
||
9. **Market Data stale:** `HALTED`, `MARKET_DATA_STALE`; Recovery nach frischen Daten.
|
||
10. **Kritischer Consumer fehlt:** HALT-Severity korrekt (NO_CONSUMER auf kritischer Queue).
|
||
11. **Queue-Backlog:** Warning/HALT gemäß Schwelle (100/1000, Unit-getestet).
|
||
12. **Recovery:** Ursache erneut geprüft, kein Auto-RESUME solange Ursache, Status konsistent, kein Event-Spam.
|
||
13. **Manuelle Controls:** mit `CONTROL_API_ENABLED=false` sauber abgelehnt (`control_api_disabled`); Trading unangetastet.
|
||
14. **Idempotenz/Event-Flut:** stabiler Zustand → keine wiederholten SYSTEM_HALTED/SERVICE_UNHEALTHY (nur 1 Audit je Änderung).
|
||
15. **RabbitMQ-Reconnect:** automatisch (alle Recovery-Zyklen), keine Zombies, keine Eventverluste.
|
||
16. **Audit:** `control_event`/`system_health`/`service_health` vollständig + zeitlich nachvollziehbar.
|
||
17. **Security:** kein öffentlicher Port (PortBindings `{}`), kein Docker-Socket, keine Secrets im Compose/Logs.
|
||
|
||
## Bekannte Design-Hinweise (für separaten M15→M09-Vorschlag)
|
||
- **M03–M06 (Market-Data-Pipeline) sind in `CRITICAL_SERVICES`** klassifiziert. Ausfall eines einzelnen Daten-Moduls führt so zu `HALTED` (über `CRITICAL_SERVICE_DOWN`), nicht ausschließlich über `MARKET_DATA_STALE`. Das ist fachlich konservativ aber zu klären (nur Data-Pipeline vs. Execution-Pfad).
|
||
- **DEGRADED → `PAUSED`** (nicht `ENABLED`): bei Analytics/Notification-Ausfall wird Trading vorsichtig gestoppt, nicht vollständig killed. Design-Entscheidung, im Vorschlag benannt.
|
||
- **PG-Ausfall ⇒ `PERSISTENCE_FAILURE`**: ohne PG kann `control_event`-Audit nicht geschrieben werden (fachlich korrekt FAIL-CLOSED), aber Audit-Lücke im Fehlerfenster.
|
||
|
||
## Freigabe
|
||
- **Noch NICHT freigegeben.** Implementierung + E2E (17 Szenarien) grün. Freigabe ausstehend.
|
||
- M09-Anbindung **nicht** umgesetzt (Design-Vorschlag separat).
|
||
|
||
## Nächste Module
|
||
- **Modul-16 ff.** — noch Platzhalter (alpine), NICHT gestartet.
|
||
|
||
---
|
||
## Geändert
|
||
```
|
||
Geändert von: Rain Ocampo
|
||
Datum: 20.08.2026
|
||
Grund: Modul-15-Monitoring-Control dokumentiert — implementiert + E2E-verifiziert (17 Szenarien grün),
|
||
Rules/API/Events/Audit/Security dokumentiert, NICHT FREIGEGEBEN.
|
||
```
|