feat(backfill): Skript fuer alle KeyHelp-Hosts generalisiert

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.
This commit is contained in:
mm committed 2026-09-14 15:29:11 +02:00
1 parent c18263e4ed
commit 39e86adfba
2 files changed
+158 -149

No files matched your search

+152
View File
@@ -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 <<EOF
============================================================
FERTIG. Naechster Schritt auf dem imac.
============================================================
WICHTIG: NICHT von Hand entpacken. Die Bloecke gehoeren in die getrennte
Archiv-Instanz (:9091), nie in die Produktions-TSDB. Genau dieser Weg hat am
14.09.2026 die Zeitreferenz verschoben — 13 Nodes verloren ihre Metriken.
# 1) Uebertragen (vom Host aus, im NetBird)
scp $OM mm@100.83.134.46:/tmp/apache-archive.openmetrics
# 2) Auf dem imac einspielen (prueft Zukunft, Manifest, Gegenprobe)
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --file /tmp/apache-archive.openmetrics'
# 3) Zweiter Lauf derselben Datei wird automatisch uebersprungen.
# Anzeigen, was schon importiert ist:
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --list'
# 4) Pruefen (step >= 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
+6 -149
View File
@@ -1,151 +1,8 @@
#!/bin/bash #!/bin/bash
# x22-log-backfill.sh — Alte Apache-Logs von x22 messen, parsen, als Archiv # x22-log-backfill.sh — Kompatibilitaets-Alias.
# bereitstellen. DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. # Die Logik liegt in keyhelp-log-backfill.sh (fuer alle KeyHelp-Hosts).
# # Dieser Name bleibt bestehen, damit bereits verteilte Aufrufe weiterlaufen.
# SICHERHEIT (die drei Wege, die schon schiefgingen): export NODE="${NODE:-x22.idx.wf-keyhelp}"
# 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
REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}"
OUT_DIR="${OUT_DIR:-/root/apache-archive}" exec curl -fsSL -m 30 "$REPO_RAW/scripts/keyhelp-log-backfill.sh" -o /tmp/.kh-backfill.sh \
NODE="${NODE:-x22.idx.wf-keyhelp}" && NODE="$NODE" REPO_RAW="$REPO_RAW" bash /tmp/.kh-backfill.sh "$@"
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 <<EOF
============================================================
FERTIG auf x22. Naechster Schritt auf dem imac.
============================================================
WICHTIG: NICHT von Hand entpacken. Die Bloecke gehoeren in die getrennte
Archiv-Instanz (:9091), nie in die Produktions-TSDB. Genau dieser Weg hat am
14.09.2026 die Zeitreferenz verschoben — 13 Nodes verloren ihre Metriken.
# 1) Uebertragen (von x22 aus, im NetBird)
scp $OM mm@100.83.134.46:/tmp/apache-archive.openmetrics
# 2) Auf dem imac einspielen (prueft Zukunft, Manifest, Gegenprobe)
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --file /tmp/apache-archive.openmetrics'
# 3) Zweiter Lauf derselben Datei wird automatisch uebersprungen.
# Anzeigen, was schon importiert ist:
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --list'
# 4) Pruefen (step >= 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