Keep OpenWebUI online during recovery backups
This commit is contained in:
1 parent
3d528f2716
commit
5a51940e60
2 files changed
+36
-21
No files matched your search
@@ -58,10 +58,9 @@ Das Skript nimmt ausschließlich auf:
|
|||||||
Der Klartext liegt nur in einem kurzlebigen root-only Verzeichnis unter
|
Der Klartext liegt nur in einem kurzlebigen root-only Verzeichnis unter
|
||||||
`/tmp` und wird beim Ende entfernt. Das Ergebnis ist vollständig mit age
|
`/tmp` und wird beim Ende entfernt. Das Ergebnis ist vollständig mit age
|
||||||
verschlüsselt. Ohne erfolgreiche Ausgabe `RECOVERY_BUNDLE_OK` gilt die
|
verschlüsselt. Ohne erfolgreiche Ausgabe `RECOVERY_BUNDLE_OK` gilt die
|
||||||
Sicherung als fehlgeschlagen. Für ein konsistentes SQLite-Abbild stoppt das
|
Sicherung als fehlgeschlagen. Für ein konsistentes SQLite-Abbild verwendet das
|
||||||
Skript OpenWebUI kurz und startet es anschließend auch bei einem Fehler über
|
Skript die Online-Backup-Schnittstelle der Datenbank. OpenWebUI, laufende
|
||||||
eine Aufräumroutine wieder. Laufende LLM-Profile und MCP-Dienste bleiben dabei
|
LLM-Profile und MCP-Dienste bleiben während der Sicherung verfügbar.
|
||||||
unangetastet.
|
|
||||||
|
|
||||||
## Wiederherstellung nach SSD-Verlust
|
## Wiederherstellung nach SSD-Verlust
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,9 @@ install -d -m 0700 "$(dirname "$OUTPUT")"
|
|||||||
[[ ! -e $OUTPUT ]] || die "Zieldatei existiert bereits: $OUTPUT"
|
[[ ! -e $OUTPUT ]] || die "Zieldatei existiert bereits: $OUTPUT"
|
||||||
|
|
||||||
stage=$(mktemp -d /tmp/mike-ai-recovery.XXXXXX)
|
stage=$(mktemp -d /tmp/mike-ai-recovery.XXXXXX)
|
||||||
openwebui_was_running=false
|
sqlite_snapshot=""
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if $openwebui_was_running; then
|
[[ -z $sqlite_snapshot ]] || rm -f -- "$sqlite_snapshot"
|
||||||
docker start "$OPENWEBUI_CONTAINER" >/dev/null 2>&1 || \
|
|
||||||
printf 'WARNUNG: OpenWebUI konnte nach dem Backup nicht gestartet werden.\n' >&2
|
|
||||||
fi
|
|
||||||
rm -rf "$stage"
|
rm -rf "$stage"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
@@ -44,18 +41,37 @@ tar -C "$stage/rootfs" -czf "$stage/payload/host-config.tar.gz" .
|
|||||||
volume_path=$(docker volume inspect -f '{{.Mountpoint}}' "$OPENWEBUI_VOLUME")
|
volume_path=$(docker volume inspect -f '{{.Mountpoint}}' "$OPENWEBUI_VOLUME")
|
||||||
[[ -s $volume_path/webui.db ]] || die "OpenWebUI-Datenbank fehlt oder ist leer."
|
[[ -s $volume_path/webui.db ]] || die "OpenWebUI-Datenbank fehlt oder ist leer."
|
||||||
|
|
||||||
# OpenWebUI uses SQLite. A filesystem archive while the database is being
|
# OpenWebUI uses SQLite. The online backup API creates a transactionally
|
||||||
# written can be internally inconsistent even if tar itself succeeds. Keep the
|
# consistent snapshot while the service remains available. The archive omits
|
||||||
# interruption short, but always restart the container through the EXIT trap.
|
# the live DB/WAL/SHM and stores that snapshot under the canonical DB name.
|
||||||
if [[ $(docker inspect -f '{{.State.Running}}' "$OPENWEBUI_CONTAINER" 2>/dev/null || true) == true ]]; then
|
[[ $(docker inspect -f '{{.State.Running}}' "$OPENWEBUI_CONTAINER" 2>/dev/null || true) == true ]] || \
|
||||||
openwebui_was_running=true
|
die "OpenWebUI läuft nicht; Online-Datenbanksicherung nicht möglich."
|
||||||
docker stop --time 30 "$OPENWEBUI_CONTAINER" >/dev/null
|
sqlite_snapshot="$volume_path/.mike-ai-recovery-webui.db"
|
||||||
fi
|
rm -f -- "$sqlite_snapshot"
|
||||||
tar -C "$volume_path" -czf "$stage/payload/openwebui-data.tar.gz" .
|
docker exec -i "$OPENWEBUI_CONTAINER" python - <<'PY'
|
||||||
if $openwebui_was_running; then
|
import os
|
||||||
docker start "$OPENWEBUI_CONTAINER" >/dev/null
|
import sqlite3
|
||||||
openwebui_was_running=false
|
|
||||||
fi
|
source = "/app/backend/data/webui.db"
|
||||||
|
snapshot = "/app/backend/data/.mike-ai-recovery-webui.db"
|
||||||
|
if os.path.exists(snapshot):
|
||||||
|
os.unlink(snapshot)
|
||||||
|
with sqlite3.connect(source) as src, sqlite3.connect(snapshot) as dst:
|
||||||
|
src.backup(dst)
|
||||||
|
with sqlite3.connect(snapshot) as check:
|
||||||
|
result = check.execute("PRAGMA integrity_check").fetchone()
|
||||||
|
if not result or result[0] != "ok":
|
||||||
|
raise SystemExit("SQLite integrity_check failed")
|
||||||
|
PY
|
||||||
|
[[ -s $sqlite_snapshot ]] || die "Konsistenter OpenWebUI-Snapshot wurde nicht erzeugt."
|
||||||
|
tar -C "$volume_path" \
|
||||||
|
--exclude='./webui.db' \
|
||||||
|
--exclude='./webui.db-wal' \
|
||||||
|
--exclude='./webui.db-shm' \
|
||||||
|
--transform='s#^\./\.mike-ai-recovery-webui\.db$#./webui.db#' \
|
||||||
|
-czf "$stage/payload/openwebui-data.tar.gz" .
|
||||||
|
tar -tzf "$stage/payload/openwebui-data.tar.gz" | grep -Eq '(^|/)webui\.db$' || \
|
||||||
|
die "OpenWebUI-Archiv enthält den konsistenten Datenbanksnapshot nicht."
|
||||||
|
|
||||||
source_commit=unknown
|
source_commit=unknown
|
||||||
[[ ! -s $STACK_DIR/.mike-ai-source-commit ]] || source_commit=$(<"$STACK_DIR/.mike-ai-source-commit")
|
[[ ! -s $STACK_DIR/.mike-ai-source-commit ]] || source_commit=$(<"$STACK_DIR/.mike-ai-source-commit")
|
||||||
|
|||||||
Reference in new issue
Block a user