Files
awoooi/scripts/ops/pg-backup.sh
OG T f98be41517 feat(ops): pg-backup.sh — PostgreSQL 每 6h 自動備份
備份目標 (188):
- awoooi_prod (host PostgreSQL, TCP 127.0.0.1)
- momo_analytics (momo-db 容器)

功能:
- gzip 壓縮,保留 7 天自動清理
- Telegram 通知 (成功/失敗)
- cron 0 */6 * * * 已設定

驗證: 兩個 DB 備份成功 (awoooi_prod 206K, gz 完整)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 09:16:21 +08:00

118 lines
3.7 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# scripts/ops/pg-backup.sh
# Sprint 5.2: PostgreSQL 自動備份腳本
# 部署: cron 0 */6 * * * on 188 (ollama user)
# 備份目標: awoooi_prod + momo_analytics
# 保留策略: 7 天
# 2026-04-09 Claude Sonnet 4.6 Asia/Taipei
set -euo pipefail
BACKUP_DIR="${BACKUP_DIR:-/home/ollama/backups}"
SECRETS_FILE="${SECRETS_FILE:-/home/ollama/awoooi-ops-secrets/secrets.env}"
RETAIN_DAYS="${RETAIN_DAYS:-7}"
AWOOOI_API_URL="${AWOOOI_API_URL:-https://awoooi.wooo.work}"
# 載入 secrets含 Telegram token for fallback
[[ -f "$SECRETS_FILE" ]] && source "$SECRETS_FILE"
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
LOG_PREFIX="[$(date '+%Y-%m-%d %H:%M:%S %z')]"
log() { echo "${LOG_PREFIX} $*"; }
notify_telegram() {
local msg="$1"
if [[ -n "${TELEGRAM_BOT_TOKEN:-}" && -n "${TELEGRAM_CHAT_ID:-}" ]]; then
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{\"chat_id\":\"${TELEGRAM_CHAT_ID}\",\"text\":\"${msg}\",\"parse_mode\":\"HTML\"}" \
> /dev/null 2>&1 || true
fi
}
backup_db() {
local label="$1" # awoooi_prod | momo_analytics
local host="$2" # 127.0.0.1
local user="$3"
local password="$4"
local dbname="$5"
local outfile="${BACKUP_DIR}/${label}_${TIMESTAMP}.sql.gz"
log "開始備份 ${label}${outfile}"
if PGPASSWORD="$password" pg_dump \
-h "$host" -U "$user" -d "$dbname" \
--no-owner --no-acl \
2>/dev/null | gzip > "$outfile"; then
local size
size=$(du -sh "$outfile" | cut -f1)
log "${label} 備份完成 (${size})"
echo "success:${label}:${size}"
else
log "${label} 備份失敗"
echo "failed:${label}"
fi
}
cleanup_old_backups() {
local label="$1"
local count
count=$(find "$BACKUP_DIR" -name "${label}_*.sql.gz" -mtime "+${RETAIN_DAYS}" | wc -l)
if (( count > 0 )); then
find "$BACKUP_DIR" -name "${label}_*.sql.gz" -mtime "+${RETAIN_DAYS}" -delete
log "🗑️ 清理 ${label} 舊備份 ${count} 個 (>${RETAIN_DAYS}天)"
fi
}
main() {
mkdir -p "$BACKUP_DIR"
log "=== pg-backup 開始 (retain=${RETAIN_DAYS}d) ==="
local results=()
# awoooi_prod (host PostgreSQL, TCP)
results+=("$(backup_db "awoooi_prod" "127.0.0.1" "awoooi" "awoooi_prod_2026" "awoooi_prod")")
# momo_analytics (momo-db 容器,透過 host TCP)
local momo_pass
momo_pass=$(docker inspect momo-db --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null | grep POSTGRES_PASSWORD | cut -d= -f2)
if [[ -n "$momo_pass" ]]; then
results+=("$(backup_db "momo_analytics" "127.0.0.1" "momo" "$momo_pass" "momo_analytics")")
else
log "⚠️ momo-db 密碼未取得,跳過 momo_analytics"
results+=("skipped:momo_analytics")
fi
# 清理舊備份
cleanup_old_backups "awoooi_prod"
cleanup_old_backups "momo_analytics"
log "=== pg-backup 完成 ==="
# 組裝 Telegram 通知
local success_count=0 fail_count=0 details=""
for r in "${results[@]}"; do
IFS=':' read -r status label size_or_empty <<< "$r"
case "$status" in
success) ((success_count++)) || true; details+="${label} (${size_or_empty})\n" ;;
failed) ((fail_count++)) || true; details+="${label} 失敗\n" ;;
skipped) details+="⏭️ ${label} 跳過\n" ;;
esac
done
local icon="✅"
[[ $fail_count -gt 0 ]] && icon="⚠️"
notify_telegram "${icon} <b>AWOOOI DB 備份</b>
├ 時間: $(date '+%Y-%m-%d %H:%M') +0800
├ 成功: ${success_count} | 失敗: ${fail_count}
${details}"
[[ $fail_count -gt 0 ]] && exit 1
return 0
}
main "$@"