5分钟服务器脚本备份教程

一、备份前准备

先在网页上手动关闭“真仓开关”,确认页面显示:真仓开关:关闭

然后确认当前数据库正常:

cd /opt/polymarket-mcp-server

sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"

第一行必须返回:ok

二、安装安全备份脚本

执行:

nano /root/backup_polymarket_mcp_safe.sh

粘贴下面完整内容:

#!/usr/bin/env bash
set -Eeuo pipefail

umask 077

PROJECT_DIR="/opt/polymarket-mcp-server"
DB_PATH="${PROJECT_DIR}/data/btc5m_polymarket.sqlite"
BACKUP_DIR="/root/polymarket-backups"
STAMP="$(date -u +%Y%m%d_%H%M%S)"
HOST_NAME="$(hostname -s)"

STAGE_DIR="$(mktemp -d "/root/polymarket-backup-stage-${STAMP}-XXXXXX")"
ARCHIVE="${BACKUP_DIR}/polymarket-mcp-offline-${HOST_NAME}_${STAMP}.tar.gz"
CHECKSUM="${ARCHIVE}.sha256"
DB_SNAPSHOT="${STAGE_DIR}/project/data/btc5m_polymarket.sqlite"

mkdir -p "$BACKUP_DIR"

cleanup() {
    rm -rf "$STAGE_DIR"
}
trap cleanup EXIT

if [ ! -d "$PROJECT_DIR" ]; then
    echo "STOP: 项目目录不存在:$PROJECT_DIR"
    exit 1
fi

if [ ! -f "$DB_PATH" ]; then
    echo "STOP: 数据库不存在:$DB_PATH"
    exit 1
fi

echo "=== 1. 备份前检查生产数据库 ==="

db_check="$(sqlite3 "$DB_PATH" "PRAGMA quick_check;" 2>&1 || true)"

if [ "$db_check" != "ok" ]; then
    echo "STOP: 生产数据库检查失败:"
    echo "$db_check"
    exit 1
fi

file "$DB_PATH"
echo "生产数据库检查通过"

echo "=== 2. 记录服务原始状态 ==="

WEB_WAS_ACTIVE=0
SETTLE_TIMER_WAS_ACTIVE=0
PREVIEW_TIMER_WAS_ACTIVE=0

systemctl is-active --quiet polymarket-mcp-web.service \
    && WEB_WAS_ACTIVE=1 || true

systemctl is-active --quiet btc5m-live-settlement-sync.timer \
    && SETTLE_TIMER_WAS_ACTIVE=1 || true

systemctl is-active --quiet btc5m-live-backtest-preview.timer \
    && PREVIEW_TIMER_WAS_ACTIVE=1 || true

printf 'web=%s settlement_timer=%s preview_timer=%s\n' \
    "$WEB_WAS_ACTIVE" \
    "$SETTLE_TIMER_WAS_ACTIVE" \
    "$PREVIEW_TIMER_WAS_ACTIVE"

echo "=== 3. 停止数据库写入来源 ==="

systemctl stop btc5m-live-settlement-sync.timer 2>/dev/null || true
systemctl stop btc5m-live-settlement-sync.service 2>/dev/null || true

systemctl stop btc5m-live-backtest-preview.timer 2>/dev/null || true
systemctl stop btc5m-live-backtest-preview.service 2>/dev/null || true

systemctl stop btc5m-db-maintenance.timer 2>/dev/null || true
systemctl stop btc5m-db-maintenance.service 2>/dev/null || true

systemctl stop polymarket-mcp-web.service

sleep 3

if systemctl is-active --quiet polymarket-mcp-web.service; then
    echo "STOP: Web服务没有停止"
    exit 1
fi

if fuser "$DB_PATH" >/dev/null 2>&1; then
    echo "STOP: 仍有进程占用数据库:"
    fuser -v "$DB_PATH" || true
    exit 1
fi

echo "所有已知数据库写入服务均已停止"

echo "=== 4. 停写后再次检查数据库 ==="

db_check="$(sqlite3 "$DB_PATH" "PRAGMA quick_check;" 2>&1 || true)"

if [ "$db_check" != "ok" ]; then
    echo "STOP: 停止服务后数据库检查失败:"
    echo "$db_check"
    exit 1
fi

echo "数据库完整性正常"

echo "=== 5. 复制项目文件 ==="

mkdir -p "${STAGE_DIR}/project"

tar \
    --exclude='./venv' \
    --exclude='./.git' \
    --exclude='./__pycache__' \
    --exclude='*/__pycache__' \
    --exclude='*.pyc' \
    --exclude='./data/btc5m_polymarket.sqlite' \
    --exclude='./data/btc5m_polymarket.sqlite-wal' \
    --exclude='./data/btc5m_polymarket.sqlite-shm' \
    --exclude='./data/btc5m_polymarket.sqlite-journal' \
    -C "$PROJECT_DIR" \
    -cpf - . |
tar -C "${STAGE_DIR}/project" -xpf -

mkdir -p "$(dirname "$DB_SNAPSHOT")"

echo "=== 6. 使用SQLite生成一致性快照 ==="

sqlite3 \
    -cmd ".timeout 30000" \
    "$DB_PATH" \
    ".backup '$DB_SNAPSHOT'"

snapshot_check="$(sqlite3 "$DB_SNAPSHOT" "PRAGMA quick_check;" 2>&1 || true)"

if [ "$snapshot_check" != "ok" ]; then
    echo "STOP: 快照数据库检查失败:"
    echo "$snapshot_check"
    exit 1
fi

header="$(od -An -tc -N16 "$DB_SNAPSHOT")"

if ! printf '%s\n' "$header" | grep -q "S.*Q.*L.*i.*t.*e"; then
    echo "STOP: 快照数据库文件头异常:"
    echo "$header"
    exit 1
fi

echo "快照数据库检查通过"

echo "=== 7. 保存数据库统计清单 ==="

mkdir -p "${STAGE_DIR}/manifest"

sqlite3 -header -column "$DB_SNAPSHOT" "
SELECT
    COUNT(*) AS live_total,
    MAX(id) AS latest_live_id
FROM btc5m_live_trade_intents;

SELECT
    COUNT(*) AS paper_total,
    MAX(id) AS latest_paper_id
FROM btc5m_paper_trades;

SELECT
    COUNT(*) AS snapshot_total,
    MAX(id) AS latest_snapshot_id
FROM btc5m_orderbook_snapshots;
" | tee "${STAGE_DIR}/manifest/database-counts.txt"

file "$DB_SNAPSHOT" \
    > "${STAGE_DIR}/manifest/database-file-type.txt"

sha256sum "$DB_SNAPSHOT" \
    > "${STAGE_DIR}/manifest/database.sha256"

echo "=== 8. 保存systemd配置 ==="

mkdir -p "${STAGE_DIR}/systemd/etc/systemd/system"

find /etc/systemd/system \
    -maxdepth 2 \
    -type f \
    \( \
        -name 'btc5m-*' \
        -o -name 'polymarket-mcp-web.service' \
        -o -path '*/polymarket-mcp-web.service.d/*' \
    \) \
    -print0 |
while IFS= read -r -d '' unit_file; do
    relative_path="${unit_file#/etc/systemd/system/}"
    destination="${STAGE_DIR}/systemd/etc/systemd/system/${relative_path}"

    mkdir -p "$(dirname "$destination")"
    cp -a "$unit_file" "$destination"
done

systemctl list-timers --all --no-pager \
    > "${STAGE_DIR}/manifest/systemd-timers.txt"

systemctl list-unit-files --no-pager |
grep -E 'btc5m|polymarket-mcp' \
    > "${STAGE_DIR}/manifest/systemd-unit-files.txt" || true

echo "=== 9. 保存环境信息 ==="

{
    date -u
    hostnamectl 2>/dev/null || true
    python3 --version
    sqlite3 --version
    du -sh "$PROJECT_DIR"
} > "${STAGE_DIR}/manifest/environment.txt"

if [ -x "${PROJECT_DIR}/venv/bin/pip" ]; then
    "${PROJECT_DIR}/venv/bin/pip" freeze \
        > "${STAGE_DIR}/manifest/pip-freeze.txt" || true
fi

echo "=== 10. 生成压缩包 ==="

tar -C "$STAGE_DIR" -czf "$ARCHIVE" .

sha256sum "$ARCHIVE" |
sed "s|  ${ARCHIVE}|  $(basename "$ARCHIVE")|" \
    > "$CHECKSUM"

echo "=== 11. 验证压缩包 ==="

gzip -t "$ARCHIVE"

(
    cd "$BACKUP_DIR"
    sha256sum -c "$(basename "$CHECKSUM")"
)

tar -tzf "$ARCHIVE" |
grep -q 'project/data/btc5m_polymarket.sqlite'

echo "压缩包及数据库文件均已验证"

echo "=== 12. 恢复原来运行的服务 ==="

if [ "$WEB_WAS_ACTIVE" -eq 1 ]; then
    systemctl start polymarket-mcp-web.service
fi

if [ "$SETTLE_TIMER_WAS_ACTIVE" -eq 1 ]; then
    systemctl start btc5m-live-settlement-sync.timer
fi

if [ "$PREVIEW_TIMER_WAS_ACTIVE" -eq 1 ]; then
    systemctl start btc5m-live-backtest-preview.timer
fi

# 数据库维护定时器不会自动恢复。
# 等维护脚本确认安全后,再单独启用。

sleep 5

echo "=== 13. 最终状态 ==="

systemctl is-active polymarket-mcp-web.service || true
systemctl is-active btc5m-live-settlement-sync.timer || true
systemctl is-active btc5m-live-backtest-preview.timer || true

echo
echo "OK: 完整安全备份成功"
echo "备份文件:$ARCHIVE"
echo "校验文件:$CHECKSUM"
echo "注意:真仓开关保持你备份前的配置;请人工确认后再开启。"

保存并退出:Ctrl+O
回车
Ctrl+X

设置权限:

chmod 700 /root/backup_polymarket_mcp_safe.sh

三、执行备份

确定真仓已手动关闭后运行:

/root/backup_polymarket_mcp_safe.sh

成功时最后会显示:

OK: 完整安全备份成功
备份文件:/root/polymarket-backups/polymarket-mcp-offline-…
校验文件:/root/polymarket-backups/polymarket-mcp-offline-….sha256

如果中途显示 STOP,不要手动开启真仓,先根据错误检查数据库和服务。

四、备份完成后验证

找到最新备份

cd /root/polymarket-backups

latest_archive="$(ls -1t polymarket-mcp-offline-*.tar.gz | head -1)"

printf '最新备份:%s\n' "$latest_archive"

sha256sum -c "${latest_archive}.sha256"

gzip -t "$latest_archive"

tar -tzf "$latest_archive" |
grep 'project/data/btc5m_polymarket.sqlite'

应分别看到:

OK以及数据库文件路径。

再做一次独立解压验证:

verify_dir="$(mktemp -d /root/polymarket-verify-XXXXXX)"

tar -xzf "$latest_archive" \
  -C "$verify_dir" \
  ./project/data/btc5m_polymarket.sqlite

verify_db="$verify_dir/project/data/btc5m_polymarket.sqlite"

file "$verify_db"

sqlite3 "$verify_db" "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"

确认第一行是:ok

验证目录暂时保留即可;确认无误后可删除:

rm -rf -- "$verify_dir"

这里只删除刚刚由 mktemp 创建的明确目录。

五、确认系统恢复运行

systemctl is-active polymarket-mcp-web.service

systemctl is-active btc5m-live-settlement-sync.timer

systemctl is-active btc5m-live-backtest-preview.timer

journalctl \
  -u polymarket-mcp-web.service \
  -n 80 \
  --no-pager

再检查数据库是否继续增长:

cd /opt/polymarket-mcp-server

sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id)
FROM btc5m_orderbook_snapshots;
"

间隔约十秒后再运行一次,快照数量或最大 ID 应继续增加。

最后打开网页确认:

  • 数据记录器运行中;
  • 模拟交易运行中;
  • 自动结算运行中;
  • 真仓统计能读取;
  • 当前真仓规则正确;
  • 需要真仓时,再人工开启真仓开关。

六、一定要把备份复制出VPS

只存放在原VPS不算完整备份。VPS损坏或到期后,本机备份也会一起消失。

复制到电脑:

scp root@你的VPS地址:/root/polymarket-backups/polymarket-mcp-offline-*.tar.gz .
scp root@你的VPS地址:/root/polymarket-backups/polymarket-mcp-offline-*.sha256 .

或者复制到新VPS:

scp \
  /root/polymarket-backups/polymarket-mcp-offline-*.tar.gz \
  root@新VPS地址:/root/

scp \
  /root/polymarket-backups/polymarket-mcp-offline-*.sha256 \
  root@新VPS地址:/root/

备份包可能包含 .env、API凭据和私钥,因此:

  • 不要上传到公开网盘或公开 GitHub;
  • 本地备份文件应加密保存;
  • 备份目录保持 root 专用权限;
  • 不要把备份文件发送给陌生人。

这套流程不会直接复制运行中的 WAL 数据库,也不会自动开启真仓;数据库维护定时器仍保持停用。

!!!备份脚本后,为保护数据库没有自动重启 Web 服务

1. 检查数据库

cd /opt/polymarket-mcp-server

file data/btc5m_polymarket.sqlite

sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"

如果第一行返回:ok

继续下一步。

2. 启动网页服务

systemctl daemon-reload

systemctl start polymarket-mcp-web.service

systemctl is-active polymarket-mcp-web.service

正常应显示:active

然后查看启动日志:

journalctl \
  -u polymarket-mcp-web.service \
  -n 120 \
  --no-pager -l

3. 测试本机网页接口

curl -m 20 -i \
  http://127.0.0.1:8080/

再测试健康接口:

curl -m 20 -sS \
  "http://127.0.0.1:8080/api/btc5m/system/health?_=$(date +%s)" |
python3 -m json.tool

如果首页使用的不是8080端口,先执行:

systemctl cat polymarket-mcp-web.service --no-pager

查看 ExecStart 中的实际端口。

4. 恢复两个安全定时器

网页正常后执行:

systemctl start btc5m-live-settlement-sync.timer

systemctl start btc5m-live-backtest-preview.timer

检查:

systemctl is-active \
  btc5m-live-settlement-sync.timer \
  btc5m-live-backtest-preview.timer

两个都应该显示:active
active

数据库维护定时器先不要启动:

systemctl stop btc5m-db-maintenance.timer

重新开启数据库维护定时器,但不要手动启动维护 .service,只启用 .timer。

systemctl daemon-reload

systemctl enable --now btc5m-db-maintenance.timer

检查是否成功:

systemctl is-enabled btc5m-db-maintenance.timer

systemctl is-active btc5m-db-maintenance.timer

应该显示:enabled
active

查看下次维护时间:

systemctl list-timers \
  btc5m-db-maintenance.timer \
  --all \
  --no-pager

再检查详细状态:

systemctl show btc5m-db-maintenance.timer \
  -p ActiveState \
  -p LastTriggerUSec \
  -p NextElapseUSecRealtime \
  -p Unit \
  --no-pager

最后确认没有立即执行失败:

journalctl \
  -u btc5m-db-maintenance.service \
  -n 80 \
  --no-pager -l

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注