Files
netbird-telegraf-installer/README.md
T

22 KiB

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).

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.

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.

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.