/api/history liefert zusätzlich swapPercent und tempMaxCelsius (für den 2-Tage-Verlauf im Dashboard). Das Docker-Netz p42 legt update-docker.sh bei Bedarf an, damit die Startreihenfolge mit P42-F.W. egal ist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.7 KiB
P42-S.M. – Server-Monitoring-Agent
Kleiner Docker-Dienst, der die Auslastung des Ubuntu-Servers misst und als JSON-API bereitstellt. P42-F.W. (Dashboard) liest die Werte aus und stellt sie dar.
Gemessen wird alle 5 Sekunden: CPU (gesamt, je Thread, iowait, Takt, Load), RAM, Swap, Platten (Belegung je Laufwerk, Lese-/Schreibrate), Netzwerk (Rate je Schnittstelle und Summe), Temperaturen, Prozesse, Uptime. Die letzte Stunde wird als Verlauf vorgehalten.
- Nur Python-Standardbibliothek, keine Pakete. Das Image ist
python:3.13-slimplus etwa 15 KB Code. - Liest
/procund/des Hosts nur lesend ein und läuft ohne Root, ohne Capabilities und mit schreibgeschütztem Dateisystem. - Kein Docker-Socket: der Container sieht keine anderen Container und kann nichts steuern.
Betrieb
Einrichten und Starten
/pfad/zu/docker ist ein Platzhalter – durch den Container-Ordner auf dem Server ersetzen.
git clone https://github.com/guyg1474/p42.s-m.git /pfad/zu/docker/p42-sm
cd /pfad/zu/docker/p42-sm
./deploy/update-docker.sh
Beim ersten Aufruf legt das Skript .env mit einem zufälligen Token an und gibt ihn aus. Diesen Token
an P42-F.W. weitergeben. Nach etwa 10 Sekunden zeigt docker compose ps den Zustand healthy.
Test auf dem Server:
curl -H "Authorization: Bearer $(grep ^SM_TOKEN= .env | cut -d= -f2)" http://127.0.0.1:9142/api/metrics
Hoch- und runterfahren, beobachten
docker compose stop # anhalten
docker compose start # wieder starten
docker compose restart # nach Änderungen an .env: besser "docker compose up -d"
docker compose down # Container entfernen (das Netz "p42" bleibt bestehen)
docker compose ps # Status inkl. healthy/unhealthy
docker compose logs -f # Log: nur Start, Fehler und abgelehnte Anfragen
docker stats p42-sm # Verbrauch des Agenten selbst (wenige MB RAM)
Updates: ./deploy/update-docker.sh holt per git pull den neuen Stand und baut den Container neu.
Einstellungen (.env)
| Variable | Standard | Bedeutung |
|---|---|---|
SM_TOKEN |
– (Pflicht) | Zugangs-Token der API |
SM_PORT |
9142 |
Port auf dem Server |
SM_BIND |
127.0.0.1 |
An welche Adresse der Port gebunden wird. Für Abfragen aus dem LAN die Server-IP, nie 0.0.0.0 |
SM_INTERVAL |
5 |
Messintervall in Sekunden |
SM_HISTORY |
720 |
Anzahl Verlaufspunkte (720 × 5 s = 1 h) |
SM_UID |
1000 |
Benutzer-ID des Containers, siehe unten |
Fehlt eine Platte in disks? Der Container muss die Ordner bis zum Einhängepunkt durchsuchen dürfen.
Liegt die Platte z. B. unter /home/<user>/<platte> (Rechte 750), muss SM_UID die UID dieses Benutzers
sein (id -u <user>). Netzlaufwerke (NFS/SMB) werden bewusst nicht gemessen.
Anbindung an P42-F.W.
Erreichbarkeit
A) Dashboard läuft als Container auf demselben Server (empfohlen): Beide hängen im Docker-Netz p42,
das die update-docker.sh beider Projekte bei Bedarf anlegt (Reihenfolge egal). Das Dashboard erreicht den
Dienst darin unter http://p42-sm:9142, ohne offenen Port nach außen. Im Dashboard unter Monitoring
→ „+ Server hinzufügen“ → Prüfung „Server-Auslastung (P42-S.M.)“, Adresse http://p42-sm:9142, Token aus .env.
B) Dashboard läuft auf einem anderen Rechner: In .env SM_BIND auf die LAN-IP des Servers setzen.
Die Adresse lautet dann http://<server-ip>:9142. Per Firewall sollte nur der Dashboard-Rechner zugreifen dürfen.
Abgefragt wird serverseitig aus dem Dashboard-Backend, nicht aus dem Browser. Token und Port bleiben so verborgen, deshalb gibt es kein CORS.
API
Alle Antworten sind JSON. Byte-Werte sind ganze Zahlen in Byte, Raten in Byte pro Sekunde, Prozente auf
eine Nachkommastelle gerundet, und Zeitstempel stehen in ISO 8601 mit Zeitzone. Sinnvolles Abfrageintervall:
alle 5–30 Sekunden; häufiger bringt nichts, weil nur alle intervalSeconds neu gemessen wird.
| Anfrage | Token | Antwort |
|---|---|---|
GET /api/health |
nein | 200 {"status":"ok"}, bzw. 503, solange noch keine Werte da sind oder die Messung hängt |
GET /api/metrics |
ja | aktueller Stand, siehe unten |
GET /api/history |
ja | Verlauf; ?minutes=N begrenzt auf die letzten N Minuten |
Token als Header: Authorization: Bearer <SM_TOKEN>. Fehlt er oder ist er falsch, kommt 401.
Bis die erste Messung vorliegt (etwa 1 s nach dem Start), kommt 503.
GET /api/metrics
{
"timestamp": "2026-10-14T14:59:01+02:00",
"intervalSeconds": 5,
"host": { "hostname": "server", "os": "Ubuntu 24.04.1 LTS", "kernel": "6.8.0-45-generic", "uptimeSeconds": 15943 },
"cpu": {
"model": "AMD Ryzen 7 5700X 8-Core Processor",
"threads": 16,
"percent": 6.2,
"iowaitPercent": 0.0,
"perCorePercent": [2.6, 2.0, 5.5, 21.0],
"mhz": 2894,
"load": { "1m": 0.81, "5m": 0.91, "15m": 0.8 }
},
"memory": { "totalBytes": 16682397696, "usedBytes": 6809317376, "availableBytes": 9873080320, "cachedBytes": 5789290496, "percent": 40.8 },
"swap": { "totalBytes": 536866816, "usedBytes": 0, "percent": 0.0 },
"disks": [
{ "mount": "/", "device": "/dev/sda2", "fs": "ext4", "totalBytes": 983040749568, "usedBytes": 883751505920, "freeBytes": 49278058496, "percent": 94.7 }
],
"diskIo": { "readBytesPerSec": 44951, "writeBytesPerSec": 73557 },
"network": {
"rxBytesPerSec": 10279,
"txBytesPerSec": 51353,
"interfaces": [
{ "interface": "eth0", "virtual": false, "rxBytesPerSec": 10279, "txBytesPerSec": 51353, "rxBytesTotal": 704405487, "txBytesTotal": 89327228 }
]
},
"temperatures": [
{ "sensor": "k10temp", "label": "Tctl", "celsius": 51.0, "criticalCelsius": null },
{ "sensor": "nvme", "label": "Composite", "celsius": 39.85, "criticalCelsius": 79.85 }
],
"processes": { "total": 429, "running": 1 }
}
Hinweise zu den Feldern:
memory.usedBytesist der tatsächlich belegte Speicher, also ohne Cache, der jederzeit freigegeben werden kann. Das entspricht der Spalte „used“ vonfree.disks[].percentwird wie beidfberechnet.freeBytesist der für Benutzer verfügbare Platz.network.rxBytesPerSec/txBytesPerSecsummiert nur echte Schnittstellen. Docker-Bridges (docker0,br-*) stehen mit"virtual": truein der Liste, werden aber nicht mitgezählt, sonst zählt Container-Verkehr doppelt. Dieveth*-Schnittstellen der einzelnen Container werden gar nicht gelistet.temperatures: welche Sensoren es gibt, hängt von der Hardware ab. Die Liste kann leer sein (z. B. bei VMs) undcriticalCelsiuskannnullsein.cpu.mhzist der Durchschnitt aller Threads und kann bei VMsnullsein.
GET /api/history?minutes=30
{
"intervalSeconds": 5,
"points": [
{ "timestamp": "2026-10-14T14:58:59+02:00", "cpuPercent": 5.0, "memoryPercent": 40.9, "swapPercent": 0.0,
"tempMaxCelsius": 51.0, "load1m": 0.81,
"rxBytesPerSec": 2769, "txBytesPerSec": 6111, "readBytesPerSec": 0, "writeBytesPerSec": 2757518 }
]
}
Älteste Punkte zuerst. Der Verlauf liegt nur im Speicher und beginnt nach einem Neustart des Containers von vorn. Für längere Zeiträume speichert das Dashboard die Werte selbst.
Lokal testen (ohne Docker)
HOST_PROC=/proc HOST_ROOT=/ SM_TOKEN=test PORT=9142 HOST=127.0.0.1 python3 -m app
Aufbau
app/collector.py liest /proc und /sys, berechnet Raten aus zwei Messungen
app/server.py HTTP-API, Token-Prüfung, Mess-Thread, Verlauf
app/__main__.py Start, sauberes Beenden bei "docker stop"
Dockerfile, docker-compose.yml
deploy/update-docker.sh Bauen/Aktualisieren, legt beim ersten Mal .env mit Token an
.env Token und Einstellungen (nicht ins Git)