feat(deploy): Unterstützung für Staging-Backups und -Wiederherstellungen
Erweitert die Backup- und Restore-Skripte um die Möglichkeit, Staging-Umgebungen zu unterstützen. Fügt die Option `-dest stage` hinzu, um spezifische Konfigurationen für Staging zu verwenden, einschließlich separater Docker-Compose-Dateien und Datenbankcontainer. Dokumentation aktualisiert, um manuelle Tests und Umgebungsvariablen für Staging zu reflektieren.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
Automatische und manuelle Sicherung von PostgreSQL, `.env`, `docker-compose.yml` und App-Code (Git-Archiv) auf der Prod-VM.
|
||||
|
||||
**Staging:** Kein automatisches Backup — Daten sind bewusst wegwerfbar. Deploy via `update-remotes.sh -dest stage` legt kein Backup an.
|
||||
**Staging:** Kein automatisches Backup — Daten sind bewusst wegwerfbar. Deploy via `update-remotes.sh -dest stage` legt kein Backup an. Zum manuellen Testen auf Staging: `-dest stage` (oder Auto-Fallback, wenn nur `daagbox-staging-db` läuft).
|
||||
|
||||
## Was wird gesichert?
|
||||
|
||||
@@ -36,6 +36,15 @@ cd /opt/kapteins-daagbok
|
||||
./scripts/backup.sh --reason manual --dry-run # Vorschau ohne Schreiben
|
||||
```
|
||||
|
||||
### Staging (manueller Test)
|
||||
|
||||
```bash
|
||||
cd /opt/kapteins-daagbok-staging
|
||||
./scripts/backup.sh -dest stage --reason manual
|
||||
# oder: Auto-Fallback, wenn nur daagbox-staging-db läuft
|
||||
./scripts/backup.sh --reason manual
|
||||
```
|
||||
|
||||
## Crontab (unbeaufsichtigt)
|
||||
|
||||
Beispiel: [`scripts/crontab.prod.example`](../../scripts/crontab.prod.example)
|
||||
@@ -94,9 +103,9 @@ Vor [`rotate-postgres-password.sh`](../../scripts/rotate-postgres-password.sh) e
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
| Variable | Default (Prod) |
|
||||
|----------|----------------|
|
||||
| `BACKUP_DIR` | `/var/backups/kapteins-daagbok` |
|
||||
| `COMPOSE_FILE` | `docker-compose.yml` |
|
||||
| `DB_CONTAINER` | `daagbox-prod-db` |
|
||||
| `RETENTION` | `5` |
|
||||
| Variable | Prod (default) | Staging (`-dest stage`) |
|
||||
|----------|----------------|-------------------------|
|
||||
| `COMPOSE_FILE` | `docker-compose.yml` | `docker-compose.staging.yml` |
|
||||
| `DB_CONTAINER` | `daagbox-prod-db` | `daagbox-staging-db` |
|
||||
| `BACKUP_DIR` | `/var/backups/kapteins-daagbok` | gleich |
|
||||
| `RETENTION` | `5` | `5` |
|
||||
|
||||
+50
-2
@@ -5,6 +5,7 @@
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/backup.sh
|
||||
# ./scripts/backup.sh -dest stage # Staging-Container (daagbox-staging-db)
|
||||
# ./scripts/backup.sh --reason cron
|
||||
# ./scripts/backup.sh --reason pre-deploy --tag v0.1.1.20
|
||||
# ./scripts/backup.sh --dry-run
|
||||
@@ -14,18 +15,40 @@
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/kapteins-daagbok}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||
DB_CONTAINER="${DB_CONTAINER:-daagbox-prod-db}"
|
||||
ENV_FILE="${ENV_FILE:-.env}"
|
||||
RETENTION="${RETENTION:-5}"
|
||||
DEST="prod"
|
||||
REASON="manual"
|
||||
EXPLICIT_TAG=""
|
||||
DRY_RUN=0
|
||||
COMPOSE_FILE=""
|
||||
DB_CONTAINER=""
|
||||
|
||||
apply_dest_config() {
|
||||
local dest="$1"
|
||||
local force="${2:-0}"
|
||||
if [[ "$dest" == "stage" ]]; then
|
||||
if [[ "$force" == "1" || -z "${COMPOSE_FILE}" ]]; then
|
||||
COMPOSE_FILE="docker-compose.staging.yml"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${DB_CONTAINER}" ]]; then
|
||||
DB_CONTAINER="daagbox-staging-db"
|
||||
fi
|
||||
else
|
||||
if [[ "$force" == "1" || -z "${COMPOSE_FILE}" ]]; then
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${DB_CONTAINER}" ]]; then
|
||||
DB_CONTAINER="daagbox-prod-db"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
sed -n '2,14p' "$0"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -dest prod|stage Target environment (default: prod)"
|
||||
echo " --reason cron|pre-deploy|manual Backup trigger (default: manual)"
|
||||
echo " --tag TAG Git tag label (e.g. v0.1.1.20 for pre-deploy)"
|
||||
echo " --dry-run Show actions without writing backup"
|
||||
@@ -34,6 +57,14 @@ usage() {
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-dest)
|
||||
DEST="${2:?-dest requires an argument}"
|
||||
shift 2
|
||||
;;
|
||||
-dest=*)
|
||||
DEST="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--reason)
|
||||
REASON="${2:?--reason requires an argument}"
|
||||
shift 2
|
||||
@@ -58,6 +89,16 @@ while [ $# -gt 0 ]; do
|
||||
esac
|
||||
done
|
||||
|
||||
case "$DEST" in
|
||||
prod|stage) ;;
|
||||
*)
|
||||
echo "Error: invalid -dest '$DEST' (use prod or stage)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
apply_dest_config "$DEST"
|
||||
|
||||
case "$REASON" in
|
||||
cron|pre-deploy|manual) ;;
|
||||
*)
|
||||
@@ -124,9 +165,15 @@ if [ -z "${POSTGRES_PASSWORD:-}" ]; then
|
||||
fi
|
||||
|
||||
if ! docker inspect "$DB_CONTAINER" >/dev/null 2>&1; then
|
||||
if [[ "$DEST" == "prod" ]] && docker inspect daagbox-staging-db >/dev/null 2>&1; then
|
||||
echo "Note: $DB_CONTAINER not found — falling back to staging (daagbox-staging-db). Use -dest stage explicitly."
|
||||
apply_dest_config stage 1
|
||||
DEST="stage"
|
||||
else
|
||||
echo "Error: DB container '$DB_CONTAINER' not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "$DB_CONTAINER")" != "true" ]; then
|
||||
echo "Error: DB container '$DB_CONTAINER' is not running" >&2
|
||||
@@ -173,6 +220,7 @@ from datetime import datetime, timezone
|
||||
manifest = {
|
||||
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"local_timestamp": "${TIMESTAMP}",
|
||||
"destination": "${DEST}",
|
||||
"reason": "${REASON}",
|
||||
"git_tag": "${GIT_TAG}",
|
||||
"git_sha": "${GIT_SHA}",
|
||||
|
||||
@@ -3,21 +3,46 @@
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/restore-backup.sh --list
|
||||
# ./scripts/restore-backup.sh -dest stage --restore PATH
|
||||
# ./scripts/restore-backup.sh --restore /var/backups/kapteins-daagbok/kapteins-daagbok_....tar.gz
|
||||
# ./scripts/restore-backup.sh --restore PATH --db-only
|
||||
# ./scripts/restore-backup.sh --restore PATH --env-only
|
||||
# ./scripts/restore-backup.sh --restore PATH --full --yes
|
||||
#
|
||||
# Environment overrides:
|
||||
# BACKUP_DIR, COMPOSE_FILE, DB_CONTAINER, BACKEND_CONTAINER, ENV_FILE
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/kapteins-daagbok}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||
DB_CONTAINER="${DB_CONTAINER:-daagbox-prod-db}"
|
||||
BACKEND_CONTAINER="${BACKEND_CONTAINER:-daagbox-prod-backend}"
|
||||
ENV_FILE="${ENV_FILE:-.env}"
|
||||
MAX_WAIT=90
|
||||
DEST="prod"
|
||||
COMPOSE_FILE=""
|
||||
DB_CONTAINER=""
|
||||
BACKEND_CONTAINER=""
|
||||
|
||||
apply_dest_config() {
|
||||
local dest="$1"
|
||||
local force="${2:-0}"
|
||||
if [[ "$dest" == "stage" ]]; then
|
||||
if [[ "$force" == "1" || -z "${COMPOSE_FILE}" ]]; then
|
||||
COMPOSE_FILE="docker-compose.staging.yml"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${DB_CONTAINER}" ]]; then
|
||||
DB_CONTAINER="daagbox-staging-db"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${BACKEND_CONTAINER}" ]]; then
|
||||
BACKEND_CONTAINER="daagbox-staging-backend"
|
||||
fi
|
||||
else
|
||||
if [[ "$force" == "1" || -z "${COMPOSE_FILE}" ]]; then
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${DB_CONTAINER}" ]]; then
|
||||
DB_CONTAINER="daagbox-prod-db"
|
||||
fi
|
||||
if [[ "$force" == "1" || -z "${BACKEND_CONTAINER}" ]]; then
|
||||
BACKEND_CONTAINER="daagbox-prod-backend"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
MODE="full"
|
||||
RESTORE_PATH=""
|
||||
@@ -28,6 +53,7 @@ usage() {
|
||||
sed -n '2,10p' "$0"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -dest prod|stage Target environment (default: prod)"
|
||||
echo " --list List available backups"
|
||||
echo " --restore PATH Backup archive to restore"
|
||||
echo " --full Restore DB + .env (default)"
|
||||
@@ -48,6 +74,14 @@ confirm() {
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-dest)
|
||||
DEST="${2:?-dest requires an argument}"
|
||||
shift 2
|
||||
;;
|
||||
-dest=*)
|
||||
DEST="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--list)
|
||||
LIST=1
|
||||
shift
|
||||
@@ -84,6 +118,16 @@ while [ $# -gt 0 ]; do
|
||||
esac
|
||||
done
|
||||
|
||||
case "$DEST" in
|
||||
prod|stage) ;;
|
||||
*)
|
||||
echo "Error: invalid -dest '$DEST' (use prod or stage)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
apply_dest_config "$DEST"
|
||||
|
||||
list_backups() {
|
||||
if [ ! -d "$BACKUP_DIR" ]; then
|
||||
echo "No backup directory: $BACKUP_DIR"
|
||||
@@ -138,9 +182,42 @@ echo "Backup manifest:"
|
||||
python3 -m json.tool "$MANIFEST"
|
||||
echo ""
|
||||
|
||||
GIT_TAG="$(python3 -c "import json; print(json.load(open('$MANIFEST'))['git_tag'])")"
|
||||
GIT_SHA="$(python3 -c "import json; print(json.load(open('$MANIFEST'))['git_sha'])")"
|
||||
BACKUP_TS="$(python3 -c "import json; print(json.load(open('$MANIFEST'))['local_timestamp'])")"
|
||||
read_manifest_field() {
|
||||
python3 -c "import json; print(json.load(open('$MANIFEST')).get('$1', '') or '')"
|
||||
}
|
||||
|
||||
MANIFEST_COMPOSE="$(read_manifest_field compose_file)"
|
||||
MANIFEST_DB="$(read_manifest_field db_container)"
|
||||
MANIFEST_DEST="$(read_manifest_field destination)"
|
||||
|
||||
if [ -n "$MANIFEST_COMPOSE" ]; then
|
||||
COMPOSE_FILE="$MANIFEST_COMPOSE"
|
||||
fi
|
||||
if [ -n "$MANIFEST_DB" ]; then
|
||||
DB_CONTAINER="$MANIFEST_DB"
|
||||
fi
|
||||
if [ -n "$MANIFEST_DEST" ]; then
|
||||
if [[ "$MANIFEST_DEST" == "stage" ]]; then
|
||||
BACKEND_CONTAINER="${BACKEND_CONTAINER:-daagbox-staging-backend}"
|
||||
else
|
||||
BACKEND_CONTAINER="${BACKEND_CONTAINER:-daagbox-prod-backend}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! docker inspect "$DB_CONTAINER" >/dev/null 2>&1; then
|
||||
if [[ "$DEST" == "prod" ]] && docker inspect daagbox-staging-db >/dev/null 2>&1; then
|
||||
echo "Note: $DB_CONTAINER not found — falling back to staging (daagbox-staging-db). Use -dest stage explicitly."
|
||||
apply_dest_config stage 1
|
||||
DEST="stage"
|
||||
else
|
||||
echo "Error: DB container '$DB_CONTAINER' not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
GIT_TAG="$(read_manifest_field git_tag)"
|
||||
GIT_SHA="$(read_manifest_field git_sha)"
|
||||
BACKUP_TS="$(read_manifest_field local_timestamp)"
|
||||
|
||||
if ! confirm "Restore backup from $BACKUP_TS (tag: $GIT_TAG)? Mode: $MODE"; then
|
||||
echo "Aborted."
|
||||
|
||||
Reference in New Issue
Block a user