feat(backup-system): segundo offsite Backblaze B2 (crown jewels)

- b2-crown-jewels.sh: sync diario 04:00 de las 2 copias mas recientes de
  cada servicio irremplazable (todo menos uptime-kuma) a
  b2:chemavx-k3s-backups/crown-jewels/ (~2GB, tier gratuito).
- reddit-intel.sh sube tambien a B2 (retencion 3d).
- verify.sh comprueba Mega (completo) + B2 (crown jewels).
- Config via Secret out-of-band rclone-conf-b2 (mega+b2, kubectl, NO git);
  consolidar en Infisical RCLONE_CONF cuando se anada [b2] por UI (la
  identity del operator es read-only por API).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 17:24:02 +00:00
co-authored by Claude Fable 5
parent 39ea16fe91
commit c364d46ac6
5 changed files with 120 additions and 5 deletions
+51 -1
View File
@@ -247,6 +247,33 @@ data:
log "Sync completado."
log "Archivos en Mega:"
rclone ls mega:k3s-backups/ --config /rclone/rclone.conf
b2-crown-jewels.sh: |
#!/bin/sh
# Segunda copia offsite (Backblaze B2): "crown jewels" — las 2 copias más
# recientes de cada servicio irremplazable (~2GB, cabe en los 10GB gratis).
# Todo menos uptime-kuma (622M de historial de heartbeats, prescindible).
# Imagen BusyBox — sin GNU-ismos. Config: Secret out-of-band rclone-conf-b2
# (mega+b2; consolidar en Infisical RCLONE_CONF cuando se añada [b2] en la UI).
set -e
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
BACKUP_DIR=/data/backups/backups
CROWN="n8n openclaw homarr authentik-pg grafana k3s-db gitea ghost-en-db ghost-en-content zona-exclusion-db zona-exclusion-content researchowl roswell-pg umami-pg infisical-pg trilium vaultwarden filebrowser-db home-master"
KEEP=/tmp/crown-jewels
rm -rf "$KEEP"; mkdir -p "$KEEP"
for p in $CROWN; do
for f in $(ls -1 "$BACKUP_DIR/${p}"_* 2>/dev/null | sort | tail -2); do
ln -s "$f" "$KEEP/$(basename "$f")"
done
done
N=$(ls -1 "$KEEP" | wc -l)
log "Sync de $N artefactos crown-jewels a B2..."
# -L: resuelve los symlinks; sync = espejo → la retención remota (2/servicio) es automática
rclone sync "$KEEP" b2:chemavx-k3s-backups/crown-jewels/ -L \
--config /rclone/rclone.conf --log-level INFO --stats 60s
rm -rf "$KEEP"
log "B2 crown-jewels completado ($N artefactos)."
reddit-intel.sh: |
#!/bin/sh
# Corre en chemavx-n97 (imagen rclone, BusyBox — sin GNU-ismos).
@@ -265,6 +292,9 @@ data:
rclone copy "$F" mega:k3s-backups/reddit-intel/ --config /rclone/rclone.conf --log-level INFO
log "Podando copias remotas de más de 8 días..."
rclone delete mega:k3s-backups/reddit-intel/ --min-age 8d --config /rclone/rclone.conf
log "Subiendo a B2 (crown jewels, retención 3 días)..."
rclone copy "$F" b2:chemavx-k3s-backups/reddit-intel/ --config /rclone/rclone.conf --log-level INFO
rclone delete b2:chemavx-k3s-backups/reddit-intel/ --min-age 3d --config /rclone/rclone.conf
rm -f "$F"
log "reddit-intel OK"
verify.sh: |
@@ -325,12 +355,32 @@ data:
TOTAL=$(echo "$FILES" | grep -c "tar.gz\|sql.gz\|db.gz" || true)
log "Total archivos en Mega: $TOTAL"
# === Segundo offsite: B2 crown-jewels (todo menos uptime-kuma) ===
log "=== Verificación de crown-jewels en B2 ==="
CROWN="n8n openclaw homarr authentik-pg grafana k3s-db gitea ghost-en-db ghost-en-content zona-exclusion-db zona-exclusion-content researchowl roswell-pg umami-pg infisical-pg trilium vaultwarden filebrowser-db home-master"
B2FILES=$(rclone ls b2:chemavx-k3s-backups/ --config /rclone/rclone.conf 2>&1)
for svc in $CROWN; do
FOUND=0
for d in $ACCEPTED_DATES; do
if echo "$B2FILES" | grep -q "${svc}_${d}"; then
FOUND=1
break
fi
done
if [ "$FOUND" -eq 1 ]; then
log "[OK] b2/$svc"
else
log "[WARN] b2/$svc — sin backup en los últimos 3 días"
ERRORS=$((ERRORS + 1))
fi
done
if [ "$ERRORS" -gt 0 ]; then
log "RESULTADO: $ERRORS servicio(s) sin backup reciente. Revisar."
exit 1
fi
log "RESULTADO: Todos los backups verificados correctamente."
log "RESULTADO: Todos los backups verificados correctamente (Mega + B2)."
kind: ConfigMap
metadata:
name: backup-scripts