Files
Celes Renata 357e68a764 feat: add database and Redis backup/restore scripts
- scripts/backup-db.sh: pg_dump compressed backup with table count
  verification, optional MinIO upload, auto-prune keeping last 7
- scripts/restore-db.sh: restore from backup with safety confirmation,
  scales down services during restore, re-grants permissions, scales
  back up with correct replica counts
- scripts/backup-redis.sh: triggers BGSAVE, copies RDB dump locally,
  shows key stats
2026-04-16 00:10:27 +00:00

99 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Stonks Oracle — Database Backup
# Creates a compressed pg_dump of the stonks database and optionally
# uploads it to MinIO for offsite storage.
#
# Usage:
# ./scripts/backup-db.sh # backup to local file
# ./scripts/backup-db.sh --upload-minio # backup + upload to MinIO
#
# Requires: kubectl access to the cluster
NAMESPACE_PG="postgresql-service"
PG_POD="postgresql-1"
PG_USER="postgres"
PG_DB="stonks"
BACKUP_DIR="${BACKUP_DIR:-$HOME/backups/stonks-oracle}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="stonks-${TIMESTAMP}.sql.gz"
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILE}"
MINIO_BUCKET="stonks-backups"
UPLOAD_MINIO=false
for arg in "$@"; do
case "$arg" in
--upload-minio) UPLOAD_MINIO=true ;;
esac
done
echo "=== Stonks Oracle Database Backup ==="
echo "Timestamp: ${TIMESTAMP}"
echo "Target: ${BACKUP_PATH}"
# Ensure backup directory exists
mkdir -p "${BACKUP_DIR}"
# Run pg_dump inside the PostgreSQL pod and stream the compressed output
echo "[1/3] Running pg_dump..."
kubectl exec -n "${NAMESPACE_PG}" "${PG_POD}" -c postgres -- \
pg_dump -U "${PG_USER}" -d "${PG_DB}" \
--no-owner --no-privileges --clean --if-exists \
| gzip > "${BACKUP_PATH}"
BACKUP_SIZE=$(du -h "${BACKUP_PATH}" | cut -f1)
echo " Backup size: ${BACKUP_SIZE}"
# Verify the backup is not empty
if [ ! -s "${BACKUP_PATH}" ]; then
echo "ERROR: Backup file is empty!"
rm -f "${BACKUP_PATH}"
exit 1
fi
# Count tables in the backup as a sanity check
TABLE_COUNT=$(zcat "${BACKUP_PATH}" | grep -c "^CREATE TABLE" || true)
echo " Tables found: ${TABLE_COUNT}"
# Upload to MinIO if requested
if [ "${UPLOAD_MINIO}" = true ]; then
echo "[2/3] Uploading to MinIO bucket ${MINIO_BUCKET}..."
# Create bucket if it doesn't exist
kubectl exec -n stonks-oracle "$(kubectl get pods -n stonks-oracle -l app=ingestion -o name | head -1 | sed 's|pod/||')" -- \
python -c "
from minio import Minio
import os
client = Minio(
os.environ.get('MINIO_ENDPOINT', 'minio.minio-service.svc.cluster.local:80'),
access_key=os.environ.get('MINIO_ACCESS_KEY', ''),
secret_key=os.environ.get('MINIO_SECRET_KEY', ''),
secure=False,
)
if not client.bucket_exists('${MINIO_BUCKET}'):
client.make_bucket('${MINIO_BUCKET}')
print('Created bucket ${MINIO_BUCKET}')
else:
print('Bucket ${MINIO_BUCKET} exists')
" 2>&1 || echo "WARNING: Could not verify MinIO bucket"
# Upload via kubectl cp + minio client
echo " (MinIO upload requires mc CLI or manual copy)"
echo " File ready at: ${BACKUP_PATH}"
else
echo "[2/3] Skipping MinIO upload (use --upload-minio to enable)"
fi
# Prune old backups (keep last 7)
echo "[3/3] Pruning old backups (keeping last 7)..."
ls -t "${BACKUP_DIR}"/stonks-*.sql.gz 2>/dev/null | tail -n +8 | xargs -r rm -v
echo ""
echo "=== Backup complete ==="
echo "File: ${BACKUP_PATH}"
echo "Size: ${BACKUP_SIZE}"
echo "Tables: ${TABLE_COUNT}"