ESPO-12-Rueckweg: Snapshot zusammenfuehren statt neu starten

Stack anhalten, /srv aushaengen, lvconvert --merge, wieder einhaengen, starten.
Das Aushaengen ist der Kern: Ist der Ursprung geoeffnet, verschiebt LVM das
Zusammenfuehren auf die naechste Aktivierung, also auf einen Neustart - hier
geschieht es sofort und nachpruefbar.

Scheitert etwas vor dem Zusammenfuehren, wird der Dienst wieder gestartet und
nichts zurueckgenommen: eine stehende Instanz ist das schlechteste Ergebnis.
Ein ungueltiger Snapshot fuehrt zum ehrlichen Abbruch statt zu einem Versuch.

Weil ganz /srv zurueckgenommen wird, kommt auch die gerenderte Compose mit dem
alten Image-Tag zurueck - die Instanz startet damit von selbst auf dem alten
Stand, ohne Migration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
gordon.zimm 2026-09-24 16:56:04 +02:00
parent e79564445d
commit 580c02af36

89
rmm-tasks/ESPO-12-Rueckweg.sh Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
# ESPO-12-Rueckweg - setzt die Instanz auf den Stand vor dem Update zurueck.
# Aufruf: ohne Argumente. Verwendet den Snapshot, den ESPO-10 angelegt hat.
#
# Der Snapshot wird dabei VERBRAUCHT: Nach dem Zusammenfuehren existiert er nicht mehr, ein zweiter
# Rueckweg ist dann nicht moeglich. Zurueckgenommen wird ganz /srv - also auch die gerenderte Compose
# mit dem alten Image-Tag. Deshalb startet die Instanz danach von selbst wieder auf dem alten Stand.
set -u
echo "ESPO-12-Rueckweg, Skriptstand 2026-09-24"
VG=ubuntu-vg
SNAPSHOT=srv-vor-update
COMPOSE=/srv/espo/docker-compose.yml
WARTE_MAX=900
fehler() { echo "FEHLER: $*"; echo "ESPO12_ERGEBNIS: FEHLER"; exit 1; }
# Bringt den Dienst wieder hoch, wenn der Rueckweg abgebrochen wird - eine stehende Instanz
# ist das schlechteste aller Ergebnisse.
dienst_zurueck() {
mountpoint -q /srv || mount /srv 2>/dev/null || true
[ -f "$COMPOSE" ] && docker compose -f "$COMPOSE" up -d --quiet-pull >/dev/null 2>&1 || true
}
# 1. Vorbedingungen.
[ "$(id -u)" -eq 0 ] || fehler "nicht als root gestartet"
command -v lvconvert >/dev/null || fehler "LVM-Werkzeuge fehlen"
command -v docker >/dev/null && docker info >/dev/null 2>&1 || fehler "Docker laeuft nicht"
# 2. Der Snapshot muss da und gueltig sein. Ein 'I' im Attributfeld heisst vollgelaufen und verworfen -
# dann traegt der Rueckweg nicht mehr, und ein Versuch waere schlimmer als ein ehrlicher Abbruch.
lvs --noheadings -o lv_name "$VG" 2>/dev/null | grep -qw "$SNAPSHOT" \
|| fehler "kein Snapshot '$SNAPSHOT' vorhanden - es gibt nichts zurueckzunehmen"
ATTR="$(lvs --noheadings -o lv_attr "$VG/$SNAPSHOT" 2>/dev/null | tr -d ' ')"
case "$ATTR" in
*I*) fehler "Snapshot '$SNAPSHOT' ist ungueltig (vollgelaufen) - der Rueckweg traegt nicht mehr" ;;
esac
echo "--- Snapshot"
lvs -o lv_name,lv_attr,origin,lv_size,data_percent "$VG/$SNAPSHOT" 2>/dev/null | sed 's/^/ /'
# 3. Stack anhalten. Solange Container laufen, haelt Docker /srv offen.
echo "--- Stack anhalten"
docker compose -f "$COMPOSE" down 2>&1 | tail -n 5
# 4. /srv aushaengen. Ist der Ursprung geoeffnet, verschiebt LVM das Zusammenfuehren auf die naechste
# Aktivierung - also auf einen Neustart. Wir wollen es hier, jetzt und nachpruefbar.
if ! umount /srv 2>/dev/null; then
echo "--- /srv ist belegt von:"
{ command -v fuser >/dev/null && fuser -vm /srv 2>&1 | head -n 20; } || echo " (fuser nicht verfuegbar)"
dienst_zurueck
fehler "/srv liess sich nicht aushaengen - es wurde nichts zurueckgenommen, der Dienst laeuft wieder"
fi
# 5. Zusammenfuehren.
if ! lvconvert --merge "$VG/$SNAPSHOT" >/dev/null 2>&1; then
dienst_zurueck
fehler "Zusammenfuehren fehlgeschlagen - Stand unveraendert, der Dienst laeuft wieder"
fi
echo "Snapshot zusammengefuehrt - /srv steht auf dem Stand vor dem Update."
# 6. Wieder einhaengen und mit der ebenfalls zurueckgenommenen Compose starten.
mount /srv || fehler "/srv liess sich nicht wieder einhaengen - Handarbeit noetig"
[ -f "$COMPOSE" ] || fehler "$COMPOSE fehlt nach dem Zurueckrollen - Handarbeit noetig"
START_AUSGABE="$(docker compose -f "$COMPOSE" up -d --quiet-pull 2>&1)" || {
echo "$START_AUSGABE" | tail -n 20
fehler "Start nach dem Zurueckrollen fehlgeschlagen"
}
# 7. Auf die Anwendung warten.
ENDE=$(( SECONDS + WARTE_MAX ))
while [ "$SECONDS" -lt "$ENDE" ]; do
[ "$(docker inspect -f '{{.State.Health.Status}}' espocrm 2>/dev/null)" = "healthy" ] && break
sleep 10
done
[ "$(docker inspect -f '{{.State.Health.Status}}' espocrm 2>/dev/null)" = "healthy" ] \
|| fehler "espocrm nach ${WARTE_MAX}s nicht gesund - 'docker logs espocrm' ansehen"
# 8. Ergebnis zeigen.
echo "--- Container"
docker compose -f "$COMPOSE" ps --format 'table {{.Name}}\t{{.Status}}'
echo "--- Instanz nach dem Rueckweg"
IMAGE="$(docker inspect -f '{{.Config.Image}}' espocrm 2>/dev/null)"
echo "Image: $IMAGE"
echo "Version: $(docker exec espocrm php -r '$v=""; foreach (["/var/www/html/application/Espo/Resources/defaults/config.php","/var/www/html/data/config.php","/var/www/html/data/config-internal.php"] as $f) { $a=@include $f; if (is_array($a) && !empty($a["version"])) { $v=$a["version"]; } } echo $v;' 2>/dev/null)"
echo "--- Hinweis"
echo "Der Snapshot ist verbraucht. Ein erneuter Rueckweg ist erst nach einem neuen ESPO-10 moeglich."
echo "Ursache des gescheiterten Updates klaeren, bevor es wiederholt wird."
echo "ESPO12_ERGEBNIS: OK"