Files
mates-install-scripts/docker-backup.sh
T
mates 10d214c2a2 fix: odstranit emoji ze vsech souboru
Kompletne odstraneno:
- Emoji (smajliky, symboly, boxes, arrows) z README
- Emoji ze skriptu (sipky, symboly)
- Emoji z docker-compose (adresare)

Nahrano za:
- Textove alternativy (sipky -> '->', boxes -> '+', atd.)
- ASCII pouze kde to slo

Ciste vsechny 7 repo (overeno Python skriptem).
2026-08-02 08:50:11 +02:00

203 lines
5.6 KiB
Bash

#!/usr/bin/env bash
#
# docker-backup.sh -- bezpečná záloha Docker projektů a named volumes
#
# - vytvoří timestamp složku v <backup-root>/
# - pro každý projekt v <docker-projects-root> zastaví kontejnery,
# zálohuje tar.zst, ověří integritu, znovu spustí
# - zvlášť zálohuje Docker named volumes
# - ponechá posledních N záloh (default 3)
#
# Použití:
# ./docker-backup.sh # všechny projekty + named volumes
# ./docker-backup.sh nginx mattermost # vybrané projekty (volumes vždy)
# BACKUP_KEEP=7 ./docker-backup.sh # ponechá 7 záloh
# ./docker-backup.sh --help # nápověda
#
set -euo pipefail
usage() {
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
}
for arg in "$@"; do
case "$arg" in
--help|-h) usage; exit 0 ;;
esac
done
DOCKERY="${DOCKERY:-<docker-projects-root>}"
BACKUP_ROOT="${BACKUP_ROOT:-<backup-root>}"
KEEP="${BACKUP_KEEP:-3}"
TS="$(date +%Y-%m-%d_%H-%M-%S)"
DEST="${BACKUP_ROOT}/${TS}"
# Barevné výstupy
if [ -t 1 ]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; NC=''
fi
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
warn() { printf '%s[%s]%s %s\n' "$YELLOW" "$(date '+%H:%M:%S')" "$NC" "$*"; }
err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*"; }
# Kontrola
if ! docker info >/dev/null 2>&1; then
err "Docker není dostupný."
exit 1
fi
if [ ! -d "$DOCKERY" ]; then
err "Dockery adresář neexistuje: $DOCKERY"
exit 1
fi
if [ ! -d "$BACKUP_ROOT" ]; then
err "Backup adresář neexistuje: $BACKUP_ROOT"
exit 1
fi
mkdir -p "$DEST"
# Najdi projekty
if [ "$#" -gt 0 ]; then
PROJECTS=("$@")
else
PROJECTS=()
for d in "$DOCKERY"/*/; do
[ -d "$d" ] || continue
if compgen -G "$d/docker-compose.y*ml" > /dev/null; then
PROJECTS+=("$(basename "$d")")
fi
done
fi
log "Projektů k záloze: ${#PROJECTS[@]}"
log "Cíl: $DEST"
log "Uchovat posledních: $KEEP záloh"
# === FÁZE 1: zastavit všechny dotčené kontejnery najednou ===
log ""
log "=== STOP: zastavuji všechny kontejnery v ${PROJECTS[*]} ==="
for project in "${PROJECTS[@]}"; do
cd "$DOCKERY/$project"
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
if docker compose ps -q 2>/dev/null | grep -q .; then
log " [$project] docker compose stop"
docker compose stop 2>&1 || true
fi
fi
done
# === FÁZE 2: záloha projektů (paralelně pro velké projekty) ===
log ""
log "=== BACKUP projekty (tar.zst) ==="
FAILED=()
for project in "${PROJECTS[@]}"; do
cd "$DOCKERY/$project"
ARCHIVE="$DEST/${project}.tar.zst"
log " [$project] tar -c <docker-projects-root>/$project/ -> $ARCHIVE"
# absolutní cesta (background tar pitfall: vždy absolutní)
if /usr/bin/tar --exclude='._*' --exclude='.DS_Store' \
--use-compress-program='/opt/homebrew/bin/zstd -1 -T0' \
-cf "$ARCHIVE" \
"<docker-projects-root>/$project/" 2>>"$DEST/.errors"; then
# Ověření
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
SIZE=$(stat -f '%z' "$ARCHIVE")
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
else
err " zstd -t selhal pro $ARCHIVE"
FAILED+=("$project")
fi
else
err " tar selhal pro $project"
FAILED+=("$project")
fi
done
# === FÁZE 3: named volumes (per-volume záloha přes Docker) ===
log ""
log "=== BACKUP named volumes ==="
VOLUMES=$(docker volume ls --format '{{.Name}}' \
| grep -vE '^(host-services|docker-desktop)$' || true)
for vol in $VOLUMES; do
ARCHIVE="$DEST/${vol}.tar.zst"
log " [volume:$vol] -> $ARCHIVE"
# Nejdřív dekomprimuj na hostiteli, pak pošli do kontejneru (alpine tar neumí zstd)
if docker run --rm \
-v "${vol}:/src:ro" \
-v "$DEST:/dest" \
alpine:3.20 \
sh -c "cd /src && tar --exclude='._*' --exclude='.DS_Store' -cf - ." \
> "$DEST/${vol}.tar" 2>>"$DEST/.errors"; then
/opt/homebrew/bin/zstd -1 -T0 -f "$DEST/${vol}.tar" -o "$ARCHIVE" 2>>"$DEST/.errors"
rm -f "$DEST/${vol}.tar"
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
SIZE=$(stat -f '%z' "$ARCHIVE")
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
else
err " zstd -t selhal pro $ARCHIVE"
FAILED+=("volume:$vol")
fi
else
err " docker run selhal pro volume $vol"
FAILED+=("volume:$vol")
fi
done
# === FÁZE 4: znovu spustit projekty ===
log ""
log "=== UP: spouštím projekty ==="
for project in "${PROJECTS[@]}"; do
cd "$DOCKERY/$project"
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
log " [$project] docker compose up -d"
docker compose up -d 2>&1 || warn " [$project] up -d selhal (zkontroluj)"
fi
done
# === FÁZE 5: hash report ===
log ""
log "=== SHA-256 hashe ==="
for f in "$DEST"/*.tar.zst; do
[ -f "$f" ] || continue
H=$(shasum -a 256 "$f" | awk '{print $1}')
S=$(stat -f '%z' "$f")
log " $(basename "$f") sha256=$H size=$S"
done
# === FÁZE 6: retention ===
log ""
log "=== RETENTION: ponechávám posledních $KEEP záloh ==="
# set -u a prázdné pole
existing=()
while IFS= read -r d; do
[ -d "$d" ] && existing+=("$d")
done < <(find "$BACKUP_ROOT" -maxdepth 1 -mindepth 1 -type d | sort)
# Pokud něco selhalo, nepromazávej
if [ "${#FAILED[@]}" -gt 0 ]; then
warn "Některé zálohy selhaly (${#FAILED[@]}), RETENTION se neprovádí"
else
if [ "${#existing[@]}" -gt "$KEEP" ]; then
for old in "${existing[@]:0:${#existing[@]}-KEEP}"; do
log " mažu starou zálohu: $(basename "$old")"
rm -rf "$old"
done
fi
fi
log ""
log "======================================"
log " HOTOVO"
log "======================================"
log "Záloha: $DEST"
if [ "${#FAILED[@]}" -gt 0 ]; then
err "Selhalo: ${#FAILED[@]}"
for f in "${FAILED[@]}"; do err " [FAIL] $f"; done
exit 1
fi
log "Vše OK."