Markus Michalski FAQ

Watchdog: Technische Details

Architektur, Container, Ablauf von Planung bis Alarm, Zeitwerte, Datenmodell und Agenten-API von Watchdog. Stack: Symfony, FrankenPHP, SQLite, Messenger und Scheduler.

English Version

Zurück zur Übersicht

Watchdog: Technische Details

Stack

Baustein Einsatz
PHP 8.4 und Symfony 8.1 Anwendung
FrankenPHP (Caddy) Webserver, auf Live im Worker-Modus
SQLite über Doctrine ORM Datenbank und Warteschlange
Symfony Messenger Asynchrone Ausführung von Checks und E-Mail-Versand
Symfony Scheduler Minütliche Fälligkeitsprüfung und tägliche Bereinigung
Twig und Tailwind CSS Oberfläche

Architektur

graph LR
    U["Admin im Browser"] -->|"HTTP Basic Auth"| APP["app: Oberfläche und API"]
    APP --> DB["SQLite: data.db"]
    SCH["scheduler"] -->|"jede Minute"| Q["Warteschlange in SQLite"]
    Q --> W["worker"]
    W -->|"HTTP, TCP, DNS, Docker"| T["Überwachte Ziele"]
    W -->|"SMTP"| M["Mail-Server"]
    W --> DB
    AG["agent"] -->|"Bearer-Token"| APP

Container

Dienst Befehl Aufgabe
app FrankenPHP Oberfläche und Agenten-API
worker php bin/console messenger:consume async notifications --time-limit=3600 --memory-limit=128M Führt Dashboard-Checks aus und versendet E-Mails
scheduler php bin/console messenger:consume scheduler_checks --time-limit=3600 --memory-limit=128M --no-interaction Prüft jede Minute, welche Checks fällig sind, und räumt täglich auf
agent php bin/console watchdog:agent:run Führt Agenten-Checks aus und meldet die Ergebnisse
mailpit axllent/mailpit Mail-Fänger, nur lokal und auf Stage

Alle Container außer agent und mailpit teilen sich das Volume mit /app/var und damit die SQLite-Datei. Ein einzelner Worker arbeitet die Nachrichten nacheinander ab. Checks mit langen Zeitlimits verzögern daher andere Checks.

Ablauf

sequenceDiagram
    participant S as scheduler
    participant Q as Warteschlange
    participant W as worker
    participant D as SQLite
    participant M as SMTP
    S->>Q: RunSiteChecks je fälligem Check
    Q->>W: Nachricht
    W->>D: Ergebnis speichern, Status vergleichen
    W->>Q: MailNotification bei Statuswechsel
    Q->>W: Nachricht
    W->>M: eine E-Mail je Kontakt
  1. Der Scheduler prüft jede Minute alle aktiven Dashboard-Checks aktiver Clients. Ein Check ist fällig, wenn noch kein Ergebnis existiert oder seit dem letzten Ergebnis mindestens Intervall - 30 Sekunden vergangen sind. Bei einer täglichen Uhrzeit ist er fällig, sobald die Uhrzeit erreicht ist und das letzte Ergebnis älter als diese Uhrzeit ist.
  2. Für jeden fälligen Check landet eine Nachricht RunSiteChecksMessage in der Warteschlange async.
  3. Der Worker führt den Check aus, speichert das Ergebnis und vergleicht es mit dem bisherigen Status (alert_states).
  4. Bei einem Wechsel legt er eine MailNotificationMessage in die Warteschlange notifications, die derselbe Worker verarbeitet.
  5. Agenten-Ergebnisse gelangen über die API direkt in Schritt 3.

Zeitwerte

Wert Dauer
Fälligkeitsprüfung der Dashboard-Checks jede Minute
Bereinigung alter Ergebnisse täglich 03:00 Uhr
Tick des Agenten 30 Sekunden
Abruf der Check-Liste durch den Agenten alle 300 Sekunden
Agent gilt als online Kontakt in den letzten 10 Minuten
Wiederholungen fehlgeschlagener Nachrichten 3, mit doppeltem Abstand
Zeitlimit der Container-Prozesse 3600 Sekunden oder 128 MB, dann Neustart durch Docker

Datenmodell

Tabelle Inhalt
clients Clients mit Name und Aktiv-Kennzeichen
contacts Kontakte mit Name und eindeutiger E-Mail-Adresse
client_contacts Zuordnung Client und Kontakt
client_urls URLs je Client mit Label und optionalen BasicAuth-Zugangsdaten
site_checks Checks: Typ, Konfiguration (JSON), Intervall, tägliche Uhrzeit, Aufbewahrung, Runner, Agent, Aktiv-Kennzeichen, Run-now-Kennzeichen
check_results Ergebnisse: Status, HTTP-Code, Antwortzeit, Meldung, Zeitpunkt
alert_states Aktueller Status je Check, Zeitpunkt des letzten Statuswechsels, Zähler aufeinanderfolgender Warnungen und Fehler
agents Agenten mit Name, Token-Hash und letztem Kontakt
messenger_messages Warteschlange und Transport failed

Agenten-API

Alle Pfade liegen unter /api/v1/agent und verlangen Authorization: Bearer <Token>. Antworten sind JSON. Ohne gültiges Token antwortet Watchdog mit 401 und {"error": "Unauthorized"}, bei Überschreitung des Limits von 60 Anfragen pro Minute und IP mit 429.

Methode Pfad Zweck
GET /config Check-Liste des Agenten. Aktualisiert den Zeitpunkt des letzten Kontakts
GET /run-now Checks mit gesetztem Run-now-Kennzeichen, auch für inaktive Clients. Setzt das Kennzeichen zurück
POST /results Ergebnisse eines Ticks entgegennehmen

Antwort von /config:

{
  "agent": {"id": 1, "name": "host-server"},
  "checks": [
    {"id": 7, "type": "disk_space", "config": {"path": "/"}, "check_interval_minutes": 5, "run_at_time": null, "run_now": false}
  ]
}

Anfrage an /results:

{
  "results": [
    {"site_check_id": 7, "status": "ok", "message": "42% used (58 GB free of 100 GB)", "response_time_ms": 12, "checked_at": "2026-01-01T12:00:00+01:00"}
  ]
}
Antwort Bedeutung
200 {"accepted": <n>, "skipped": [{"site_check_id": <id>, "reason": "..."}]}
422 Ungültiges JSON, fehlendes Feld results oder mehr als 500 Ergebnisse
401 Token fehlt oder ist ungültig

Ein Ergebnis wird übersprungen (skipped), wenn site_check_id fehlt, der Check nicht existiert oder nicht diesem Agenten zugewiesen ist, oder der Status keiner der Werte ok, warn, fail, unknown ist.

Qualitätssicherung

Werkzeug Aufruf
PHPUnit make test
PHPStan Level 9 make stan
PHP-CS-Fixer make cs (Prüfung), make fix (Korrektur)
Lint und Smoke-Test make lint, make smoke

Die CI führt Code-Style, PHPStan und die Tests aus und baut das Agenten-Image.