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).
161 lines
7.2 KiB
Bash
161 lines
7.2 KiB
Bash
#!/bin/bash
|
|
# raw-log-backfill.sh — Rohlogs eines KeyHelp-Hosts fuer den LOKI-Import sammeln.
|
|
#
|
|
# Warum es dieses Skript gibt:
|
|
# Der Prometheus-Archiv-Import verdichtet jede Logzeile auf Stunde/Domain/
|
|
# Status und VERWIRFT dabei Referrer, User-Agent und Client-IP. Fragen nach
|
|
# Herkunft ("woher kommen die Besucher") liessen sich damit nicht
|
|
# rueckwirkend beantworten. Lokis Rohzeilen koennen es — aber die
|
|
# Live-Sammlung reicht nur bis zur Installation zurueck.
|
|
# Dieses Skript holt die Historie in eine Datei, die Loki aufnehmen kann.
|
|
#
|
|
# DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. Es liest nur Logdateien,
|
|
# es stoppt nichts, es loescht nichts.
|
|
#
|
|
# AUFRUF (auf dem Host, als root):
|
|
# curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/raw-log-backfill.sh -o /root/raw-backfill.sh
|
|
# bash /root/raw-backfill.sh # MESSEN (aendert nichts)
|
|
# bash /root/raw-backfill.sh --collect # Rohdatei bauen
|
|
# bash /root/raw-backfill.sh --collect --since 2025-01-01
|
|
# bash /root/raw-backfill.sh --collect --tarball /root/apache-archive/apache-archive.tar.gz
|
|
#
|
|
# Am Ende steht der exakte 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)}"
|
|
COLLECT=0
|
|
SINCE=""
|
|
UNTIL=""
|
|
TARBALL=""
|
|
ROOTS="/var/log/apache2 /home/users"
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--collect) COLLECT=1 ;;
|
|
--since) SINCE="${2:?Datum fehlt}"; shift ;;
|
|
--until) UNTIL="${2:?Datum fehlt}"; shift ;;
|
|
--tarball) TARBALL="${2:?Pfad fehlt}"; shift ;;
|
|
--node) NODE="${2:?Name fehlt}"; shift ;;
|
|
--roots) ROOTS="${2:?Pfade fehlen}"; shift ;;
|
|
*) echo "Unbekannte Option: $1" >&2; exit 2 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
log() { printf '\033[1;34m[raw-backfill]\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m[raw-backfill] WARNUNG:\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31m[raw-backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren (die Logs liegen unter /home/users mit 0700)."
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
COLLECTOR="$OUT_DIR/raw-log-collect.py"
|
|
if [ -r "$COLLECTOR" ] && [ -n "${SKIP_FETCH:-}" ]; then
|
|
log "Collector lokal uebernommen: $COLLECTOR"
|
|
else
|
|
log "Lade Collector vom Repo …"
|
|
curl -fsSL -m 30 "$REPO_RAW/scripts/raw-log-collect.py" -o "$COLLECTOR" \
|
|
|| die "raw-log-collect.py konnte nicht geladen werden."
|
|
fi
|
|
python3 -c "import sys; sys.exit(0)" || die "python3 fehlt auf diesem Host."
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 1) MESSEN — was ist ueberhaupt da? (aendert nichts)
|
|
# --------------------------------------------------------------------------
|
|
log "Bestandsaufnahme der Apache-Logs (liest nur) …"
|
|
FILES=$(find $ROOTS -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l)
|
|
GZ=$(find $ROOTS -name '*.log*.gz' 2>/dev/null | wc -l)
|
|
RAW_BYTES=$(find $ROOTS -name 'access.log*' -o -name 'error.log*' 2>/dev/null -printf '%s\n' 2>/dev/null | awk '{s+=$1} END {print s+0}')
|
|
log " Logdateien: $FILES (davon komprimiert: $GZ)"
|
|
log " Rohgroesse: $(( RAW_BYTES / 1024 / 1024 )) MB"
|
|
log " Aelteste Datei: $(find $ROOTS -name 'access.log*' -printf '%TY-%Tm-%Td %p\n' 2>/dev/null | sort | head -1 | cut -c1-10)"
|
|
if [ -n "$TARBALL" ] && [ -r "$TARBALL" ]; then
|
|
log " Tarball: $TARBALL ($(( $(stat -c%s "$TARBALL") / 1024 / 1024 )) MB)"
|
|
log " Rohlogs im Tar: $(tar -tzf "$TARBALL" 2>/dev/null | grep -c 'raw/.*log' || echo 0) Dateien"
|
|
fi
|
|
|
|
if [ "$COLLECT" -eq 0 ]; then
|
|
echo
|
|
log "Das war NUR die Messung — es wurde nichts geschrieben."
|
|
echo " Weiter mit: bash $0 --collect${SINCE:+ --since $SINCE}"
|
|
echo " Optional zusaetzlich Rohlogs aus dem Archiv-Tarball:"
|
|
echo " --tarball $OUT_DIR/apache-archive.tar.gz"
|
|
exit 0
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 2) SAMMELN
|
|
# --------------------------------------------------------------------------
|
|
ARGS="--node $NODE --out $OUT_DIR --roots $ROOTS"
|
|
[ -n "$SINCE" ] && ARGS="$ARGS --since $SINCE"
|
|
[ -n "$UNTIL" ] && ARGS="$ARGS --until $UNTIL"
|
|
[ -n "$TARBALL" ] && [ -r "$TARBALL" ] && ARGS="$ARGS --include-tarball $TARBALL"
|
|
|
|
log "Sammle Rohlogs … (bei viel Historie dauert das einige Minuten)"
|
|
# shellcheck disable=SC2086
|
|
python3 "$COLLECTOR" $ARGS 2>&1 | tee "$OUT_DIR/collect.log"
|
|
RC=${PIPESTATUS[0]}
|
|
[ "$RC" -eq 0 ] || die "Collector brach ab (Code $RC). Siehe $OUT_DIR/collect.log"
|
|
|
|
PART=$(ls -t "$OUT_DIR"/loki-raw-*/raw.tsv 2>/dev/null | head -1)
|
|
STATE=$(ls -t "$OUT_DIR"/loki-raw-*/state.json 2>/dev/null | head -1)
|
|
[ -n "$PART" ] && [ -r "$PART" ] || die "Keine Rohdatei gefunden."
|
|
[ -n "$STATE" ] && [ -r "$STATE" ] || die "Keine Zustandsdatei gefunden."
|
|
|
|
LINES=$(wc -l < "$PART")
|
|
SIZE=$(( $(stat -c%s "$PART") / 1024 / 1024 ))
|
|
log "Rohdatei: $PART"
|
|
log " $LINES Zeilen, $SIZE MB"
|
|
|
|
# Gegenprobe: stimmt die Zeilenzahl mit der Zustandsdatei ueberein?
|
|
EXPECT=$(python3 -c "import json,sys; print(json.load(open('$STATE'))['lines'])")
|
|
if [ "$LINES" != "$EXPECT" ]; then
|
|
warn "Zeilenzahl weicht ab: Datei=$LINES, Auftrag=$EXPECT — bitte pruefen."
|
|
else
|
|
log " Gegenprobe: Zeilenzahl stimmt mit dem Auftrag ueberein ($LINES)."
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 3) Uebergabe vorbereiten
|
|
#
|
|
# Der Collector laeuft als root (die Logs liegen unter /home/users mit 0700),
|
|
# seine Ausgabe gehoert damit ebenfalls root. `scp` als normaler Nutzer kaeme
|
|
# nicht an die Datei — und ein scp als root scheitert oft am SSH-Schluessel.
|
|
# Deshalb die Dateien fuer die Uebergabe lesbar machen und an eine Stelle
|
|
# legen, die beide erreichen.
|
|
# --------------------------------------------------------------------------
|
|
STAGE=/tmp/rawlog-upload
|
|
mkdir -p "$STAGE"
|
|
cp "$PART" "$STAGE/raw.tsv"
|
|
cp "$STATE" "$STAGE/state.json"
|
|
chmod 644 "$STAGE/raw.tsv" "$STAGE/state.json"
|
|
log "Uebergabedateien bereit: $STAGE/ (644, fuer scp als normaler Nutzer)"
|
|
|
|
echo
|
|
log "NAECHSTER SCHRITT — diese Zeilen auf dem imac ausfuehren:"
|
|
echo
|
|
echo " # 1) Rohdatei und Auftrag uebertragen (als normaler Nutzer moeglich)"
|
|
echo " # ACHTUNG: nach /data/tmp, NICHT nach /tmp!"
|
|
echo " # Auf dem imac ist /tmp ein tmpfs mit 15 GB (also RAM). Eine grosse"
|
|
echo " # Rohdatei wuerde dort den Arbeitsspeicher belegen und Loki stoeren."
|
|
echo " ssh mm@100.83.134.46 'mkdir -p /data/tmp'"
|
|
echo " scp $STAGE/raw.tsv $STAGE/state.json mm@100.83.134.46:/data/tmp/"
|
|
echo
|
|
echo " # 2) Einlesen (der Import laeuft LOKAL auf dem imac — kein Token noetig)"
|
|
echo " ssh mm@100.83.134.46 \\"
|
|
echo " 'python3 /usr/local/bin/loki-raw-import.py \\"
|
|
echo " --file /tmp/$(basename "$PART") --state /tmp/$(basename "$STATE") \\"
|
|
echo " --url http://127.0.0.1:3100'"
|
|
echo
|
|
echo " # Vorher pruefen (empfohlen, aendert nichts):"
|
|
echo " ssh mm@100.83.134.46 \\"
|
|
echo " 'python3 /usr/local/bin/loki-raw-import.py \\"
|
|
echo " --file /tmp/$(basename "$PART") --state /tmp/$(basename "$STATE") \\"
|
|
echo " --url http://127.0.0.1:3100 --dry-run'"
|
|
echo
|
|
log "Danach im Grafana pruefen: {imported=\"1\"} (Dashboard: Weblogs-Analyse)"
|
|
warn "Das Skript selbst schreibt NICHTS nach Loki — der Import ist ein eigener Schritt."
|