Kalina b8246472f5 fix(rawlogs): Uebergabedateien lesbar in /tmp/rawlog-upload legen
Der Collector laeuft als root (Logs unter /home/users mit 0700), seine
Ausgabe ist damit 0600 fuer root. scp als normaler Nutzer kam nicht an
die Datei: 'stat ... Keine Berechtigung'. Jetzt werden Kopien mit 644
in /tmp/rawlog-upload/ abgelegt — von beiden Seiten erreichbar.
2026-09-14 17:32:08 +02:00
2026-09-14 01:25:55 +02:00

netbird-telegraf-installer

Ein Befehl, und eine Node liefert Metriken + Logs an die zentrale Observability auf dem imac. Der Ausgang läuft über den NetBird-Reverse-Proxy (imac-prometheus.x05.idx.wf, imac-loki.x05.idx.wf) mit Bearer-Token — dadurch funktioniert der Installer auch auf Maschinen, die kein NetBird-Peer sind (Büro-LAN, Kundenlaptop, CGNAT, VM ohne Client).

Installation — der sichere Weg

# Empfohlen: erst herunterladen, dann ausführen
curl -fsSL <raw>/install.sh -o /tmp/netbird-install.sh
sudo sh /tmp/netbird-install.sh --name <node> --token <TOKEN>

Warum nicht direkt curl … | sh? Bei einer Pipe ist stdin das Skript selbst. Jeder Unterbefehl, der stdin liest, kann es wegräumen — und der Installer ruft Paketmanager auf (apt-get), die genau das tun. Auf Ubuntu startet dabei needrestart:

Scanning processes...
Scanning linux images...

Danach setzt die Shell mitten im Quelltext fort statt im Skript — sichtbar an Ausgaben wie ; then, fi oder Kommentarzeilen. Der Lauf bricht ab, die Node ist halb konfiguriert. Auf Nodes mit bereits installiertem Telegraf passiert das nicht, weil dann kein Paketmanager läuft — dort funktioniert curl … | sh scheinbar problemlos.

Ab 1.4.0 schützt sich der Installer selbst: erkennt er eine echte Pipe, lädt er sich frisch von der Quelle, startet sich mit stdin </dev/null neu und meldet das:

[netbird-telegraf] Selbstschutz: Skript laeuft jetzt von /tmp/… (stdin frei).

Fehlen curl/wget auf einem frischen System, meldet er das ausdrücklich (HINWEIS: kein curl/wget fuer den Selbstschutz) — dann ist der Download-Weg oben die sichere Wahl.

Der Fehler ist reproduzierbar: x14 brach genau so ab. Der Installer lief bis Node=… Apache=1, dann kamen Scanning processes…-Zeilen, danach Quelltext-Ausgaben, und apache.conf fehlte am Ende.

Wenn der Lauf hängt

Der Installer darf nie unbegrenzt warten. Alle Netzwerkaufrufe haben Timeouts, und die ACL-Phase meldet sich vorher:

[netbird-telegraf] Log-Leserechte: pruefe/ergaenze ACL fuer 'telegraf' (kann etwas dauern) …

Diese Zeile dauert auf einem frischen System bis zu 3 Minuten — dort läuft apt-get update + apt-get install acl. Erscheint sie und danach nichts, nicht sofort abbrechen: bis 3 Minuten ist normal.

Aufruf Timeout Versuche
fetch (Confs/Scripts vom Repo) 30 s 3
apt-get update / install acl 180 s 1
InfluxData-Keyring 30 s 1
Telegraf-Binary (statisch) 120 s 1
Selbstschutz-Download 30 s 1

Vor 1.5.0 gab es diese Timeouts nicht — fetch() wartete unbegrenzt. Symptom: der Lauf bleibt nach der letzten Erfolgsmeldung stehen, ohne Fehler; man bricht mit ^C ab und weiß nicht, wo es hing. Real passiert auf x14.

Diagnose: „Logs kommen nicht an"

Ein Befehl zeigt, warum:

curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/install.sh \
  | sh -s -- --diagnose

Er prüft genau die Stellen, die schon still versagt haben (kein E! in keinem Log):

Prüfung Warum
Dienst-Benutzer + Gruppen Telegraf läuft als telegraf, nicht als root
Jede Datei aus der files-Liste Existenz, Größe, Lesbarkeit aus Sicht des Dienst-Benutzers
Parser-Lauf ohne Filter echte E!-Zeilen sichtbar machen
Offset-Zustand erklärt, warum eine bestehende Datei „leer" wirkt

Beispielausgabe:

-- Logpfade aus der aktiven Conf --
  OK           /var/log/apache2/keyhelp/access.log (75 Bytes)
  NICHT LESBAR /home/users/u1/logs/d1/access.log (Benutzer telegraf) ← URSACHE
  -> 3 Pfade, 2 lesbar, 0 leer, 1 nicht lesbar

Wichtig: initial_read_offset = "saved-or-end" heißt, dass beim ersten Lauf nur Neues gelesen wird. Bestehende Zeilen kommen erst mit neuen Zugriffen — für einen sofortigen Test eine Seite aufrufen und ein Intervall abwarten.

Der Konfig-Test prüft ALLE Dateien

Ein Detail, das schon eine Node lahmgelegt hat: Nie mit --input-filter prüfen.

# FALSCH — validiert nur die gefilterten Plugins:
telegraf --config … --config-directory … --test --input-filter cpu:mem

# RICHTIG — parst alle Dateien, genau wie der Dienststart:
telegraf --config … --config-directory … --test --test-wait 1

Mit --input-filter cpu:mem überspringt Telegraf alle übrigen Konfigdateien. Ein ungültiges Feld in telegraf.d/apache.conf lief damit durch, der Installer meldete „OK — Node liefert", und erst der Dienststart scheiterte danach mit:

Job for telegraf.service failed because the control process exited with error code.

Der Installer prüft deshalb ohne Filter und wertet nur echte Parser-Fehler (E! … loading config file) als Abbruch — nicht jede Warnung, denn ein fehlendes Input-Binary (nvidia-smi) zeigt sich erst beim Start, nicht beim Parsen.

Apache-Logs

Mit --role apache (oder automatisch, wenn apache2ctl/httpd gefunden wird) bindet der Installer Apache ein — auf zwei Wegen aus derselben Datei:

Ziel Was Wofür
Loki jede Zeile roh Fehlersuche im Einzelfall
Prometheus apache_access mit Tags resp_code, verb Request-Rate, Status-Klassen, Error-Rate — Panels und Alerts

Logpfade werden automatisch gesammelt

Der Installer sammelt alle passenden Muster — nicht nur das erste. Auf einem KeyHelp-Host laufen zwei Strukturen parallel, und die globale Standarddatei ist leer:

Panel Pfadmuster
KeyHelp /home/users/<user>/logs/<domain>/access.log (zweistufig, pro Domain)
KeyHelp /var/log/apache2/<vhost>/access.log (z. B. keyhelp/access.log)
Plesk /var/www/vhosts/<domain>/logs/access_log
cPanel /usr/local/apache/domlogs/<domain>
Klassisch /var/log/apache2/access.log, /var/log/httpd/access_log

Verifiziert auf x22: Die Requests liegen unter /home/users/classi/logs/classi.parts/access.log und /var/log/apache2/keyhelp/access.log, während /var/log/apache2/access.log 0 Bytes hat. Ein Input auf die leere Datei hätte still nichts geliefert — leere Standarddateien werden per -s-Test ausgeschlossen.

Alle gefundenen Pfade landen als TOML-Liste in der Conf:

files = ["/var/log/apache2/*/access.log", "/home/users/*/logs/*/access.log"]

Jede Serie trägt ein path-Tag, damit man in Grafana nach Datei filtert. Überschreiben mit --apache-log <pfad> (mehrere Pfade leerzeichengetrennt).

Zusätzlich wird die CustomLog-Direktive aus /etc/apache2 bzw. /etc/httpd ausgewertet — die verlässlichste Quelle, ${APACHE_LOG_DIR} wird aufgelöst.

Zeitstempel-Kollisionen: der stille Totalausfall

Symptom. Ein Node liefert gar nichts mehr — nicht nur die Apache-Metriken, sondern auch system_uptime, CPU, Disk. Andere Nodes laufen normal.

Erkennungsmerkmal. Im Prometheus-Log wiederholt sich derselbe eingefrorene Timestamp alle ~15 s:

duplicate sample for timestamp 1789343466232; overrides not allowed:
existing 43930, new value 51608
series="{__name__=\"apache_access_resp_bytes\", path=\"…/pixtasia.de/access.log\",
         resp_code=\"200\", verb=\"GET\"}"

Derselbe Wert über Minuten heißt: ein vergifteter Batch sitzt im FIFO-Kopf und wird endlos wiederholt — nicht ein neuer Fehler pro Intervall.

Ursache. Telegraf schreibt pro Sammelzyklus einen Batch, dessen Metriken alle denselben Zeitstempel tragen. Remote Write kennt nur Millisekunden. Treffen zwei Requests derselben Serie (path + resp_code + verb) in dieselbe Millisekunde, lehnt Prometheus den ganzen Batch ab. Telegrafs Puffer ist FIFO: der Batch bleibt am Kopf stehen und blockiert jede weitere Serie — deshalb fehlt am Ende die ganze Node.

Lösung: Aggregation statt Zeitstempel-Trick. Ein Zeitstempel-Trick kann das nicht beheben — feiner als Millisekunden geht Remote Write nicht. Ein Aggregator fasst alle Metriken einer Serie pro Intervall zu einem Punkt zusammen; damit ist eine Kollision strukturell unmöglich, unabhängig von der Last:

[[aggregators.basicstats]]
  namepass = ["apache_access"]
  period = "60s"
  drop_original = true          # zwingend — sonst sind die Einzelwerte zurück
  stats = ["count", "sum", "mean"]

Gemessen an 2000 echten x22-Zeilen:

Punkte Kollisionen
ohne Aggregator 2000 Risiko pro Zelle
mit Aggregator 4 0

Die Requests werden dabei vollständig gezählt (1997 von 2000 — Differenz durch das Zeitfenster).

Entstehende Metriken (Telegraf stellt den Feldnamen voran):

Metrik Bedeutung
apache_access_resp_bytes_count Requests je Gruppe — das brauchen die Panels
apache_access_resp_bytes_sum übertragene Bytes je Gruppe
apache_access_resp_bytes_mean mittlere Antwortgröße

resp_code und verb bleiben Tags und damit Gruppenschlüssel — dafür sorgt processors.converter oben im File (das eingebaute Muster liefert sie als Tags, ein eigenes Muster als Felder; ohne den Converter brächen Filter nach resp_code).

Diagnose.

# derselbe Timestamp mehrfach = vergifteter Batch
docker logs observability-prometheus 2>&1 \
  | grep "duplicate sample" | grep -oE "timestamp [0-9]+" | sort | uniq -c | tail

Leserechte: der stille Killer

Auf x22 waren 64 von 84 Logpfaden für telegraf unlesbar — inputs.tail meldet das nicht, es liefert einfach nichts, und der Node sieht „aktiv" aus. Zwei verschiedene Rechte-Welten:

Ort Rechte Was hilft
/var/log/apache2/… root:adm 0640 usermod -aG adm telegraf
/home/users/<user>/logs/… <user>:<user> 0640 adm hilft nicht — ACL nötig

Der Installer setzt dafür ACLs (setfacl -R -m u:telegraf:rX plus eine Default-ACL, damit neue Domain-Ordner das Recht erben). Das acl-Paket wird bei Bedarf nachinstalliert. Der Dienst bleibt im eingeschränkten Konto — kein root nötig.

Danach folgt ein Gegentest: Der Installer liest eine echte Logdatei als Dienst-Benutzer. Meldet er

[netbird-telegraf] Gegentest: 'telegraf' kann Apache-Logs lesen (…)

ist die Kette nachweislich in Ordnung.

Glob-Muster statt Einzelpfade

Die Conf enthält Muster, nicht die expandierten Dateinamen:

files = ["/var/log/apache2/*/access.log", "/home/users/*/logs/*/access.log"]

Damit wird eine neu angelegte Domain sofort erfasst, ohne dass der Installer erneut laufen muss. (Der Installer schaltet beim Rendern set -f ein — sonst würde die Shell die Muster selbst auflösen und feste Pfade in die Conf schreiben.)

/etc/default/telegraf wird gesourct — Werte müssen gequotet sein

Diese Datei wird mit . /etc/default/telegraf eingelesen (vom Installer und von der Telegraf-Unit). Ein ungequoteter Wert mit Leerzeichen bricht das Sourcing ab — bei mehreren Logpfaden versucht die Shell, den zweiten Pfad als Befehl auszuführen:

sh: /etc/default/telegraf: /home/users/classi/logs/classi.parts/access.log: Permission denied

Der Installer schreibt APACHE_LOG='…' deshalb mit einfachen Anführungszeichen. Zusätzlich liest er die Datei über load_defaults ein, das syntaktisch kaputte Dateien sichert und überschreibt statt abzubrechen — eine Node lässt sich so auch dann reparieren, wenn ihre Defaults-Datei bereits defekt ist.

Zwei Fallstricke

1. from_beginning existiert bei inputs.tail nicht. Ein solcher Block lässt den Dienst beim Start hart scheitern — systemctl start schlägt fehl und alle Metriken fallen aus, nicht nur die Apache-Logs:

E! loading config file /etc/telegraf/telegraf.d/apache.conf failed:
   plugin inputs.tail: line 2: configuration specified the fields ["from_beginning"],
   but they were not used ... does not exist in this version

Richtig ist initial_read_offset = "saved-or-end" (Default) — damit wird beim ersten Lauf nicht die komplette Historie ausgeschüttet. Für einen Test gegen eine vorgefüllte Datei "beginning" setzen.

2. Die Grok-Muster heißen COMMON_LOG_FORMAT und COMBINED_LOG_FORMAT — nicht HTTPD_COMMON_LOG/HTTPD_COMBINED_LOG_FORMAT. Die HTTPD_*-Namen aus anderen Grok-Sammlungen kennt Telegraf nicht:

E! [inputs.tail] Creating parser: no pattern found for %{HTTPD_COMBINED_LOG_FORMAT}

Die Reihenfolge in grok_patterns ist spezifisch → allgemein, damit die VHost-Varianten (example.com:80 ...) vor der einfachen Form greifen.

Response-Time

Common und Combined Log Format enthalten keine Antwortzeit. Für resp_time in den Metriken muss das LogFormat in Apache erweitert werden:

LogFormat "%h %l %u %t \"%r\" %>s %b \"%D\" \"%{User-agent}i\"" combined_time
CustomLog ${APACHE_LOG_DIR}/access.log combined_time

%D = Dauer in Mikrosekunden. Danach im Grok-Muster als Feld ergänzen. Ohne diese Änderung sind alle anderen Werte trotzdem verfügbar — nur die Antwortzeit fehlt.

Auto-Updater und Force-Updater

Der Installer hält sich auf den Nodes selbst aktuell.

Prüfen

sh install.sh --version   # installierte + verfügbare Version
sh install.sh --check     # Exit 0 = aktuell, Exit 10 = Update verfügbar

Beides braucht keinen Token und kein root — gut für Monitoring-Checks.

Auto-Updater (systemd-Timer)

sh install.sh --name <node> --token <T> --install-update-timer daily
sh install.sh --name <node> --token <T> --install-update-timer 30min
sh install.sh --name <node> --token <T> --install-update-timer weekly

Legt telegraf-installer-update.timer an (Default daily 04:30, ±15 min Streuung). Der Lauf ist billig: Es werden nur VERSION und der SHA des Installers verglichen. Erst bei Abweichung lädt er den Installer und ruft sich mit --update-only auf. Log: /var/log/telegraf-installer-update.log.

--update-only fasst nur telegraf.d/-Konfs und Hilfsscripts an: kein Paket-Update, /etc/default/telegraf (und damit der Token) bleibt unberührt, kein Neustart wenn sich nichts geändert hat.

Warum Version und Hash: Die VERSION erkennt neue Auslieferungen, der SHA auch Korrekturen ohne Versionssprung (z. B. ein Fix an journal-tail.sh). Ohne den Hash bliebe eine Node mit fehlerhaftem Script stehen.

Force-Updater

sh install.sh --force-update --name <node> --token <T>

Schreibt Konfs und Scripts immer neu, sichert sie vorher nach /var/backups/telegraf-<zeitstempel>/ und startet den Dienst neu. Schlägt der Konfig-Test fehl, wird automatisch zurückgerollt. Idempotent — mehrfaches Ausführen ist folgenlos. Das Werkzeug nach einem kaputten Update oder wenn jemand Dateien von Hand verändert hat.

Entfernen: sh install.sh --remove-update-timer

Windows

.\install.ps1 -Name <node> -Token <T> -Check          # Versionsvergleich
.\install.ps1 -Name <node> -Token <T> -InstallUpdateTask          # taeglich
.\install.ps1 -Name <node> -Token <T> -InstallUpdateTask -UpdateInterval hourly
.\install.ps1 -Name <node> -Token <T> -ForceUpdate
.\install.ps1 -RemoveUpdateTask

Die Aufgabe \NetBirdTelegraf\Update läuft mit denselben Semantiken (Version + Hash, nur bei Änderung). Log: %ProgramData%\netbird-telegraf\update.log.

Marker auf der Node

Datei Inhalt
/etc/telegraf/CONFIG_VERSION zuletzt installierte Repo-Version
/etc/telegraf/INSTALLER_SHA SHA256 des Installers zum Installationszeitpunkt
/etc/telegraf/REPO_RAW genutzte Repo-Quelle (GitHub-Mirror, Testserver)

REPO_RAW ist per Umgebungsvariable überschreibbar — nützlich für Tests, Mirrors oder einen eigenen Fork.

Docker-Nodes (kein journalctl im Image)

Läuft Telegraf als Container (offizielles telegraf-Image), ist der Journal-Weg nicht möglich: das Image ist Debian ohne systemd und hat kein journalctl; /hostfs ist zudem read-only. Dafür gibt es scripts/syslog-tail.sh — es liest die Syslog-Dateien des Hosts unter /hostfs/var/log/.

Drei Fallstricke, die alle still scheitern (kein E!, das Panel bleibt einfach leer):

  1. --config-directory muss im Command stehen. Der Standard-CMD im Image ist nur telegraf und liest ausschließlich /etc/telegraf/telegraf.conf. Alles in telegraf.d/ wird ohne diesen Schalter ignoriert. In docker-compose.yml:
    command: --config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d
    
  2. Gruppe adm ergänzen (group_add: ["4"]). Der Entrypoint des Images setzt Telegraf per setpriv auf uid 999 — unabhängig von user: im Compose. /var/log/syslog gehört Gruppe adm (Modus 640), als uid 999 also nicht lesbar. Ohne adm gibt das Script nichts aus.
  3. State-Verzeichnis muss schreibbar sein (SYSLOG_STATE_DIR). Voreinstellung ist /tmp/telegraf-syslog. Zeigt es auf ein nicht schreibbares Verzeichnis (z. B. /var/lib/... als uid 999), kann der Offset nie gespeichert werden — das Script landet bei jedem Lauf im Erstanlauf-Zweig und liefert dauerhaft nichts. Seit der aktuellen Version warnt es in dem Fall auf stderr.

Output-Filter mitziehen: outputs.loki filtert per namepass. Enthält der Block namepass = ["docker_log"], wird das journal-Measurement verworfen und die System-Logs kommen nie an — auch wenn der Input läuft. journal ergänzen; im Remote-Write-Output gehört es in namedrop (Logs haben in Prometheus nichts zu suchen).

Kein Zeitstempel im erzeugten Line Protocol. Die Syslog-Zeit ist lokal (+02:00), mawk (Standard im Image) kennt keine Zeitzonen-Offsets — selbst berechnete Werte lagen 2 h in der Zukunft und Loki wies jeden Push ab: has timestamp too new. Ohne Zeitstempel stempelt Telegraf beim Einsammeln.

System-Logs (PVE und Nicht-Docker-Hosts)

Bis Version dieses Abschnitts lieferten nur Docker-Container Logs nach Loki (inputs.docker_log) — PVE-Knoten und alle Hosts ohne Docker also gar nichts. Jetzt werden zusätzlich journald-Logs übertragen.

Wie: scripts/journal-tail.sh liest journalctl -o json inkrementell und gibt Influx Line Protocol aus (inputs.exec). Der Cursor steckt in jeder Journal-Zeile (__CURSOR) und wird in /var/lib/telegraf-journal/cursor gespeichert — keine Duplikate, kein Verlust bei Neustart oder Rotation.

Warum nicht inputs.journal: Das Plugin existiert in den offiziellen Telegraf-Builds nicht (Docker-Image und InfluxData-Debian-Paket 1.40 geprüft; es braucht einen Build mit systemd-Headern).

Warum nicht /var/log/syslog: Auf PVE (Debian ohne rsyslog) existiert die Datei nicht. ls /var/log/ zeigt dort journal/, pve/, pveproxy/, pve-firewall.log, ceph/, corosync/ — aber kein syslog.

Feintuning in /etc/default/telegraf:

Variable Default Bedeutung
JOURNAL_PRIORITY warning warning, err, info, debug
JOURNAL_MAX_LINES 500 Obergrenze pro Lauf (Flutschutz)
JOURNAL_BUDGET 15 Sekunden, die journalctl lesen darf
JOURNAL_SINCE — nur beim ersten Lauf, z. B. -1h

Standardmäßig werden Warnungen und schlimmer übertragen. Für Debugging JOURNAL_PRIORITY=info setzen und Telegraf neu starten.

Labels in Loki: unit (z. B. pvedaemon.service), ident, priority, jhost (Journal-Hostname), dazu host/node von Telegraf. Das jhost-Label heißt bewusst nicht host — dieser Name gehört Telegrafs global_tag, sonst filtert das Dashboard nach dem falschen Knoten.

Der Token — zuerst lesen

Jeder Aufruf braucht --token <TOKEN>. Ohne Token bricht das Script mit FEHLER: Kein Token: --token <Bearer-Token> angeben ab.

Den Token legt man im NetBird-Dashboard an: https://x05.idx.wf → Reverse Proxy → Service imac-prometheus → Authentication → Header → Preset „Bearer Token". Für imac-loki denselben Wert eintragen (oder einen zweiten, dann aber getrennt halten).

Token nie in dieses Repo schreiben — er landet ausschließlich in /etc/default/telegraf (Modus 0600) auf der Zielmaschine.

Linux — Debian, Ubuntu, Proxmox VE, RHEL/Rocky/Alma/Fedora, Alpine, sonstige

curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/install.sh \
  | sudo sh -s -- --name <node-name> --token <BEARER-TOKEN>
Option Bedeutung
--name <n> Node-Name im Dashboard (Default: hostname -s)
--token <t> Pflicht. Bearer-Token des Reverse-Proxys
--collector <ip> Direktweg innerhalb des NetBird statt Reverse Proxy (z. B. 100.83.134.46); braucht dann keinen Token-Zwang, aber die Node muss Peer sein
--role proxmox|apache|docker|none Zusatz-Profil; Default auto erkennt Proxmox, Docker und Apache selbst
--apache-log <pfad> Access-Log (Default /var/log/apache2/access.log)
--uninstall alles entfernen

Root-Rechte: Das Script braucht sie (Dienst, /etc/telegraf). Wo du schon root bist — PVE-Knoten, Hetzner-/Root-Server wie x17, LXC-Container, minimale Debian-Installationen — kein sudo: dort ist es oft gar nicht installiert und der erste Befehl bricht mit sudo: command not found ab. Auf normalen Desktops/Servern mit eigenem Benutzer (Ubuntu-Desktop, ROG): | sudo sh -s -- ….

Prüfen statt raten: id -u — kommt 0, ohne sudo. Das Script prüft es selbst und bricht mit klarer Meldung ab, wenn es nicht root ist.

Ablauf: Paketquelle von InfluxData (apt/dnf/apk) oder statisches Binary (amd64/arm64/armv7) → /etc/telegraf/telegraf.conf + telegraf.d/ aus diesem Repo → /etc/default/telegraf mit NODE_NAME, PROM_URL, LOKI_URL, TELEGRAF_TOKEN → systemd-Dienst telegraf, enabled, Konfig-Test vor dem Start.

Direktweg (Node ist NetBird-Peer)

curl -fsSL …/install.sh | sudo sh -s -- --name <node> --token <TOKEN> --collector 100.83.134.46

Proxmox VE

Kein API-Token nötig. Der Installer nutzt pvesh direkt (Telegraf läuft für diese Rolle als root — das Drop-in 10-proxmox-root.conf setzt das). Damit entfällt die gesamte Token-/ACL-Thematik.

Warum nicht inputs.proxmox? Das Plugin fragt als Erstes /nodes/<node>/dns ab und braucht dort Sys.Audit. Schlägt das fehl, liefert es komplett nichts — ohne Logmeldung. Mehrere ACL-Versuche (PVEAuditor auf /, auf /nodes, auf /nodes/<node>, eigene Rolle mit Sys.Audit) blieben auf PVE 8 mit 403 erfolglos.

Dann auf jedem PVE-Knoten (auf dem Host selbst, nicht in einer VM). Auf PVE (und auf Root-Servern wie x17) gibt es kein sudo — du bist dort bereits root, also ohne sudo:

curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/install.sh \
  | sh -s -- --name <PVE-KNOTENNAME> --token <BEARER-TOKEN> --role proxmox

Auf Debian/Ubuntu/Normalnutzer dagegen | sudo sh -s -- … verwenden. Das Script prüft id -u selbst und bricht mit klarer Meldung ab, wenn es nicht root ist.

Der Installer erkennt den PVE-Knoten selbst (pvesh get /nodes) und legt das systemd-Drop-in 10-proxmox-root.conf an — Telegraf läuft für diese Rolle als root, weil pvesh root verlangt.

Liefert pro Gast: proxmox_vm_cpu_usage_percent, _cpu_cores, _mem_bytes, _mem_max_bytes, _mem_usage_percent, _swap_bytes, _disk_bytes, _disk_max_bytes, _diskread/_diskwrite_bytes_total, _net_in/_net_out_bytes_total, _uptime_seconds, _running — je mit vmid, name, node, type, status. Plus die normalen Host-Metriken des Knotens.

Wenn nach der Installation keine VM-Metriken kommen: systemctl cat telegraf | grep User muss User=root zeigen. Vorher prüfen: pvesh get /cluster/resources --output-format json muss als root Daten liefern. Als Nicht-root gibt pvesh nur ipcc_send_rec[...] failed aus — ohne Fehler im Log.

Apache

Wird automatisch erkannt (apache2ctl/httpd). Access-Log geht roh nach Loki. Für Request-Metriken inputs.apache in telegraf/apache.conf einschalten (braucht mod_status).

Windows — PowerShell als Administrator

Set-ExecutionPolicy Bypass -Scope Process -Force
& ([scriptblock]::Create((iwr -useb https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/install.ps1))) -Name ROG -Token <BEARER-TOKEN>

Installiert nach C:\Program Files\Telegraf, Dienst telegraf; liefert CPU/RAM/Disk/Net/Dienste + Eventlog (Warnung/Fehler). -Uninstall entfernt alles.

GPU-Metriken

Hersteller Erkennung Metriken
NVIDIA nvidia-smi -L funktioniert Auslastung, VRAM, Temperatur, Leistung, Takte, P-State
AMD rocm-smi vorhanden Auslastung, Temperatur, Leistung, VRAM (via scripts/gpu-rocm.sh)
Intel /dev/dri/renderD128 Hinweis im Log — intel_gpu_top wird nicht automatisch erfasst

AMD liefert sieben Metriken (Auslastung, VRAM genutzt/gesamt, Temperatur edge/junction/memory, Leistung). NVIDIA entsprechend über inputs.nvidia_smi.

Im Dashboard: Row GPU (Auslastung, Speicher, Temperatur, Leistung). Leere Panels heißen: keine GPU-Node im Mesh.

Beim Aktualisieren des AMD-Scripts (falls rocm-smi seine Spalten ändert) beide Dateien neu holen — Script und Conf, und data_format muss prometheus bleiben:

sudo curl -fsSL …/scripts/gpu-rocm.sh | sudo tee /etc/telegraf/gpu-rocm.sh >/dev/null
sudo chmod +x /etc/telegraf/gpu-rocm.sh
sudo sh -c 'cat > /etc/telegraf/telegraf.d/gpu.conf <<EOF
[[inputs.exec]]
  interval = "30s"
  timeout = "20s"
  data_format = "prometheus"
  commands = ["/etc/telegraf/gpu-rocm.sh"]
EOF'
sudo systemctl restart telegraf

Im Docker-Betrieb muss der Treiber im Container verfügbar sein (nvidia-container-toolkit bzw. --device /dev/dri); als systemd-Dienst ist das automatisch der Fall.

Prüfen

TOKEN=<BEARER-TOKEN>
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://imac-prometheus.x05.idx.wf/api/v1/query?query=system_uptime{node=\"<node-name>\"}"
journalctl -u telegraf -f

Im Grafana-Dashboard „NetBird Observability — Overview“ erscheint die Node automatisch im Node-Filter. Grafana selbst: http://100.83.134.46:3000 (nur im NetBird, Login admin).

Docker-Host ohne root

Wer kein sudo hat, aber in der Gruppe docker ist: Container-Variante aus netbird-observability/stacks/telegraf-node (privates Repo). Dort ist network_mode: host gesetzt — ohne das sieht Telegraf nur lo+eth0 statt der echten Interfaces.

Error-Logs (seit 1.3.0)

Apache schreibt zwei Logarten mit verschiedenen Formaten:

Datei Format Ziel
access.log IP ident auth [HTTPDATE] "VERB REQ" CODE BYTES Loki (roh) und Prometheus (Metriken)
error.log [Mon Sep 14 00:10:01.123456 2026] [core:error] [pid 9] AH00124: … Loki (roh), Tag logtype=error

Bis 1.2.0 wurden nur Access-Logs gesammelt. Nachgemessen: 31 logfile-Labels in Loki, 0 davon error.log — auf einem Panel-Host mit PHP/CMS fehlten damit genau die Fehler, für die man ins Monitoring schaut.

Error-Logs laufen über einen eigenen Input, nicht durch den Access-Grok: das Format passt nicht, und der Aggregator würde sie sonst in leere Gruppen einordnen. Sie tragen logtype=error und logfile=<pfad> — in Grafana nach beiden filterbar:

{node="<host>", logtype="error"}
{node="<host>", logtype="error"} | logfmt | line_format "{{.message}}" |~ "PHP Fatal"

Drei stille Fallen, die dabei behoben wurden

Alle drei hätten dazu geführt, dass die Logs gesammelt und dann verworfen werden — ohne Fehlermeldung:

  1. processors.rename hatte namepass = ["journal"]. Für apache_error hieß das Feld deshalb value statt message — jede Query mit {{.message}} hätte es nicht gefunden.
  2. outputs.loki hatte namepass = ["docker_log", "apache_log", "journal"] — ohne apache_error. Der Input sammelt ein, der Output verwirft.
  3. outputs.prometheus fehlte apache_error in namedrop — sonst wandern Textzeilen als Zeitreihen in die TSDB.

Alte Logs archivieren (Historie nachträglich auswertbar machen)

inputs.tail liest mit initial_read_offset = "saved-or-end" nur Neues — die vorhandene Historie bleibt unausgewertet. Dafür gibt es ein eigenes Skript, das dieselben Zeilen parst und als Prometheus-Blöcke ablegt:

# Auf der Node: erst messen (ändert nichts)
curl -fsSL <raw>/scripts/x22-log-archive.sh -o /root/archiv.sh
bash /root/archiv.sh                # Bestandsaufnahme

# dann archivieren
bash /root/archiv.sh --backfill     # erzeugt apache-archive.openmetrics + tar.gz

Das Skript prüft seine eigene Ausgabe vor der Übergabe: Zeitraum, Einheit (Sekunden) und ein Zukunfts-Check — Punkte nach „jetzt" werden gemeldet, weil sie später beim Import die Zeitreferenz verschieben würden.

Eingespielt wird nur in die Archiv-Instanz, nie in die Produktions-TSDB:

scp /root/apache-archive/apache-archive.openmetrics mm@<imac>:/tmp/
ssh mm@<imac> 'sudo /usr/local/bin/prom-archive-import.sh --file /tmp/apache-archive.openmetrics'

Geparst werden Access- und Error-Logs, inklusive rotierter .log.1/.log.N.gz. Was entsteht: apache_archive_requests_total (je Domain/Status/Methode/Stunde), _response_bytes_total, _unique_clients, _errors_total (je Level), _log_lines_total. Als Datasource in Grafana einhängen (http://archive-prometheus:9090) — Live und Archiv sind dann getrennt abfragbar.

Archiv-Import: Schutzschichten

Alte Logs lassen sich als Prometheus-Blöcke archivieren (scripts/apache-log-archive.py). Der Import in eine laufende TSDB ist heikel — drei Fehler sind möglich, alle still:

Fehler Folge Schutz
Zeitstempel in Millisekunden statt Sekunden Blöcke im Jahr 58.662, Prometheus verwirft sie als „too far into the future" Einheiten-Wächter im Skript (Abbruch ab Jahr 2100)
Block reicht in die Zukunft Prometheus verschiebt seine Zeitreferenz, alle Live-Samples gelten als „too old" — alle Nodes fallen aus Zukunft-Prüfung in prom-archive-import.sh (Abbruch, Toleranz 5 min)
rm -rf <tsdb>/01M* beim Aufräumen Glob trifft alle Blöcke → gesamte Historie gelöscht kein Glob; nur exakte Block-IDs; Pflicht-Snapshot vorher

Deshalb: Archiv-Blöcke nie in die Produktions-TSDB, sondern in die getrennte Instanz (stacks/archive-prometheus, Port 9091). Sie nimmt keine Live-Daten an und kann die Produktion nicht beeinflussen.

Sicherheit

  • Der Proxy verlangt Bearer-Token; das Header wird vor dem Weiterleiten entfernt und liegt damit nicht bei Prometheus/Loki.
  • Telegraf läuft als Benutzer telegraf (Docker-Socket read-only via Gruppe docker, Apache-Logs via Gruppe adm).
  • Ausnahme --role proxmox: systemd-Drop-in setzt User=root, weil pvesh root benötigt. Nur auf PVE-Hosts, wo du ohnehin root bist.
  • Dieses Repo ist öffentlich und enthält keine Secrets.
S
Description
One-liner Telegraf installer for NetBird observability nodes (Linux/Proxmox/Windows) → Prometheus Remote Write + Loki
Readme MIT
986 KiB
0 Stars 1 Watchers 0 Forks
Languages
Shell 57.2%
Python 37.5%
PowerShell 5.3%