juntekim.com/mist_infra/scripts/backup_k8s_storage_to_s3.sh
2026-02-08 19:39:45 +00:00

162 lines
4.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# ==================================================
# REQUIRED ENV VARS
# ==================================================
ENVIRONMENT="${ENVIRONMENT:-}"
if [[ -z "$ENVIRONMENT" ]]; then
echo "❌ ENVIRONMENT must be set to dev or prod"
exit 1
fi
# ==================================================
# GLOBAL CONFIG
# ==================================================
K8S_STORAGE_ROOT="/k8s_storage"
BACKUP_ROOT="/tmp/k8s-backups"
DATE="$(date -u +%Y-%m-%d_%H-%M-%S)"
BACKUP_DIR="$BACKUP_ROOT/$DATE"
mkdir -p "$BACKUP_DIR"
# NEVER touch raw Postgres data
TAR_EXCLUDES=(
"$K8S_STORAGE_ROOT/postgres"
"$K8S_STORAGE_ROOT/lost+found"
)
# ==================================================
# ENVIRONMENT SWITCH
# ==================================================
case "$ENVIRONMENT" in
dev)
PG_SECRET_NAME="postgres-dev"
PG_POD_SELECTOR="app=postgres"
S3_PREFIX="dev"
NAMESPACE="dev"
;;
prod)
if [[ "${I_UNDERSTAND_THIS_IS_PROD:-}" != "true" ]]; then
echo "❌ Refusing to run PROD backup without confirmation"
echo " Re-run with: I_UNDERSTAND_THIS_IS_PROD=true"
exit 1
fi
PG_SECRET_NAME="postgres-prod"
PG_POD_SELECTOR="app=postgres"
S3_PREFIX="prod"
NAMESPACE="default"
;;
*)
echo "❌ Invalid ENVIRONMENT: $ENVIRONMENT (must be dev or prod)"
exit 1
;;
esac
echo "=== Backup started ($(date -u)) ==="
echo "Environment: $ENVIRONMENT"
echo "Namespace: $NAMESPACE"
# ==================================================
# LOCATE POSTGRES POD
# ==================================================
POSTGRES_POD=$(kubectl get pods \
-n "$NAMESPACE" \
-l "$PG_POD_SELECTOR" \
-o jsonpath='{.items[0].metadata.name}')
if [[ -z "$POSTGRES_POD" ]]; then
echo "❌ No Postgres pod found for selector: $PG_POD_SELECTOR"
kubectl get pods -n "$NAMESPACE"
exit 1
fi
echo "Using Postgres pod: $POSTGRES_POD"
# ==================================================
# READ DATABASE_URL FROM SECRET
# ==================================================
DATABASE_URL=$(kubectl get secret "$PG_SECRET_NAME" \
-n "$NAMESPACE" \
-o jsonpath='{.data.DATABASE_URL}' | base64 -d)
if [[ -z "$DATABASE_URL" ]]; then
echo "❌ DATABASE_URL missing in secret $PG_SECRET_NAME"
exit 1
fi
# Parse DATABASE_URL
POSTGRES_USER="$(echo "$DATABASE_URL" | sed -E 's|.*://([^:]+):.*|\1|')"
POSTGRES_DB="$(echo "$DATABASE_URL" | sed -E 's|.*/([^?]+).*|\1|')"
if [[ -z "$POSTGRES_USER" || -z "$POSTGRES_DB" ]]; then
echo "❌ Failed to parse DATABASE_URL"
exit 1
fi
echo "Dumping database: $POSTGRES_DB (user: $POSTGRES_USER)"
# ==================================================
# POSTGRES LOGICAL DUMP (SAFE)
# ==================================================
kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \
pg_dump "$POSTGRES_DB" \
> "$BACKUP_DIR/postgres.sql"
echo "✔ pg_dump complete ($(du -h "$BACKUP_DIR/postgres.sql" | cut -f1))"
# ==================================================
# NORMALISE PERMISSIONS (EXCLUDING POSTGRES)
# ==================================================
echo "Normalising permissions (excluding Postgres data)..."
sudo find "$K8S_STORAGE_ROOT" \
-mindepth 1 \
-maxdepth 1 \
! -name postgres \
-exec chmod -R a+rX {} \; || true
# ==================================================
# ARCHIVE K8S STORAGE (SAFE)
# ==================================================
TAR_EXCLUDE_ARGS=()
for path in "${TAR_EXCLUDES[@]}"; do
TAR_EXCLUDE_ARGS+=(--exclude="$path")
done
tar \
--ignore-failed-read \
--warning=no-file-changed \
-czf "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" \
"${TAR_EXCLUDE_ARGS[@]}" \
"$K8S_STORAGE_ROOT"
echo "✔ k8s_storage archived ($(du -h "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" | cut -f1))"
# ==================================================
# UPLOAD TO S3
# ==================================================
S3_BUCKET="s3://mist-backups/$S3_PREFIX/$DATE"
aws s3 cp "$BACKUP_DIR" "$S3_BUCKET" --recursive
echo "✔ Uploaded to $S3_BUCKET"
# ==================================================
# RESTORE GUIDE
# ==================================================
echo ""
echo "========================================"
echo "=== RESTORE GUIDE ($ENVIRONMENT)"
echo "========================================"
echo ""
echo "Restore volumes:"
echo " sudo tar -xzf k8s_storage_$DATE.tar.gz -C /"
echo ""
echo "Restore Postgres:"
echo " kubectl exec -n $NAMESPACE -i $POSTGRES_POD -- \\"
echo " psql $POSTGRES_DB < postgres.sql"
echo ""
echo "=== Backup completed successfully ==="