Restore OpenWebUI with its matching image

This commit is contained in:
Mikei386 committed 2026-08-21 14:28:44 +02:00
1 parent 0c21aed7af
commit 934a4d8ced
3 files changed
+85 -1

No files matched your search

+14
View File
@@ -50,6 +50,7 @@ sudo ./platform/migration/restore-reference-backup.sh \
Das Skript prüft zuerst die Archiv-Hashes und übernimmt ausschließlich: Das Skript prüft zuerst die Archiv-Hashes und übernimmt ausschließlich:
- die persistente OpenWebUI-Datenbank samt Einstellungen und Uploads, - die persistente OpenWebUI-Datenbank samt Einstellungen und Uploads,
- das zur Datenbank passende, gesicherte OpenWebUI-Container-Image,
- den dazugehörigen WebUI- und Router-Schlüssel, - den dazugehörigen WebUI- und Router-Schlüssel,
- die freigegebenen HA-, ARR- und Unraid-Secrets, - die freigegebenen HA-, ARR- und Unraid-Secrets,
- `runraid`, dessen privaten Schlüssel, Zertifikat und Known-Hosts-Datei. - `runraid`, dessen privaten Schlüssel, Zertifikat und Known-Hosts-Datei.
@@ -59,5 +60,18 @@ Experimentdienste oder `.before-*`-Altstände. Vor dem Ersetzen des frischen
OpenWebUI-Volumes wird unter `/data/open-webui-before-restore-*.tar.gz` eine OpenWebUI-Volumes wird unter `/data/open-webui-before-restore-*.tar.gz` eine
Rückfallsicherung erstellt. Secret-Werte werden nicht ausgegeben. Rückfallsicherung erstellt. Secret-Werte werden nicht ausgegeben.
OpenWebUI-Datenbanken sind nicht beliebig vor- oder rückwärtskompatibel. Das
Restore-Skript lädt deshalb bewusst das im Backup inventarisierte Original-
Image, versieht es lokal mit dem Tag `mike-ai/openwebui:reference` und trägt
diesen Tag sowohl in `stack.env` als auch – falls vorhanden – in
`/root/mike-ai-install.env` ein. Dadurch bleibt auch ein späterer, idempotenter
Installerlauf versionsgleich. Ein Upgrade auf eine neuere OpenWebUI-Version
erfolgt erst danach kontrolliert und mit einer eigenen Datenbanksicherung.
Falls ein älteres Image-Archiv trotz seines Namens das OpenWebUI-Image nicht
enthält, verwendet das Skript ausschließlich den im Docker-Inventar gesicherten
unveränderlichen Registry-Digest. Es fällt niemals auf `latest` zurück.
Da lokale Docker-Image-IDs beim Wiederherstellen von einem Registry-Digest
abweichen können, verifiziert es zusätzlich die gesicherte OCI-Build-Revision.
Der alte Host bleibt bis zum bestandenen Abnahmetest unverändert und dient nur Der alte Host bleibt bis zum bestandenen Abnahmetest unverändert und dient nur
als Referenz. Es werden keine Caches oder unbekannten Altverzeichnisse kopiert. als Referenz. Es werden keine Caches oder unbekannten Altverzeichnisse kopiert.
+7
View File
@@ -185,6 +185,13 @@ experimentelle Altcontainer zurückzubringen. Der Leerhost-Probelauf wird auf
Athena praktisch protokolliert und seine Korrekturen fließen direkt in den Athena praktisch protokolliert und seine Korrekturen fließen direkt in den
Installer zurück. Installer zurück.
Ein Container-Backup gilt nur dann als vollständig, wenn nach `docker save`
nicht bloß das Archiv und seine Prüfsumme existieren: Ein isolierter
Probeimport muss außerdem jede erwartete Image-ID beziehungsweise den
unveränderlichen Registry-Digest und die OCI-Build-Revision bestätigen. Die
OpenWebUI-Datenbank wird immer zusammen mit genau diesem geprüften Image
gesichert und wiederhergestellt.
Jeder Schritt muss wiederholbar, einzeln prüfbar und bei Fehlern abbrechbar Jeder Schritt muss wiederholbar, einzeln prüfbar und bei Fehlern abbrechbar
sein. Ein fehlgeschlagener Schritt darf keinen halb aktivierten Dienst sein. Ein fehlgeschlagener Schritt darf keinen halb aktivierten Dienst
hinterlassen. hinterlassen.
+64 -1
View File
@@ -8,6 +8,8 @@ BACKUP=${1:-}
STACK_DIR=${STACK_DIR:-/opt/mike-ai/stack} STACK_DIR=${STACK_DIR:-/opt/mike-ai/stack}
SECRETS_DIR=${SECRETS_DIR:-/etc/mike-ai} SECRETS_DIR=${SECRETS_DIR:-/etc/mike-ai}
OPENWEBUI_VOLUME=${OPENWEBUI_VOLUME:-mike-ai_open-webui-data} OPENWEBUI_VOLUME=${OPENWEBUI_VOLUME:-mike-ai_open-webui-data}
INSTALL_CONFIG=${INSTALL_CONFIG:-/root/mike-ai-install.env}
REFERENCE_OPENWEBUI_TAG=${REFERENCE_OPENWEBUI_TAG:-mike-ai/openwebui:reference}
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; } die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
log() { printf '\n==> %s\n' "$*"; } log() { printf '\n==> %s\n' "$*"; }
@@ -20,7 +22,8 @@ log() { printf '\n==> %s\n' "$*"; }
ui_archive=$BACKUP/archives/open-webui-migrated.tar.gz ui_archive=$BACKUP/archives/open-webui-migrated.tar.gz
mcp_archive=$BACKUP/archives/mcp-and-search-config.tar.gz mcp_archive=$BACKUP/archives/mcp-and-search-config.tar.gz
for archive in "$ui_archive" "$mcp_archive"; do image_archive=$BACKUP/images/openwebui-mcp-images.tar.gz
for archive in "$ui_archive" "$mcp_archive" "$image_archive"; do
[[ -f $archive ]] || die "Archiv fehlt: $archive" [[ -f $archive ]] || die "Archiv fehlt: $archive"
expected=$(awk -v suffix="/${archive#"$BACKUP"/}" \ expected=$(awk -v suffix="/${archive#"$BACKUP"/}" \
'$2 ~ suffix "$" {print $1; exit}' "$BACKUP/SHA256SUMS") '$2 ~ suffix "$" {print $1; exit}' "$BACKUP/SHA256SUMS")
@@ -29,6 +32,62 @@ for archive in "$ui_archive" "$mcp_archive"; do
[[ $actual == "$expected" ]] || die "Prüfsumme falsch: $archive" [[ $actual == "$expected" ]] || die "Prüfsumme falsch: $archive"
done done
[[ -f $BACKUP/inventory/docker-images.txt ]] || \
die "Docker-Image-Inventar fehlt."
reference_openwebui_image=$(awk \
'$1 == "ghcr.io/open-webui/open-webui" { print $3; exit }' \
"$BACKUP/inventory/docker-images.txt")
[[ $reference_openwebui_image == sha256:* ]] || \
die "Gesicherte OpenWebUI-Image-ID ist nicht eindeutig ermittelbar."
[[ -f $BACKUP/inventory/docker-inspect.json ]] || \
die "Docker-Inspect-Inventar fehlt."
reference_openwebui_ref=$(python3 - "$BACKUP/inventory/docker-inspect.json" <<'PY'
import json
import sys
for container in json.load(open(sys.argv[1], encoding="utf-8")):
name = (container.get("Name") or "").lstrip("/").lower()
if name in {"open-webui", "mike-ai-open-webui"}:
image = (container.get("Config") or {}).get("Image", "")
if "@sha256:" in image:
print(image)
break
PY
)
reference_openwebui_revision=$(python3 - "$BACKUP/inventory/docker-inspect.json" <<'PY'
import json
import sys
for container in json.load(open(sys.argv[1], encoding="utf-8")):
name = (container.get("Name") or "").lstrip("/").lower()
if name in {"open-webui", "mike-ai-open-webui"}:
labels = (container.get("Config") or {}).get("Labels") or {}
print(labels.get("org.opencontainers.image.revision", ""))
break
PY
)
log "Versionsgleiches OpenWebUI-Image aus der Sicherung laden"
docker load -i "$image_archive" >/dev/null
selected_openwebui_image=$reference_openwebui_image
if ! docker image inspect "$selected_openwebui_image" >/dev/null 2>&1; then
[[ $reference_openwebui_ref == *@sha256:* ]] || \
die "Image-Archiv enthält OpenWebUI nicht und Registry-Digest fehlt."
log "Image-Archiv unvollständig; exakten OpenWebUI-Digest nachladen"
docker pull "$reference_openwebui_ref" >/dev/null
selected_openwebui_image=$reference_openwebui_ref
fi
docker image inspect "$selected_openwebui_image" >/dev/null 2>&1 || \
die "Gesichertes OpenWebUI-Image wurde nicht geladen."
actual_openwebui_revision=$(docker image inspect --format \
'{{ index .Config.Labels "org.opencontainers.image.revision" }}' \
"$selected_openwebui_image")
if [[ -n $reference_openwebui_revision && \
$actual_openwebui_revision != "$reference_openwebui_revision" ]]; then
die "OpenWebUI-Build-Revision stimmt nicht mit der Sicherung überein."
fi
docker tag "$selected_openwebui_image" "$REFERENCE_OPENWEBUI_TAG"
stage=$(mktemp -d /data/mike-ai-restore.XXXXXX) stage=$(mktemp -d /data/mike-ai-restore.XXXXXX)
trap 'rm -rf "$stage"' EXIT trap 'rm -rf "$stage"' EXIT
@@ -82,6 +141,10 @@ rsync -a --delete "$ui_source/data/" "$volume_path/"
log "Schlüssel und aktuelle Tool-Secrets wiederherstellen" log "Schlüssel und aktuelle Tool-Secrets wiederherstellen"
set_env_value "$SECRETS_DIR/stack.env" WEBUI_SECRET_KEY "$old_webui_key" set_env_value "$SECRETS_DIR/stack.env" WEBUI_SECRET_KEY "$old_webui_key"
set_env_value "$SECRETS_DIR/stack.env" ROUTER_API_KEY "$old_router_key" set_env_value "$SECRETS_DIR/stack.env" ROUTER_API_KEY "$old_router_key"
set_env_value "$SECRETS_DIR/stack.env" OPENWEBUI_IMAGE "$REFERENCE_OPENWEBUI_TAG"
if [[ -f $INSTALL_CONFIG ]]; then
set_env_value "$INSTALL_CONFIG" OPENWEBUI_IMAGE "$REFERENCE_OPENWEBUI_TAG"
fi
install -d -m 0700 "$SECRETS_DIR/runraid" "$SECRETS_DIR/keys" "$SECRETS_DIR/ssh" install -d -m 0700 "$SECRETS_DIR/runraid" "$SECRETS_DIR/keys" "$SECRETS_DIR/ssh"
install -m 0600 "$stage/approved/etc/mike-ai/homeassistant-admin-mcp.env" \ install -m 0600 "$stage/approved/etc/mike-ai/homeassistant-admin-mcp.env" \