From e1a17acbe41e762975395cbedadfa887b37677cf Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 6 Jul 2026 14:55:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(ci):=20SQLite=20=E6=AF=8F=E6=97=A5?= =?UTF-8?q?=E5=A4=87=E4=BB=BD=E5=88=B0=20NAS(=E5=BC=82=E5=9C=B0=E5=AE=B9?= =?UTF-8?q?=E7=81=BE,#26/F2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backup-db.sh:ssh pangolin1 → sqlite3 online .backup 取 WAL 一致快照 + 远端 integrity_check → gzip 流回 → 存 NAS /volume1/docker/backups/pangolin,保留 30 天。 backup.yml:每日 02:00(北京)cron + 手动,ubuntu-latest,复用 DEPLOY_SSH_KEY, Telegram 通知。异地=家里 NAS≠VPS 机房。不在 pangolin1 装/常驻任何东西。 Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/backup.yml | 36 +++++++++++++++++++++++++ .gitea/workflows/ci.yml | 3 ++- scripts/ci/backup-db.sh | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 .gitea/workflows/backup.yml create mode 100644 scripts/ci/backup-db.sh diff --git a/.gitea/workflows/backup.yml b/.gitea/workflows/backup.yml new file mode 100644 index 0000000..8cab5dc --- /dev/null +++ b/.gitea/workflows/backup.yml @@ -0,0 +1,36 @@ +name: DB Backup + +on: + # 每日北京 02:00(= UTC 18:00)自动备份;也可手动触发。 + schedule: + - cron: '0 18 * * *' + workflow_dispatch: + +concurrency: + group: db-backup + cancel-in-progress: false + +jobs: + backup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Backup pangolin1 SQLite → NAS + env: + DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: bash scripts/ci/backup-db.sh + + - name: Notify (Telegram) + if: always() + env: + TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + run: | + . scripts/ci/notify.sh + if [ "${{ job.status }}" = "success" ]; then + notify_ok "pangolin1 SQLite 备份成功(NAS)" + else + notify_fail "pangolin1 SQLite 备份失败" + fi diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 99e5673..1c227b8 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -54,7 +54,8 @@ jobs: /mnt/scripts/ci/compile-backend.sh \ /mnt/scripts/ci/release-server.sh \ /mnt/scripts/ci/deploy-server.sh \ - /mnt/scripts/ci/test.sh + /mnt/scripts/ci/test.sh \ + /mnt/scripts/ci/backup-db.sh # ── Job 2: OpenAPI Sync Check ──────────────────────────────────────────── openapi-check: diff --git a/scripts/ci/backup-db.sh b/scripts/ci/backup-db.sh new file mode 100644 index 0000000..b1b4ea7 --- /dev/null +++ b/scripts/ci/backup-db.sh @@ -0,0 +1,53 @@ +#!/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 -t "$DEST" +if ! find "$DEST" -size +50k -print -quit | grep -q .; then + echo "==> backup-db: 备份异常小(<50k),中止" >&2 + rm -f "$DEST" + exit 1 +fi + +# 保留最近 30 天。 +find "$BACKUP_DIR" -name 'pangolin_*.db.gz' -mtime +30 -delete + +echo "==> backup-db: 备份完成 ${DEST}"