138 lines
5.3 KiB
Bash
Executable File
138 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Stonks Oracle Backup — PostgreSQL + MinIO → NFS
|
|
# Usage: bash scripts/backup.sh
|
|
set -euo pipefail
|
|
|
|
NAMESPACE="stonks-oracle"
|
|
NFS_SERVER="192.168.42.8"
|
|
NFS_PATH="/volume1/Kubernetes/stonks"
|
|
BACKUP_NAME="stonks-backup-$(date +%Y%m%d-%H%M%S)"
|
|
JOB_NAME="stonks-backup"
|
|
|
|
echo "=== Stonks Oracle Backup: ${BACKUP_NAME} ==="
|
|
|
|
# Clean up any previous backup job
|
|
kubectl delete job ${JOB_NAME} -n ${NAMESPACE} --ignore-not-found=true 2>/dev/null || true
|
|
|
|
cat <<EOJOB | kubectl apply -f -
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: ${JOB_NAME}
|
|
namespace: ${NAMESPACE}
|
|
spec:
|
|
ttlSecondsAfterFinished: 300
|
|
backoffLimit: 0
|
|
template:
|
|
spec:
|
|
restartPolicy: Never
|
|
volumes:
|
|
- name: nfs-backup
|
|
nfs:
|
|
server: ${NFS_SERVER}
|
|
path: ${NFS_PATH}
|
|
containers:
|
|
- name: backup
|
|
image: postgres:18-alpine
|
|
volumeMounts:
|
|
- name: nfs-backup
|
|
mountPath: /backup
|
|
envFrom:
|
|
- configMapRef:
|
|
name: stonks-config
|
|
- secretRef:
|
|
name: stonks-core-secrets
|
|
env:
|
|
- name: MINIO_ACCESS_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: stonks-core-secrets
|
|
key: MINIO_ACCESS_KEY
|
|
- name: MINIO_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: stonks-core-secrets
|
|
key: MINIO_SECRET_KEY
|
|
command: ["sh", "-c"]
|
|
args:
|
|
- |
|
|
set -e
|
|
apk add --no-cache curl ca-certificates
|
|
|
|
STAMP="${BACKUP_NAME}"
|
|
DIR="/backup/\${STAMP}"
|
|
mkdir -p "\${DIR}/minio"
|
|
|
|
echo "[1/3] Backing up PostgreSQL..."
|
|
PGPASSWORD="\${POSTGRES_PASSWORD}" pg_dump \
|
|
-h "\${POSTGRES_HOST}" -p "\${POSTGRES_PORT}" \
|
|
-U "\${POSTGRES_USER}" -d "\${POSTGRES_DB}" \
|
|
--no-owner --no-acl -Fc \
|
|
-f "\${DIR}/stonks.pgdump"
|
|
echo " pg_dump done: \$(du -h "\${DIR}/stonks.pgdump" | cut -f1)"
|
|
|
|
echo "[2/3] Backing up MinIO buckets..."
|
|
# Install mc (MinIO client)
|
|
curl -sL https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc
|
|
chmod +x /usr/local/bin/mc
|
|
mc alias set backup "http://\${MINIO_ENDPOINT}" "\${MINIO_ACCESS_KEY}" "\${MINIO_SECRET_KEY}" --api S3v4 2>/dev/null
|
|
|
|
for bucket in stonks-raw-market stonks-raw-news stonks-raw-filings stonks-normalized stonks-llm-prompts stonks-llm-results stonks-lakehouse stonks-audit; do
|
|
if mc ls "backup/\${bucket}" >/dev/null 2>&1; then
|
|
COUNT=\$(mc ls --recursive "backup/\${bucket}" 2>/dev/null | wc -l)
|
|
if [ "\${COUNT}" -gt 0 ]; then
|
|
echo " \${bucket}: \${COUNT} objects, mirroring..."
|
|
mc mirror "backup/\${bucket}" "\${DIR}/minio/\${bucket}/" 2>&1 | tail -1
|
|
SIZE=\$(du -sh "\${DIR}/minio/\${bucket}/" 2>/dev/null | cut -f1)
|
|
echo " \${bucket}: done (\${SIZE})"
|
|
else
|
|
echo " \${bucket}: empty, skipping"
|
|
fi
|
|
else
|
|
echo " \${bucket}: not found, skipping"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "=== Backup Summary ==="
|
|
echo " PostgreSQL: \$(du -h "\${DIR}/stonks.pgdump" | cut -f1)"
|
|
MINIO_TOTAL=\$(du -sh "\${DIR}/minio/" 2>/dev/null | cut -f1)
|
|
echo " MinIO total: \${MINIO_TOTAL}"
|
|
for bucket in stonks-raw-market stonks-raw-news stonks-raw-filings stonks-normalized stonks-llm-prompts stonks-llm-results stonks-lakehouse stonks-audit; do
|
|
if [ -d "\${DIR}/minio/\${bucket}" ]; then
|
|
BSIZE=\$(du -sh "\${DIR}/minio/\${bucket}/" 2>/dev/null | cut -f1)
|
|
BCOUNT=\$(find "\${DIR}/minio/\${bucket}/" -type f 2>/dev/null | wc -l)
|
|
echo " \${bucket}: \${BSIZE} (\${BCOUNT} files)"
|
|
fi
|
|
done
|
|
TOTAL=\$(du -sh "\${DIR}/" 2>/dev/null | cut -f1)
|
|
echo " Total backup size: \${TOTAL}"
|
|
|
|
echo "[3/3] Writing manifest..."
|
|
cat > "\${DIR}/manifest.json" <<EOF
|
|
{
|
|
"backup_name": "\${STAMP}",
|
|
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"pg_dump": "stonks.pgdump",
|
|
"minio_buckets": ["stonks-raw-market","stonks-raw-news","stonks-raw-filings","stonks-normalized","stonks-llm-prompts","stonks-llm-results","stonks-lakehouse","stonks-audit"]
|
|
}
|
|
EOF
|
|
|
|
# Symlink latest
|
|
ln -sfn "\${STAMP}" /backup/latest
|
|
echo "=== Backup complete: \${DIR} ==="
|
|
ls -lh "\${DIR}/"
|
|
EOJOB
|
|
|
|
echo "Waiting for backup job to complete..."
|
|
kubectl wait --for=condition=complete job/${JOB_NAME} -n ${NAMESPACE} --timeout=600s 2>&1 || {
|
|
echo "Backup job failed or timed out. Logs:"
|
|
kubectl logs job/${JOB_NAME} -n ${NAMESPACE} 2>&1 | tail -20
|
|
exit 1
|
|
}
|
|
|
|
echo ""
|
|
kubectl logs job/${JOB_NAME} -n ${NAMESPACE} 2>&1 | tail -10
|
|
echo ""
|
|
echo "=== Backup ${BACKUP_NAME} complete ==="
|