Files
awoooi/scripts/ops/pg-backup.sh
Your Name b191f8e9fe
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 2m6s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
fix(telegram): close ops direct sender gaps
2026-07-02 19:32:36 +08:00

146 lines
4.6 KiB
Bash
Raw Permalink 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}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 載入受控 ops 通知所需環境;禁止 direct Bot API 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_awoooi_ops() {
local status="$1"
local msg="$2"
local helper="${SCRIPT_DIR}/notify-awoooi-ops.sh"
[[ -x "$helper" ]] || return 1
AWOOI_OPS_ALERTNAME="Backup.PG" \
AWOOI_OPS_JOB_NAME="AWOOOI DB 備份" \
AWOOI_OPS_STATUS="$status" \
AWOOI_OPS_SEVERITY="info" \
AWOOI_OPS_SOURCE="pg-backup" \
AWOOI_OPS_COMPONENT="postgres-backup" \
AWOOI_OPS_SUMMARY="AWOOOI DB 備份 ${status}" \
AWOOI_OPS_DETAIL="$msg" \
"$helper" >/dev/null
}
notify_telegram() {
local msg="$1"
local status="${2:-success}"
# 所有通知都必須交給 AWOOI API由 TelegramGateway 送出並鏡像到 AwoooP。
# API 不可達時只留下本地 log避免繞過 DB/log receipt 與 AI automation。
if notify_awoooi_ops "$status" "$msg"; then
return 0
fi
log "WARN: AWOOI API notification unavailable; direct Telegram fallback disabled; receipt remains local"
return 0
}
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 容器,無對外 port用 docker exec 直接 dump)
local outfile_momo="${BACKUP_DIR}/momo_analytics_${TIMESTAMP}.sql.gz"
log "開始備份 momo_analytics (docker exec) → ${outfile_momo}"
if docker exec momo-db pg_dump -U momo momo_analytics 2>/dev/null | gzip > "$outfile_momo"; then
local size_momo
size_momo=$(du -sh "$outfile_momo" | cut -f1)
log "✅ momo_analytics 備份完成 (${size_momo})"
results+=("success:momo_analytics:${size_momo}")
else
log "❌ momo_analytics 備份失敗"
rm -f "$outfile_momo"
results+=("failed: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="⚠️"
local notify_status="success"
[[ $fail_count -gt 0 ]] && notify_status="failed"
notify_telegram "${icon} <b>AWOOOI DB 備份</b>
├ 時間: $(date '+%Y-%m-%d %H:%M') +0800
├ 成功: ${success_count} | 失敗: ${fail_count}
${details}" "$notify_status"
[[ $fail_count -gt 0 ]] && exit 1
return 0
}
main "$@"