archibald hat kein sudo und kein linger, aber Docker-Gruppe. Muster wie
bei der llama-swap-Telemetrie auf dem ROG: ein Nutzer-Cron fuehrt den
Exporteur aus, Telegraf laeuft als Container daneben und pusht per
Remote Write zum Collector im Mesh.
- scripts/archivebox-telemetrie.py: liest die ArchiveBox-Logs und die
SQLite-DB IM Container (Host-User mm darf index.sqlite3 nicht oeffnen,
Container-User 911 schon) und schreibt eine Prometheus-Exposition.
Kein node-Label -- das setzt Telegraf per global_tags.
- scripts/install-archivebox-telemetrie.sh: installiert Exporteur,
Cron-Eintrag (jede Minute) und den Container, mit --dry-run.
- stacks/telegraf-container/: telegraf 1.39, network_mode host,
inputs.file (data_format prometheus), outputs.prometheus_remote_write.
Warum: llama-swap laeuft als Nutzer-Unit. Der System-Telegraf liest das
Systemjournal mit priority>=warning und liest damit keine einzige
llama-swap-INFO-Zeile - Loki hatte fuer den ROG null Zeilen. Ausserdem kann
die System-Instanz auf einem Knoten ohne sudo nicht erweitert werden.
- scripts/llamaswap-telemetrie.py: Token/Anfragen je Modell aus llama-swaps
Statistik-API plus llama.cpp /metrics je laufendem Profil (Ports sind
dynamisch, deshalb kein statischer Scrape)
- scripts/llamaswap-logs.py: Nutzer-Journal (llama-swap, kalina-backends)
nach Loki, gleiche Zeilenform wie journal-tail.sh
- telegraf/llamaswap-user.conf + .service: Zugangsdaten per
EnvironmentFile=/etc/default/telegraf, keine Token-Kopie
- scripts/install-llamaswap-user.sh: idempotente Installation mit --dry-run
und Konfigurationspruefung
Zweites String-Feld derselben Klasse wie container_id: inputs.docker liefert
health_status="healthy". Telegraf macht daraus docker_container_health_health_status
und Prometheus antwortet 'bad sample value "healthy"' -> der ganze Flush faellt aus,
auch die Container-Statistiken, die damit nichts zu tun haben.
GEMESSEN auf kalina nach dem ersten Fix: genau EINE 'series were dropped'-Zeile
mit 862 Serien im haengenden Batch, last recorded error health_status. Nach dem
Zusatz: 0 E!, 0 dropped, docker_container_* wieder da (kalina 501, imac 1126
Serien).
Feldliste diesmal empirisch erzeugt statt geraten: telegraf --test des
docker-Inputs ohne Prozessoren, alle Felder mit Anfuehrungszeichen gesammelt --
es sind genau ZWEI (container_id, health_status).
VERSION 1.9.3 -> 1.9.4.
Ein permanenter 400 (vergifteter Batch) blieb am FIFO-Kopf und wurde endlos
wiederholt; die gepufferten Samples alterten dabei bis ueber die Akzeptanz-
grenze des Empfaengers (head_min_time ~2x Blockdauer) und waren danach
unzustellbar — der Knoten verstummte dauerhaft.
GEMESSEN 09.10.2026: 9 von 15 Nodes stumm, Sample-Versatz 185-1071 min
(konstant ueber 72 h), 1680 Ablehnungen/h, danach 'Metric buffer overflow'
= endgueltiger Verlust. Mit der Option wird der Batch verworfen statt
wiederholt ('Received non-retryable status 400. Metrics are lost.') und der
Knoten erholt sich beim naechsten Flush selbst. 5xx/408/429 bleiben retrybar.
Zweiter Fix, gleiche Ursache: der vorhandene processors.override traf nur
'docker_log' und liess docker_container_blkio_container_id (String) durch —
der 400 warf die ganze docker_container_*-Familie weg. Neuer Block
namepass=['docker','docker_container*'] mit fieldexclude der *_id-Felder.
VERSION 1.9.2 -> 1.9.3 (loest den Auto-Updater aus).
Die 10-s-Timeouts waren kuerzer als die gemessene Zustellzeit (11-19 s durch
den NetBird-Reverse-Proxy auf x05) -> jeder Batch brach ab. Gemessen: 702
abgelehnte Requests in 20 min, Metriken fehlten still.
Neu: scripts/docker-events-loki.py + Wrapper + systemd-Unit. Docker-Events
(Neustart/OOM/Stop) gehen nach Loki, Spool-Datei + Cursor statt RAM-Puffer.
GEMESSEN: x22 erzeugt 13,3 Log-Zeilen/s. Die alte Reserve von 20.000
reichte damit nur ~25 min — bei einem Ausfall von einer Stunde waeren
Logs verloren gegangen, ohne dass es auffiele.
200.000 = ~4 h Reserve, belegt nur waehrend eines Ausfalls (Telegraf
puffert nichts, solange der Push klappt). Ein Ueberlauf wird geloggt
('[outputs.loki] Metric buffer overflow'), also nicht still.
Kein Dienstneustart noetig ausser der regulaere Konfigurationslauf.
Die .gz-Dateien INNERHALB eines Backfill-Tarballs sind nochmals
gzip-komprimiert; tarfile entpackt nur die aeussere Ebene. Wer den
Member-Inhalt direkt dekodiert, liest gzip-Magic als Text und verliert
jede Zeile dieser Dateien — real 68.975 von 404.825 (17 %).
- .gz-Member werden ein zweites Mal entpackt, die Zahl wird gemeldet
- --limit-lines greift jetzt auch im Tarball-Zweig
- Beispiele unlesbarer Zeilen zeigen den Dateinamen
Am Ziel gemessen: /tmp ist ein tmpfs mit 15 GB. Eine 10-GB-Rohdatei plus
Sortier-Temp liegt damit im Arbeitsspeicher und bringt Loki/den Rechner
in Bedraengnis. Der Importer prueft jetzt vorher den freien Platz und
bricht frueh ab; die Uebergabe geht nach /data/tmp (863 GB frei).
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.
Der Prometheus-Archiv-Import verdichtet auf Stunde/Domain/Status und
verwirft Referrer, User-Agent und Client-IP. Fragen nach der Herkunft
liessen sich damit nicht rueckwirkend beantworten.
- raw-log-collect.py: liest access+error (inkl. .gz, rotierte Dateien),
schreibt ts_ns/labels/rohzeile und meldet Zeilen ohne Zeitstempel
statt sie still zu verwerfen. Error-Logs ohne Zeitzone (Apache-
Standard) werden korrekt nach UTC verschoben.
- raw-log-backfill.sh: Wrapper mit Messmodus; sammelt auf dem Host,
importiert selbst NICHTS.
Der x22-Backfill meldete "Zeitraum: 03.12.2024 .. 14.09.2026" und liest sich
damit wie zwei Jahre Historie. Tatsaechlich enthielt die Quelle EINEN einzelnen
Punkt vom 03.12.2024 (eine alte Logzeile) und sonst 30 Tage:
belegte Stunden: 721 von 15.601 im Zeitraum (4.6 % Abdeckung)
groesste Luecke: 620 Tage 03.12.2024 21:00 -> 15.08.2026 22:00
min..max allein sagt nichts ueber die Substanz. Die Selbstpruefung nennt jetzt
zusaetzlich die Zahl der belegten Stunden gegen die Spanne, die prozentuale
Abdeckung und — bei unter 50 % — die drei groessten Luecken mit Datum, plus den
Hinweis auf fehlende/rotierte Logs als typische Ursache.
Verifiziert gegen drei Faelle: (A) 1 alter Punkt + 30 Tage -> 4.6 %, Luecke
benannt; (B) 15 Tage durchgehend -> 100 %, kein Alarm; (C) 29 Tage mit 20-h-
Luecke -> 97.3 %, kein Alarm (echte kleine Luecken sind normal).
x22-log-backfill.sh heisst jetzt keyhelp-log-backfill.sh und leitet NODE aus
hostname -f ab, wenn es nicht gesetzt ist. Damit ist derselbe Ablauf fuer x14
nutzbar, ohne x22 im instance-Label zu tragen.
Der alte Name bleibt als Alias erhalten (laedt das generische Skript, NODE
vorbelegt auf x22) — bereits verteilte Aufrufe laufen weiter.
Verifiziert im Container mit x14-Nachbau: instance="x14.idx.wf-keyhelp",
Messmodus read-only, Selbstpruefung (Einheit/Zukunft/Domains) greift.
Der Survey kennt kein argparse — '--node' wurde still ignoriert (harmlos, aber
irrefuehrend). Argument entfernt und die Puffer-Falle im Aufruf dokumentiert.
BEFUND (x22, gemessen und reproduziert):
Der Aufruf war 'python3 apache-log-survey.py 2>&1 | tee bestand.txt'. Das Skript
gab ALLES erst nach der Scan-Schleife aus (alle print() standen dahinter), und
Python puffert stdout in eine PIPE blockweise (4-8 KB). Die Gesamtausgabe ist
aber nur ~1 KB -> sie landete erst beim Prozessende in der Datei.
Messung mit 2 Mio Zeilen (140 MB):
mit tee: nach 5 s = 0 Bytes, fertig nach 41 s -> dann 1028 Bytes
ohne Pipe: nach 1 s = 0 Bytes
python3 -u: nach 1 s = 0 Bytes
=> Die Datei war leer, weil der Scan LIEF. Auf einem KeyHelp-Host mit tausenden
Logdateien dauert das Minuten und liest sich wie ein Haenger oder Absturz.
FIX (drei Massnahmen, damit das nie wieder passiert):
1. sys.stdout.reconfigure(line_buffering=True) — jede Zeile geht sofort raus,
auch durch eine Pipe.
2. Startbanner VOR dem Scan: Anzahl und Gesamtgroesse der Dateien.
3. Fortschrittszeile pro Datei (Nummer/Gesamtzahl, Zeilen, Groesse, Pfad) plus
eine Zeile fuer nicht lesbare Dateien.
Verifiziert mit 1,4 Mio Zeilen durch eine Pipe: nach 2 s steht das Banner, drei
Fortschrittszeilen erscheinen waehrend des Scans. Gegenprobe mit dem alten Code:
nach 3 s 16 Bytes, alles erst am Ende.
BEFUND (echter Datenverlust, gemessen gegen KeyHelp-Formate):
KeyHelp/Debian schreiben error.log im Access-Format, aber OHNE Zeitzone:
[01/Sep/2026:10:00:05] [core:error] [pid 9] AH00124: ...
TS_FORMATS kannte nur "%d/%b/%Y:%H:%M:%S %z" (MIT Zone). parse_ts() gab None,
hour wurde "unknown", und der Schreiber ueberspringt unknown-Zeilen:
apache_archive_errors_total fehlte KOMPLETT — kein Fehler, kein Log, Panel leer.
Genau die stille Fehlerklasse, die nicht passieren darf.
FIX:
- Format "%d/%b/%Y:%H:%M:%S" (ohne Zone) ergaenzt.
- Unparsbare Zeitstempel werden GEZAEHLT und laut gemeldet (Domain + Beispiel +
Hinweis auf TS_FORMATS) statt still zu verschwinden.
- Neu: scripts/x22-log-backfill.sh — messen (read-only) und parsen in einem
Aufruf, mit Selbstpruefung (Einheiten, Zukunft, Domain-Anzahl) VOR der
Uebergabe und dem sicheren Import-Befehl am Ende.
Verifiziert im Container gegen echte KeyHelp-Formate inkl. rotierter .1 und
.gz-Datei: Messmodus aendert nichts (MD5-Vergleich), Backfill erzeugt alle
fuenf Metriken inkl. errors_total{level=error} und {level=warn}.
x14 sendete 105 Metriken, aber KEINE apache-Metriken und keine Logs. Der
Installer meldete Erfolg. Telegraf selbst nannte den Grund:
W! [inputs.tail] Directory "/home/users" is not readable but is followed
by wildcards, make sure you set read permissions or globbing will not work!
Drei Ursachen, alle behoben:
1. `setfacl -m u:telegraf:x` auf der Glob-Wurzel ist FALSCH. Eine Wildcard wird
per readdir expandiert — dafuer braucht das Verzeichnis `r-x`, nicht nur `x`.
Neue Logik: Muster werden mit `set -f` gelesen, dann je Treffer die Datei mit
`r` und JEDE Ebene zwischen Wurzel und Datei mit `r-x` belegt.
Gemessen: alte ACL -> 1 Warnung, 1 Punkt (nur /var/log/apache2);
neue ACL -> 0 Warnungen, 4 Punkte (alle 3 Dateien inkl. User-Homes).
2. Der Gegentest war blind: er las jede gefundene Datei per `head -c 1` als
telegraf. Das konnte nicht fehlschlagen, weil die Datei vom ROOT-Shell
expandiert wurde — die Glob-Wurzel blieb unlesbar, Telegraf bekam 0 Treffer,
die Meldung sagte trotzdem "alle N Logdateien lesen". Jetzt wird die
GLOB-AUFLOESUNG gemessen: dasselbe Muster im Dienstkontext expandieren und die
Trefferzahl vergleichen. 0 von 0 zaehlt als Fehler, nicht als Erfolg.
3. `for p in $_pfadquelle` ohne `set -f` expandiert die Muster im Root-Shell.
_pattern war danach ein konkreter Dateiname, der Wildcard-Zweig lief nie, und
die ACL landete nur auf den Dateien. Real passiert: "ACL auf 3 Datei(en)",
"kein Glob-Muster gefunden", 0 Treffer im Dienst.
Verifiziert mit echtem Telegraf 1.40.0 und REALEN KeyHelp-Rechten
(/home/users/<user> 0700, logs 0750, Dateien 0640).
Der Trace auf x14 endete exakt hier:
+ envsubst ${NODE_NAME} ... ${APACHE_ERROR_FILES}
root@x14:/home/mm#
Ohne "< \$2.tmp" liest envsubst STDIN. Bei sh install.sh vom Terminal ist stdin
die Konsole -> der Installer wartet auf Eingabe, nach der letzten Meldung
"Schreibe Konfiguration ...". Genau das Bild: Stillstand, ^C, kein Fehler.
Bei curl | sh waere es schlimmer: envsubst wuerde den Rest des Skripts fressen.
Ein frueherer Patch von mir hat die Redirection verschluckt; alle Laeufe mit
vorhandener telegraf.conf (x22) liefen trotzdem, weil dort nur der Fallback
subst_vars() aktiv war. Erst der Erstinstall (x14) traf envsubst.
Verifiziert: vorher Haenger bei envsubst, jetzt vollstaendiger Durchlauf mit
CONFIG_VERSION-Marker und allen Confs.
Zwei reale Fehler, beide still:
1. setfacl -R lief nur ab dem logs/-Verzeichnis. /home/users/<user> ist auf
Panel-Hostern chmod 700 — ohne Durchgangsrecht (x) auf den ELTERNTEIL kommt
telegraf nicht bis zur Datei. Die Meldung sagte trotzdem "ACL gesetzt".
Fix: x auf allen Elternverzeichnissen bis zur Wurzel + Default-ACL, damit
neu angelegte Domain-Ordner erben.
2. Der Gegentest brach beim ERSTEN Treffer ab. Das war
/var/log/apache2/keyhelp/access.log (root:adm, immer lesbar) -> Meldung
"Gegentest OK", waehrend ALLE Domain-Logs in den User-Homes unlesbar
blieben. Fix: jeden Logpfad pruefen und die Anzahl der Fehlschlaege melden.
Verifiziert in Debian-12 mit KeyHelp-aehnlichen Rechten (Home 700, Logs 640).
setfacl -R lief ueber die GANZE Panel-Wurzel (/home/users) — dort liegen auch
Mails, Webinhalte und Backups. Das Recht auf die Logs waere damit teuer und
wirkungslos zugleich. Jetzt werden die logs/-Verzeichnisse eingesammelt
(find -maxdepth 3), und jeder setfacl-Lauf hat ein Timeout von 120 s, damit
ein langsamer Lauf nicht wie ein Haenger aussieht.
x14 blieb nach der letzten Erfolgsmeldung stehen (Abbruch per ^C, kein Fehler).
Ursache: grant_apache_log_access() ruft apt-get install acl auf — OHNE
apt-get update davor und OHNE Timeout. Bei veralteten Paketlisten kann apt in
eine Rueckfrage laufen und unbegrenzt warten. Direkt danach haette der naechste
fetch gehangen: fetch() hatte ueberhaupt kein curl-Timeout.
Fix:
- apt-get update vor dem acl-Install, timeout 180 auf allen Paketmanager-Aufrufen
- fetch() mit -m 30 + 3 Versuchen statt unbegrenztem Warten
- alle weiteren curl-Aufrufe (keyring, Binary, Re-Exec) mit -m/--connect-timeout
- Fortschrittsmeldung vor der ACL-Phase: ein Haenger sah wie ein Absturz aus
Bei `sh install.sh` oder `< eingabe` ist stdin eine Datei — dort kann kein
Unterbefehl das Skript wegfressen. Der Re-Exec war dort unnoetig und kostete
einen Download. Nur `curl … | sh` (echte Pipe) braucht den Neustart.
Auf einem frischen System fehlt curl haeufig — genau dort, wo apt laeuft und
der Schutz am noetigsten ist. Der Re-Exec scheiterte deshalb still und der
Installer lief ohne Schutz weiter.
Jetzt: curl -> wget -> busybox wget -> lokale Quelle (NETBIRD_TG_SOURCE).
Ist keiner verfuegbar, wird das ausdruecklich gemeldet statt still uebergangen.
x14 brach mitten in der Installation ab: der Installer gab seinen eigenen
Quelltext aus ("; then", Kommentarzeilen) und blieb stehen.
Ursache: Bei `curl ... | sh` liest die Shell das Skript aus STDIN. apt startet
auf Ubuntu `needrestart` (Meldung "Scanning processes... / Scanning linux
images..."), und das liest stdin — damit war der Rest des Skripts weg. Die
Shell setzte mitten im Quelltext fort. Bei x22 trat es nicht auf, weil Telegraf
dort schon installiert war (kein apt-Lauf).
Fix, drei Schutzschichten:
1. Re-Exec: laeuft das Skript ueber eine Pipe, laedt es sich frisch von der
Quelle und startet sich neu — mit stdin auf /dev/null. NICHT per cat von
stdin kopieren: die Shell puffert bereits, die Kopie waere unvollstaendig.
2. NEEDRESTART_MODE=a + DEBIAN_FRONTEND=noninteractive fuer alle apt-Aufrufe.
3. </dev/null an jedem Paketmanager (apt, dnf, yum, apk).
Bisher wurden NUR access.log gesammelt — real nachgemessen: 31 logfile-Labels,
0 davon error.log. Bei einem KeyHelp-Host mit PHP/CMS fehlten damit genau die
Fehler, fuer die man ins Monitoring schaut.
Error-Logs jetzt in einem EIGENEN Input (name_override=apache_error), roh nach
Loki, mit Tags logtype=error und logfile=<pfad>. Getrennt vom Access-Grok, weil
das Format nicht passt:
[Mon Sep 14 00:10:01.123456 2026] [core:error] [pid 9] AH00124: ...
Liefen sie durch den Access-Parser, entstuenden keine Metriken, aber der
Aggregator wuerde sie in leere Gruppen einordnen.
DREI STILLE FALLEN dabei gefunden und behoben:
1. processors.rename hatte namepass=["journal"] — apache_error behielt das
Feld value, jede Panel-Query mit {.message} haette es nicht gefunden.
2. outputs.loki namepass enthielt apache_error nicht — Input sammelt ein,
Output verwirft, keine Fehlermeldung, Panels leer (wie bei journal damals).
3. outputs.prometheus namedrop ebenso — sonst wandern Textzeilen in die TSDB.
Verifiziert gegen echte KeyHelp-Formate: access=2 Metriken, error=4 Rohzeilen,
Feld heisst message, 0 Error-Zeilen als Metrik.
Die vorige Massnahme (log_time statt ts) war NICHT ausreichend. Ursache ist
tiefer: Telegraf schreibt pro Sammelzyklus EINEN Zeitstempel fuer den ganzen
Batch, und Remote Write kann nur Millisekunden. Zwei Requests derselben Serie
in derselben Millisekunde -> Prometheus lehnt den ganzen Batch ab -> FIFO-Kopf
sitzt fest -> Node liefert gar nichts mehr (derselbe eingefrorene Timestamp
alle ~15s im Log).
Ein Zeitstempel-Trick kann das nicht loesen. Aggregation schon: basicstats
fasst alle Metriken einer Serie pro 60s zu einem Punkt zusammen. Gemessen an
2000 echten x22-Zeilen: 2000 Punkte -> 4 Punkte, 0 Kollisionen, Requests
vollstaendig gezaehlt (1997).
VERSION 1.2.0, damit der Auto-Updater den Fix ausrollt.
Zwei Fehler, gleiche Wurzel — die Werte aus /etc/default/telegraf wurden nur
vom Auto-Updater gelesen, nicht von einem manuellen --update-only:
1. Abbruch "Kein Token", obwohl der Token auf der Maschine liegt. Der Weg
fuer eine Reparatur auf einer laufenden Node war damit verschlossen.
2. Stiller: ohne --name fiel NODE_NAME auf den Hostnamen zurueck. Auf einem
KeyHelp-Host (x22 vs x22.idx.wf-keyhelp) waere die Node unter falschem
Label registriert worden; ein PVE-Knoten haette seine Rolle verloren
(kein pvesh-Drop-in mehr).
Fix: Token, NODE_NAME, ROLE, COLLECTOR, PVE_* aus der Datei nachladen, aber
nur wenn die Kommandozeile nichts anderes sagt (NAME_GIVEN/ROLE_GIVEN).
Zusaetzlich ein zweiter, ernster Fund aus dem Test:
APACHE_FILES blieb leer, wenn ROLE=apache gesetzt war, Apache aber nicht
erkannt wurde. Die Conf rendert dann zu "files = " -> ungueltiges TOML ->
Telegraf startet nicht -> ALLE Metriken der Node aus. Berechnung jetzt
unabhaengig vom Guard, plus Sicherheitsnetz vor dem Rendern.
Echte x22-Logs: 2000 Zeilen -> 286 Kollisionen (duplicate sample).
Telegraf uebernimmt den Log-Zeitstempel; bei mehreren Treffern pro Sekunde
kollidiert der +1ms-Aufschlag mit echten Zeilen. Prometheus weist den Batch
ab, Telegraf retryt endlos -> FIFO steht -> Node liefert GAR NICHTS mehr.
Fix: eigenes Grok-Muster mit log_time als Feld (Telegraf nutzt eigene ns-Uhr)
+ processors.converter, damit resp_code/verb Tags bleiben.
Gemessen: 2000/2000 Samples, 0 Kollisionen.
Ausserdem: Einheiten-Waechter im Archiver (ms statt s -> Abbruch statt
nutzloser Block).