guyg1474andClaude Opus 5.5 a1beee3d85 Verlauf um Swap und Temperatur ergänzen, Netz p42 als externes Netz
/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>
2026-10-14 16:22:48 +02:00

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-slim plus etwa 15 KB Code.
  • Liest /proc und / 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.usedBytes ist der tatsächlich belegte Speicher, also ohne Cache, der jederzeit freigegeben werden kann. Das entspricht der Spalte „used“ von free.
  • disks[].percent wird wie bei df berechnet. freeBytes ist der für Benutzer verfügbare Platz.
  • network.rxBytesPerSec/txBytesPerSec summiert nur echte Schnittstellen. Docker-Bridges (docker0, br-*) stehen mit "virtual": true in der Liste, werden aber nicht mitgezählt, sonst zählt Container-Verkehr doppelt. Die veth*-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) und criticalCelsius kann null sein.
  • cpu.mhz ist der Durchschnitt aller Threads und kann bei VMs null sein.

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)
S
Description
Monitoring docker für p42.f-w
Readme
45 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 90.2%
Shell 5.5%
Dockerfile 4.3%