Files
pangolin/scripts/ci/backup-db.sh
T
wangjia f8e870fd6a fix(ci): backup 大小 sanity 阈值 50k→1k(小库压缩后 <50k 被误删)
远端 .backup 出的库 gzip 后可能 <50KB,原 >50k 检查把有效备份当'异常小'删了。
空 gzip 仅 ~20 字节,>1k 足以区分空/断流与有效备份。
2026-07-06 15:34:18 +08:00

55 lines
2.4 KiB
Bash

#!/usr/bin/env bash
# backup-db.sh — pangolin1 控制面 SQLite 每日备份到 NAS(异地容灾,#26 / F2)。
#
# 经 ssh 在 pangolin1 上用 sqlite3 online `.backup` 取 WAL 一致快照(不阻塞服务、
# 不像裸 cp 那样可能拿到半写状态),远端先跑 PRAGMA integrity_check 确认没坏,再把
# 快照 gzip 流回 runner,落到 NAS 备份盘(默认 /volume1/docker/backups/pangolin,
# 该盘已挂进 runner 容器)。保留 30 天。异地 = 家里 NAS ≠ VPS 机房,构成容灾。
#
# 不在 pangolin1 上安装/常驻任何东西:调度在 Gitea Actions(backup.yml),每次经
# ssh 临时跑一次 sqlite3。需环境:DEPLOY_SSH_KEY(secret)。从 repo 根调用。
set -euo pipefail
# shellcheck source=scripts/ci/lib-ssh.sh
. scripts/ci/lib-ssh.sh
BACKUP_DIR="${BACKUP_DIR:-/volume1/docker/backups/pangolin}"
mkdir -p "$BACKUP_DIR"
# 时间戳文件名(bash 内建 strftime,免 $() 命令替换)。
printf -v TS '%(%Y%m%d_%H%M%S)T' -1
DEST="${BACKUP_DIR}/pangolin_${TS}.db.gz"
# setup_ssh 内部先注册 EXIT trap 清理临时私钥,再写 key/known_hosts,导出 $SSH。
setup_ssh
echo "==> backup-db: sqlite3 .backup pangolin1(${DEPLOY_HOST})-> ${DEST}"
# 远端单引号 heredoc:$BK/$$ 在 pangolin1 上展开。以 pangolin 用户(DB 属主)跑
# online .backup + integrity_check;校验通过才 cat 回流;临时文件用完即删。
# integrity_check 失败 → 远端非零退出 → 本地 pipefail 令整条命令失败 → set -e 中止。
$SSH "root@${DEPLOY_HOST}" 'bash -s' <<'ENDSSH' | gzip > "$DEST"
set -euo pipefail
BK="/tmp/pangolin-bk-$$.db"
trap 'rm -f "$BK"' EXIT
runuser -u pangolin -- sqlite3 "/var/lib/pangolin/pangolin.db" ".backup ${BK}"
if ! runuser -u pangolin -- sqlite3 "$BK" "PRAGMA integrity_check;" | grep -q '^ok$'; then
echo "backup-db(remote): integrity_check 失败" >&2
exit 1
fi
cat "$BK"
ENDSSH
# 本地二次校验:gzip 完好 + 非空 sanity。空 gzip 仅 ~20 字节;真备份(即便很小的库)
# gzip 后也远大于 1KB,故用 >1k 区分「空/断流」与「有效备份」(不用 50k:小库压缩后可能 <50k)。
gzip -t "$DEST"
if ! find "$DEST" -size +1k -print -quit | grep -q .; then
echo "==> backup-db: 备份异常小(<1k,疑似空/断流),中止" >&2
rm -f "$DEST"
exit 1
fi
# 保留最近 30 天。
find "$BACKUP_DIR" -name 'pangolin_*.db.gz' -mtime +30 -delete
echo "==> backup-db: 备份完成 ${DEST}"