Behebt eine Reihe zusammenhaengender Fehler im Update-Weg, die zusammen verhindert haben, fuer mehr als eine Plattform auszuliefern - und die im Fehlerfall halb aktualisierte Installationen hinterliessen. Server - Migration 009: Spalte platform samt neuem Unique-Key. Zuvor verdraengte das zuletzt veroeffentlichte Paket alle anderen Plattformen derselben Version, weil ON DUPLICATE KEY auf (slug, version, channel) griff. Ein Linux-System zog sich damit das Windows-Paket. - Aufloesungsregel: je Version das plattformgenaue Paket, sonst das plattformunabhaengige. Ein Client ohne Plattformangabe sieht ausschliesslich 'any' - lieber kein Update als das falsche. - manifest_json wird endlich befuellt; die Spalte blieb bisher immer leer, wodurch die API nie der Rueckfall sein konnte, als der sie gedacht war. - Releases werden serverseitig mit RSA-SHA256 signiert, neuer Endpunkt /api/updateservice/v1/pubkey. Bewusst kein HMAC: der Pruefende laeuft auf fremden Systemen und darf den Signierschluessel nicht besitzen. Packager - Bricht ab, statt die Versionshistorie zu verlieren. Schlug das Lesen der bestehenden latest.json fehl, ersetzte ein leeres catch die komplette Historie durch einen einzigen Eintrag - ohne jede Meldung. - Echte Glob-Muster. Zuvor trafen "logs/**" und "scratch/**" aus der mitgelieferten Beispielkonfiguration nie zu. - preservePatterns: Konfigurationsvorlagen werden ausgeliefert, ersetzen am Ziel aber keine vorhandene Datei. Eine settings.json mit Zugangsdaten ueberschrieb bisher beim Update die Konfiguration jedes Zielsystems. - Warnt vor Dateien, die nach Zugangsdaten aussehen und auf keiner Liste stehen. - Prueft --version gegen die Hauptassembly. Eine Abweichung fuehrte zu einer Endlosschleife: Clients aktualisieren, melden weiter die alte Version, halten das Release erneut fuer neu. - --platform mit Ableitung aus dem Publish-Pfad. Agent - Anwenden mit Plan, Backup und vollstaendigem Rollback. Die Stelle war als "Atomic Replace with Backup" kommentiert und war eine Kopierschleife. - Verwaiste Dateien werden entfernt, aber nur solche aus dem Manifest der Vorversion. Was nicht aus einem Release stammt, bleibt liegen. - Das laufende Agent-Binary wird zur Seite gelegt statt ueberschrieben. - API-Rueckfall in FetchManifestAsync; bisher nur im SDK vorhanden, weshalb die Anwendung "Update verfuegbar" und der Agent "kein Release" sagen konnte. - Installierte Version aus --current-version oder manifest.json statt des Textes "Unbekannt", der als 0 gelesen wurde und jede Version neuer erscheinen liess. Reparatur funktioniert damit auch ohne manifest.json. - Setzt das Ausfuehrungsbit fuer Linux-Pakete, die unter Windows gebaut wurden. SDK - ResolveAgentPath() liefert den plattformrichtigen Namen; ein fest verdrahtetes "update-agent.exe" wird unter Linux nie gefunden. - LaunchUpdateAgent uebergibt jetzt --restart (wurde nie uebergeben, die Anwendung blieb nach dem Update zu), --wait-for-pid (kein Wettlauf mehr mit dem Herunterfahren) und --platform. Enthaelt ausserdem die bislang nicht committete Arbeit an Watchdog, Lizenz- Client und cli/tick.php samt Migration 008; die betroffenen Dateien liessen sich nicht getrennt stagen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
491 lines
17 KiB
Markdown
491 lines
17 KiB
Markdown
# Watchdog-Integration
|
||
|
||
Überwachung von Anwendungen, Diensten und Servern über Heartbeats.
|
||
|
||
> **Stand:** Version 2.0 — vollständig überarbeitet. Wer eine ältere Integration
|
||
> betreibt, findet die Änderungen in Abschnitt 9.
|
||
|
||
---
|
||
|
||
## 1. Wie der Zustand ermittelt wird
|
||
|
||
Eine Anwendung meldet sich in festen Abständen. Bleibt die Meldung aus, stuft
|
||
der **Evaluator** den Monitor herab:
|
||
|
||
| Zeit seit dem letzten Heartbeat | Zustand |
|
||
|---|---|
|
||
| innerhalb des Intervalls | wie gemeldet (`up` / `warning`) |
|
||
| mehr als das **Doppelte** | `warning` |
|
||
| mehr als das **Vierfache** | `down` |
|
||
|
||
Bei `interval: 60` heißt das: nach 2 Minuten auffällig, nach 4 Minuten
|
||
ausgefallen.
|
||
|
||
> **Wichtig:** Der Evaluator muss per Cron laufen. Ohne ihn ändert sich der
|
||
> Zustand ausschließlich beim Eintreffen eines Heartbeats — ein abgestürzter
|
||
> Dienst bliebe dauerhaft grün.
|
||
>
|
||
> ```bash
|
||
> * * * * * /usr/bin/php /pfad/zum/deploymentcenter/cli/tick.php --quiet
|
||
> ```
|
||
>
|
||
> Fehlt der Job, zeigt das WebUI oben einen Warnhinweis. Details und die
|
||
> HTTP-Variante: **[§11](#11-der-evaluator-tick)**.
|
||
|
||
---
|
||
|
||
## 2. Token besorgen
|
||
|
||
**Nicht** das Beispiel-Token aus älteren Fassungen dieser Anleitung verwenden —
|
||
es ist ein Demo-Wert und an eine einzelne Source gebunden.
|
||
|
||
Zwei Wege:
|
||
|
||
**a) Zentrales Token** (empfohlen für neue Integrationen)
|
||
WebUI → **Token-Verwaltung → Master-Token erstellen**, Recht *🛡️ Watchdog
|
||
Heartbeat*. Nicht an eine Source gebunden, funktioniert für beliebig viele
|
||
Dienste.
|
||
|
||
**b) Agent-Token je Monitor**
|
||
WebUI → **WatchDog → Agent-Tokens**. An eine Source gebunden; meldet der Dienst
|
||
unter einem anderen Namen, wird er abgewiesen. Beim Anlegen eines Monitors
|
||
entsteht automatisch eines.
|
||
|
||
Token nie im Quelltext ablegen — Umgebungsvariable oder Konfigurationsdatei.
|
||
|
||
---
|
||
|
||
## 3. Heartbeat senden
|
||
|
||
```
|
||
POST https://dc.mhdf.de/api/watchdog/v1/ping
|
||
Authorization: Bearer <TOKEN> (oder X-Agent-Token: <TOKEN>)
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"source": "polytrader-worker",
|
||
"instance": "default",
|
||
"type": "heartbeat",
|
||
"status": "ok",
|
||
"interval": 60,
|
||
"message": "Verarbeite Warteschlange",
|
||
"group": "Applications",
|
||
"os": ".NET 8 Service",
|
||
"version": "1.4.3",
|
||
|
||
"checks": { "db": { "ok": true },
|
||
"market_feed": { "ok": false, "message": "Letzter Tick vor 14 min" } },
|
||
"metrics": { "cpu": 18, "ram": 42, "queue_depth": 23 }
|
||
}
|
||
```
|
||
|
||
| Feld | Pflicht | Bedeutung |
|
||
|---|---|---|
|
||
| `source` | ja | Eindeutiger Name des Dienstes oder Hosts |
|
||
| `instance` | nein | Mehrere Instanzen desselben Dienstes (Vorgabe `default`) |
|
||
| `type` | nein | `heartbeat`, `host`, `hypervisor_node`, `guest` |
|
||
| `status` | nein | siehe unten (Vorgabe `ok`) |
|
||
| `interval` | nein | Erwarteter Abstand in Sekunden (Vorgabe 60) |
|
||
| `message` | nein | Kurztext, erscheint im Dashboard |
|
||
| `group` | nein | Gruppierung im Dashboard |
|
||
| `os` | nein | Plattform, steuert auch die Icon-Erkennung |
|
||
| `version` | nein | Version der laufenden Anwendung (Alias: `app_version`) |
|
||
| `checks` | nein | Selbst ermittelter Gesundheitszustand, siehe 4. |
|
||
| `metrics` | nein | Numerische Werte, siehe 5. |
|
||
|
||
### Welche Version läuft dort?
|
||
|
||
`version` erscheint als eigene Spalte in der Monitorliste und kommt im
|
||
Heartbeat-Antwortobjekt als `app_version` zurück. Damit ist auf einen Blick
|
||
sichtbar, ob ein Ausfall zeitlich zu einem Rollout passt — „Monitor X ist seit
|
||
dem Rollout von 1.4.3 unten".
|
||
|
||
Das Feld ist optional und **überschreibt einen früheren Wert nicht mit `null`**:
|
||
ein Agent, der es nicht mitschickt, löscht die zuletzt gemeldete Version nicht.
|
||
Bestehende Agenten laufen also unverändert weiter.
|
||
|
||
```csharp
|
||
// Am einfachsten aus der vom Build erzeugten Klasse, siehe
|
||
// UPDATESERVICE_INTEGRATION_GUIDE §2B
|
||
version = BuildInfo.Version
|
||
```
|
||
|
||
> Der Fehler-Stream führt `build`, der Bugtracker `build_version`, die
|
||
> Aktivierungsliste des Lizenzmoduls `app_version` — nur der Watchdog konnte
|
||
> bis 2.1 nicht sagen, welche Version tatsächlich läuft.
|
||
|
||
### Zulässige Werte für `status`
|
||
|
||
| Wert | Zustand | Wirkung |
|
||
|---|---|---|
|
||
| `ok` | `up` | Normalbetrieb |
|
||
| `warning` | `warning` | Auffällig, aber arbeitsfähig |
|
||
| `error` | `down` | Störung |
|
||
| `stopped` | `stopped` | **Bewusst beendet** — der Evaluator meldet keinen Ausfall |
|
||
| `maintenance` | `maintenance` | **Wartung** — von der Bewertung ausgenommen |
|
||
|
||
`stopped` und `maintenance` sind der saubere Weg, ein geplantes Herunterfahren
|
||
mitzuteilen. Ohne sie erzeugte jeder ordentliche Shutdown wenige Minuten später
|
||
einen Fehlalarm.
|
||
|
||
---
|
||
|
||
## 4. Gesundheitszustand mitsenden
|
||
|
||
Ein Heartbeat beweist nur, dass ein Thread läuft — nicht, dass die Anwendung
|
||
ihre Arbeit tut. Der klassische Fall: Der Timer meldet brav `ok`, während der
|
||
Datenfeed seit einer Stunde tot ist.
|
||
|
||
Deshalb kann die Anwendung ihren Zustand selbst beurteilen und mitschicken:
|
||
|
||
```json
|
||
"checks": {
|
||
"db": { "ok": true },
|
||
"market_feed": { "ok": false, "message": "Letzter Tick vor 14 min" },
|
||
"queue": { "ok": true, "value": 23 }
|
||
}
|
||
```
|
||
|
||
Das Deploymentcenter **interpretiert die Namen nicht** — es liest nur `ok` und
|
||
`message`. Was „gesund" bedeutet, entscheidet jede Anwendung selbst. Es gibt
|
||
also keine zentral gepflegten Kennzahlen, an die sich alle Projekte anpassen
|
||
müssten.
|
||
|
||
Schlägt eine Prüfung fehl, wird ein als `ok` gemeldeter Heartbeat automatisch
|
||
auf `warning` herabgestuft; die Antwort nennt die betroffenen Prüfungen:
|
||
|
||
```json
|
||
{ "status": "success",
|
||
"monitor": { "state": "warning", "failing_checks": ["market_feed"] } }
|
||
```
|
||
|
||
Kurzform ohne Zusatzangaben ist ebenfalls erlaubt:
|
||
`"checks": { "db": true, "market_feed": false }`
|
||
|
||
> **Warum kein Abruf durch den Server?** Damit auf euren Maschinen keine Ports
|
||
> geöffnet werden müssen. Der Weg ist ausgehend.
|
||
|
||
---
|
||
|
||
## 5. Metriken und Verlauf
|
||
|
||
Numerische Werte aus `metrics` werden mit Zeitstempel gespeichert
|
||
(Aufbewahrung 14 Tage). Verschachtelte Angaben werden flach abgelegt:
|
||
`{"cpu":{"load":1.2}}` wird zu `cpu.load`.
|
||
|
||
```bash
|
||
# Welche Metriken liefert dieser Monitor?
|
||
curl "https://dc.mhdf.de/api/watchdog/v1/metrics?source=polytrader-worker" \
|
||
-H "Authorization: Bearer $DC_TOKEN"
|
||
|
||
# Verlauf, auf 15-Minuten-Fenster verdichtet
|
||
curl "https://dc.mhdf.de/api/watchdog/v1/metrics?source=polytrader-worker&metric=queue_depth&hours=24" \
|
||
-H "Authorization: Bearer $DC_TOKEN"
|
||
```
|
||
|
||
Die Antwort enthält zusätzlich `deviation`: den Vergleich des aktuellen Werts
|
||
mit dem Sieben-Tage-Durchschnitt **desselben** Monitors. Damit lassen sich
|
||
Auffälligkeiten erkennen, ohne für jedes Projekt Schwellwerte zu pflegen.
|
||
|
||
---
|
||
|
||
## 6. Hierarchie und Alarmunterdrückung
|
||
|
||
Ist bei einem Monitor eine **übergeordnete Entität** gesetzt und fällt diese
|
||
aus, werden Alarme für die untergeordneten Monitore unterdrückt. Ihr Zustand
|
||
bleibt im Dashboard sichtbar.
|
||
|
||
Ohne das erzeugt ein ausgefallener Hypervisor mit zwölf VMs dreizehn Meldungen
|
||
für ein Problem.
|
||
|
||
Gepflegt wird das im WebUI unter **WatchDog → System-Hierarchie** im Feld
|
||
*Übergeordnete Entität*. Es funktioniert über mehrere Ebenen
|
||
(Rack → Host → Anwendung).
|
||
|
||
> Die Zuordnung erfolgt über `parent_source`, **nicht** über `group`. Die Gruppe
|
||
> dient nur der Anzeige.
|
||
|
||
---
|
||
|
||
## 7. Beispiele
|
||
|
||
Alle Beispiele lesen das Token aus der Umgebung.
|
||
|
||
### C# — Hintergrunddienst
|
||
|
||
```csharp
|
||
using System;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
public sealed class WatchdogReporter
|
||
{
|
||
private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromSeconds(10) };
|
||
|
||
private readonly string _url = $"{Environment.GetEnvironmentVariable("DC_URL") ?? "https://dc.mhdf.de"}/api/watchdog/v1/ping";
|
||
private readonly string _token = Environment.GetEnvironmentVariable("DC_TOKEN") ?? "";
|
||
private readonly string _source = Environment.GetEnvironmentVariable("DC_SOURCE") ?? Environment.MachineName;
|
||
|
||
public async Task SendAsync(string status, string message, object? checks = null,
|
||
object? metrics = null, CancellationToken ct = default)
|
||
{
|
||
var payload = new
|
||
{
|
||
source = _source,
|
||
status, // ok | warning | error | stopped | maintenance
|
||
interval = 60,
|
||
message,
|
||
os = Environment.OSVersion.ToString(),
|
||
version = BuildInfo.Version, // erscheint in der Monitorliste
|
||
checks,
|
||
metrics
|
||
};
|
||
|
||
var request = new HttpRequestMessage(HttpMethod.Post, _url)
|
||
{
|
||
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
|
||
};
|
||
request.Headers.Add("Authorization", $"Bearer {_token}");
|
||
|
||
try
|
||
{
|
||
await Http.SendAsync(request, ct);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// Ein nicht erreichbarer Monitoring-Server darf die Anwendung
|
||
// niemals stoppen.
|
||
Console.Error.WriteLine($"[Watchdog] Heartbeat fehlgeschlagen: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Einbindung als `BackgroundService`:
|
||
|
||
```csharp
|
||
public class HeartbeatService : BackgroundService
|
||
{
|
||
private readonly WatchdogReporter _reporter = new();
|
||
private readonly IMarketFeed _feed;
|
||
private readonly IJobQueue _queue;
|
||
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||
{
|
||
while (!stoppingToken.IsCancellationRequested)
|
||
{
|
||
bool feedOk = _feed.LastTickAge < TimeSpan.FromMinutes(5);
|
||
|
||
await _reporter.SendAsync(
|
||
status: feedOk ? "ok" : "warning",
|
||
message: feedOk ? "Betrieb normal" : "Datenfeed veraltet",
|
||
checks: new
|
||
{
|
||
market_feed = new { ok = feedOk, message = $"Letzter Tick vor {_feed.LastTickAge.TotalMinutes:F0} min" },
|
||
queue = new { ok = _queue.Depth < 1000, value = _queue.Depth }
|
||
},
|
||
metrics: new { queue_depth = _queue.Depth },
|
||
ct: stoppingToken);
|
||
|
||
await Task.Delay(TimeSpan.FromSeconds(60), stoppingToken);
|
||
}
|
||
|
||
// Sauberes Beenden ankündigen, damit kein Fehlalarm entsteht.
|
||
await _reporter.SendAsync("stopped", "Dienst planmäßig beendet", ct: CancellationToken.None);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Python
|
||
|
||
```python
|
||
import os
|
||
import requests
|
||
|
||
URL = os.environ.get("DC_URL", "https://dc.mhdf.de") + "/api/watchdog/v1/ping"
|
||
TOKEN = os.environ["DC_TOKEN"]
|
||
SOURCE = os.environ.get("DC_SOURCE", os.uname().nodename)
|
||
|
||
|
||
def heartbeat(status="ok", message="", checks=None, metrics=None):
|
||
payload = {
|
||
"source": SOURCE,
|
||
"status": status,
|
||
"interval": 60,
|
||
"message": message,
|
||
}
|
||
if checks:
|
||
payload["checks"] = checks
|
||
if metrics:
|
||
payload["metrics"] = metrics
|
||
|
||
try:
|
||
requests.post(URL, json=payload,
|
||
headers={"Authorization": f"Bearer {TOKEN}"},
|
||
timeout=10)
|
||
except Exception as exc:
|
||
# Monitoring darf die Anwendung nie zum Stillstand bringen
|
||
print(f"[Watchdog] Heartbeat fehlgeschlagen: {exc}")
|
||
|
||
|
||
heartbeat(
|
||
status="ok",
|
||
message="Import abgeschlossen",
|
||
checks={"db": {"ok": True}, "feed": {"ok": True}},
|
||
metrics={"rows_imported": 4213},
|
||
)
|
||
```
|
||
|
||
### Bash / Cron
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
: "${DC_TOKEN:?DC_TOKEN ist nicht gesetzt}"
|
||
URL="${DC_URL:-https://dc.mhdf.de}/api/watchdog/v1/ping"
|
||
SOURCE="${DC_SOURCE:-$(hostname)}"
|
||
|
||
DISK=$(df --output=pcent / | tail -1 | tr -dc '0-9')
|
||
|
||
curl -fsS -X POST "$URL" \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $DC_TOKEN" \
|
||
-d "{\"source\":\"$SOURCE\",\"status\":\"ok\",\"interval\":3600,
|
||
\"message\":\"Nächtliches Backup abgeschlossen\",
|
||
\"checks\":{\"disk\":{\"ok\":$([ "$DISK" -lt 90 ] && echo true || echo false),\"message\":\"${DISK}% belegt\"}},
|
||
\"metrics\":{\"disk_percent\":$DISK}}" \
|
||
> /dev/null
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Ereignisse protokollieren
|
||
|
||
Für einmalige Vorkommnisse statt zyklischer Meldungen:
|
||
|
||
```bash
|
||
curl -X POST https://dc.mhdf.de/api/watchdog/v1/event \
|
||
-H "Authorization: Bearer $DC_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"source":"polytrader-worker","kind":"recovered",
|
||
"severity":"info","message":"Verbindung zum Feed wiederhergestellt"}'
|
||
```
|
||
|
||
Zulässige `kind`-Werte: `started`, `stopped_graceful`, `crash_suspected`,
|
||
`hard_error`, `recovered`, `warning_raised`, `warning_cleared`,
|
||
`maintenance_start`, `maintenance_end`, `watchdog_started`.
|
||
`severity`: `info`, `warning`, `alarm`.
|
||
|
||
---
|
||
|
||
## 9. Was sich gegenüber Version 1 geändert hat
|
||
|
||
| Thema | Vorher | Jetzt |
|
||
|---|---|---|
|
||
| Zustandsbewertung | fand nicht statt — ein ausgefallenes System blieb `up` | Evaluator per Cron, 2× / 4× Intervall |
|
||
| `expected_interval_sec` | gespeichert, nie ausgewertet | bestimmt die Schwellen |
|
||
| `is_muted`, `suppress_until_utc` | gespeichert, nie ausgewertet | werden beachtet |
|
||
| Token | nur `wd_live_`-Tokens | zusätzlich zentrale Tokens mit `watchdog:ping` |
|
||
| Anwendungszustand | nicht übermittelbar | Feld `checks` |
|
||
| Metriken | nur der letzte Wert | Verlauf über 14 Tage, abrufbar |
|
||
| Geplantes Beenden | nicht möglich | `status: "stopped"` bzw. `"maintenance"` |
|
||
| Alarme bei Ausfall eines Hosts | eine Meldung je Kind | Kinder werden unterdrückt |
|
||
| Antwortformat | uneinheitlich | `{"status":"success",…}` bzw. `{"status":"error","error":{"code":…}}` |
|
||
| Laufende Version | nicht übermittelbar | Feld `version` (seit 2.1) |
|
||
| OpenAPI-Beschreibung | `/event`, `/events`, `/status` fehlten, ebenso `group` und `type` am Ping | vollständig |
|
||
|
||
Bestehende Agenten mit `wd_live_`-Token und einfachem `ok`-Ping laufen
|
||
unverändert weiter — die neuen Felder sind alle optional.
|
||
|
||
---
|
||
|
||
## 10. Empfehlungen
|
||
|
||
1. **Intervall realistisch wählen.** Ein Backup-Job, der stündlich läuft, meldet
|
||
`interval: 3600` — nicht 60.
|
||
2. **Netzwerkfehler immer abfangen.** Der Ausfall des Monitoring-Servers darf
|
||
die überwachte Anwendung nicht beeinträchtigen.
|
||
3. **Beim Beenden `stopped` senden.** Sonst folgt wenige Minuten später ein
|
||
Fehlalarm.
|
||
4. **`checks` nutzen.** Ein Heartbeat sagt nur, dass ein Thread läuft.
|
||
5. **Hierarchie pflegen**, wenn Dienste auf gemeinsamen Hosts laufen — sonst
|
||
bringt ein Hostausfall eine Alarmlawine.
|
||
6. **`version` mitschicken.** Ohne sie lässt sich ein Ausfall nicht mit einem
|
||
Rollout in Verbindung bringen.
|
||
|
||
---
|
||
|
||
## 11. Der Evaluator-Tick
|
||
|
||
Der Evaluator ist die Komponente, die Monitore anhand ihres erwarteten
|
||
Intervalls herabstuft. Er läuft **nicht** von selbst — ohne einen Cron-Eintrag
|
||
sind die Zustände im Dashboard wertlos.
|
||
|
||
Er erledigt in einem Durchlauf:
|
||
|
||
- Monitore anhand `expected_interval_sec` auf `warning` bzw. `down` stufen
|
||
- Zustandswechsel im Ereignisprotokoll festhalten und Webhooks auslösen
|
||
- Alarme für Kinder eines ausgefallenen Hosts unterdrücken
|
||
- abgelaufene Bugtracker-Leases freigeben
|
||
- stündlich den Metrik-Verlauf abräumen
|
||
|
||
### Interner Aufruf (empfohlen)
|
||
|
||
```bash
|
||
* * * * * /usr/bin/php /pfad/zum/deploymentcenter/cli/tick.php --quiet
|
||
```
|
||
|
||
Entspricht dem früheren `watchdog/cli/tick.php`. Kein Schlüssel im Crontab,
|
||
keine Abhängigkeit von Webserver, TLS oder DNS — und damit auch dann
|
||
lauffähig, wenn der Webserver gerade das Problem ist.
|
||
|
||
| Option | Wirkung |
|
||
|---|---|
|
||
| `-q`, `--quiet` | Ausgabe nur bei Zustandswechseln und Fehlern (für Cron) |
|
||
| `-j`, `--json` | Ergebnis maschinenlesbar |
|
||
| `--no-lock` | Sperre gegen überlappende Läufe übergehen (nur zur Fehlersuche) |
|
||
| `-h`, `--help` | Hilfe |
|
||
|
||
| Rückgabewert | Bedeutung |
|
||
|---|---|
|
||
| `0` | Lauf erfolgreich |
|
||
| `1` | Fehler — Meldung auf `stderr`, Einzelheiten im Log unter `var/log/` |
|
||
| `2` | Übersprungen, es lief bereits ein Tick |
|
||
|
||
Das Skript sperrt sich über `var/watchdog-tick.lock` selbst. Braucht ein Lauf
|
||
länger als eine Minute, überspringt der nächste Cron-Aufruf — sonst würden
|
||
zwei Evaluatoren dieselben Zustandswechsel doppelt melden.
|
||
|
||
> Der Aufruf ist auf `PHP_SAPI === 'cli'` beschränkt und `cli/` ist zusätzlich
|
||
> per `.htaccess` gesperrt. Über den Webserver ist das Skript also nicht
|
||
> erreichbar — sonst ließe sich ein Evaluationslauf ohne jede Authentifizierung
|
||
> auslösen, während der HTTP-Endpunkt dafür bewusst den Shared Key verlangt.
|
||
|
||
### Über die Schnittstelle
|
||
|
||
Sinnvoll, wenn der Cron auf einer anderen Maschine läuft:
|
||
|
||
```bash
|
||
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
|
||
```
|
||
|
||
Beide Wege rufen denselben Code auf und schreiben denselben Lauf-Vermerk in
|
||
`watchdog_cron_jobs`. **Nur einen von beiden einrichten** — zwei parallele
|
||
Zeitpläne bringen keinen Gewinn, nur die Gefahr überlappender Läufe.
|
||
|
||
### Prüfen, ob er läuft
|
||
|
||
```bash
|
||
php cli/tick.php --json
|
||
curl https://dc.mhdf.de/api/health -H "Authorization: Bearer <SHARED_KEY>"
|
||
```
|
||
|
||
`/api/health` meldet unter `checks.evaluator` den letzten Lauf. Im WebUI zeigt
|
||
das Watchdog-Modul denselben Zustand als Abzeichen **AKTIV** / **INAKTIV**.
|