This commit is contained in:
Jun-te Kim 2025-12-28 15:37:52 +00:00
parent 6328dee08d
commit 4f589ea44e
2 changed files with 99 additions and 64 deletions

View file

@ -48,11 +48,16 @@ jobs:
aws sts get-caller-identity aws sts get-caller-identity
- name: Run backup - name: Run DEV backup
run: | run: |
ENVIRONMENT=dev \
bash mist_infra/scripts/backup_k8s_storage_to_s3.sh bash mist_infra/scripts/backup_k8s_storage_to_s3.sh
- name: Run PROD backup
run: |
ENVIRONMENT=prod \
I_UNDERSTAND_THIS_IS_PROD=true \
bash mist_infra/scripts/backup_k8s_storage_to_s3.sh
# example of restoring a back up # example of restoring a back up
# aws s3 cp s3://mist-backups/2025-03-09/k8s_storage_mist_2025-03-09_02-30-01.tar.gz . # aws s3 cp s3://mist-backups/2025-03-09/k8s_storage_mist_2025-03-09_02-30-01.tar.gz .

View file

@ -1,68 +1,103 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# ----------------------------- # ==================================================
# Config # REQUIRED ENV VARS
# ----------------------------- # ==================================================
ENVIRONMENT="${ENVIRONMENT:-}"
if [[ -z "$ENVIRONMENT" ]]; then
echo "❌ ENVIRONMENT must be set to dev or prod"
exit 1
fi
# ==================================================
# GLOBAL CONFIG
# ==================================================
NAMESPACE="default" NAMESPACE="default"
PG_ENABLED=true
PG_SECRET_NAME="postgres-secret"
PG_POD_SELECTOR="app=postgres"
K8S_STORAGE_ROOT="/k8s_storage" K8S_STORAGE_ROOT="/k8s_storage"
# NEVER touch Postgres raw data
TAR_EXCLUDES=(
"$K8S_STORAGE_ROOT/postgres"
)
BACKUP_ROOT="/tmp/k8s-backups" BACKUP_ROOT="/tmp/k8s-backups"
DATE="$(date -u +%Y-%m-%d_%H-%M-%S)" DATE="$(date -u +%Y-%m-%d_%H-%M-%S)"
BACKUP_DIR="$BACKUP_ROOT/$DATE" BACKUP_DIR="$BACKUP_ROOT/$DATE"
mkdir -p "$BACKUP_DIR" mkdir -p "$BACKUP_DIR"
echo "=== Backup started at $(date -u) ===" # NEVER touch raw Postgres data
TAR_EXCLUDES=(
"$K8S_STORAGE_ROOT/postgres"
)
# ----------------------------- # ==================================================
# Postgres pg_dump (SAFE) # ENVIRONMENT SWITCH
# ----------------------------- # ==================================================
if [[ "$PG_ENABLED" == "true" ]]; then case "$ENVIRONMENT" in
echo "=== Postgres pg_dump enabled ===" dev)
PG_SECRET_NAME="postgres-secret"
PG_POD_SELECTOR="app=postgres-dev"
S3_PREFIX="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
POSTGRES_POD=$(kubectl get pods \ PG_SECRET_NAME="postgres-prod-secret"
PG_POD_SELECTOR="app=postgres-prod"
S3_PREFIX="prod"
;;
*)
echo "❌ Invalid ENVIRONMENT: $ENVIRONMENT (must be dev or prod)"
exit 1
;;
esac
echo "=== Backup started ($(date -u)) ==="
echo "Environment: $ENVIRONMENT"
# ==================================================
# POSTGRES DUMP (SAFE)
# ==================================================
POSTGRES_POD=$(kubectl get pods \
-n "$NAMESPACE" \ -n "$NAMESPACE" \
-l "$PG_POD_SELECTOR" \ -l "$PG_POD_SELECTOR" \
-o jsonpath='{.items[*].metadata.name}' | awk '{print $1}') -o jsonpath='{.items[*].metadata.name}' | awk '{print $1}')
if [[ -z "$POSTGRES_POD" ]]; then if [[ -z "$POSTGRES_POD" ]]; then
echo "❌ No Postgres pod found" echo "❌ No Postgres pod found for selector: $PG_POD_SELECTOR"
kubectl get pods -n "$NAMESPACE" kubectl get pods -n "$NAMESPACE"
exit 1 exit 1
fi fi
POSTGRES_USER=$(kubectl get secret "$PG_SECRET_NAME" \ POSTGRES_USER=$(kubectl get secret "$PG_SECRET_NAME" \
-n "$NAMESPACE" \ -n "$NAMESPACE" \
-o jsonpath='{.data.POSTGRES_USER}' | base64 -d) -o jsonpath='{.data.POSTGRES_USER}' | base64 -d)
POSTGRES_DB=$(kubectl get secret "$PG_SECRET_NAME" \ POSTGRES_DB=$(kubectl get secret "$PG_SECRET_NAME" \
-n "$NAMESPACE" \ -n "$NAMESPACE" \
-o jsonpath='{.data.POSTGRES_DB}' | base64 -d) -o jsonpath='{.data.POSTGRES_DB}' 2>/dev/null | base64 -d || true)
echo "Dumping database: $POSTGRES_DB" if [[ -z "$POSTGRES_DB" ]]; then
echo "❌ POSTGRES_DB missing in secret $PG_SECRET_NAME"
kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \ exit 1
pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
> "$BACKUP_DIR/postgres.sql"
echo "✔ pg_dump complete ($(du -h "$BACKUP_DIR/postgres.sql" | cut -f1))"
fi fi
# ----------------------------- echo "Dumping database: $POSTGRES_DB"
# Normalise permissions (EXCLUDING POSTGRES)
# ----------------------------- kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \
echo "=== Normalising permissions (excluding Postgres) ===" pg_dump \
-h localhost \
-U "$POSTGRES_USER" \
"$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" \ sudo find "$K8S_STORAGE_ROOT" \
-mindepth 1 \ -mindepth 1 \
@ -70,19 +105,14 @@ sudo find "$K8S_STORAGE_ROOT" \
! -name postgres \ ! -name postgres \
-exec chmod -R a+rX {} \; || true -exec chmod -R a+rX {} \; || true
# ----------------------------- # ==================================================
# Build tar exclude args # ARCHIVE K8S STORAGE (SAFE)
# ----------------------------- # ==================================================
TAR_EXCLUDE_ARGS=() TAR_EXCLUDE_ARGS=()
for path in "${TAR_EXCLUDES[@]}"; do for path in "${TAR_EXCLUDES[@]}"; do
TAR_EXCLUDE_ARGS+=(--exclude="$path") TAR_EXCLUDE_ARGS+=(--exclude="$path")
done done
# -----------------------------
# Archive k8s storage (SAFE)
# -----------------------------
echo "=== Archiving k8s storage ==="
tar \ tar \
--ignore-failed-read \ --ignore-failed-read \
--warning=no-file-changed \ --warning=no-file-changed \
@ -92,21 +122,21 @@ tar \
echo "✔ k8s_storage archived ($(du -h "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" | cut -f1))" echo "✔ k8s_storage archived ($(du -h "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" | cut -f1))"
# ----------------------------- # ==================================================
# Upload to S3 # UPLOAD TO S3
# ----------------------------- # ==================================================
S3_BUCKET="s3://mist-backups/$DATE" S3_BUCKET="s3://mist-backups/$S3_PREFIX/$DATE"
aws s3 cp "$BACKUP_DIR" "$S3_BUCKET" --recursive aws s3 cp "$BACKUP_DIR" "$S3_BUCKET" --recursive
echo "✔ Uploaded to $S3_BUCKET" echo "✔ Uploaded to $S3_BUCKET"
# ----------------------------- # ==================================================
# Restore instructions # RESTORE GUIDE
# ----------------------------- # ==================================================
echo "" echo ""
echo "========================================" echo "========================================"
echo "=== RESTORE GUIDE" echo "=== RESTORE GUIDE ($ENVIRONMENT)"
echo "========================================" echo "========================================"
echo "" echo ""
echo "Restore volumes:" echo "Restore volumes:"