feat: 首次推送到Gitea - 完整项目代码 + 安全加固 + 知识库
This commit is contained in:
+100
-18
@@ -3,6 +3,7 @@
|
||||
# 追光AI 容器卷备份脚本 (在宿主机上跑)
|
||||
# - 把 4 个 docker 卷打包到 /home/ubuntu/zhuiguang-ai-backup/dockers/
|
||||
# - 默认保留最近 7 天快照
|
||||
# - 增强:磁盘空间预检 + 完整性校验 + 汇总报告
|
||||
# - 用法: bash scripts/backup-volumes.sh
|
||||
# =============================================================================
|
||||
set -e
|
||||
@@ -12,44 +13,125 @@ BACKUP_ROOT="/home/ubuntu/zhuiguang-ai-backup/dockers"
|
||||
STAMP=$(date +%Y%m%d-%H%M%S)
|
||||
BACKUP_DIR="${BACKUP_ROOT}/${STAMP}"
|
||||
KEEP_DAYS=7
|
||||
REQUIRED_FREE_MB=${REQUIRED_FREE_MB:-500} # 最少需要磁盘空闲空间 (MB)
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
|
||||
pass() { echo " ✅ $1"; PASS=$((PASS + 1)); }
|
||||
fail() { echo " ❌ $1"; FAIL=$((FAIL + 1)); }
|
||||
warn() { echo " ⚠️ $1"; WARN=$((WARN + 1)); }
|
||||
|
||||
echo "========================================"
|
||||
echo " 追光AI 卷备份 ($(date '+%Y-%m-%d %H:%M:%S'))"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# ---- 1) 磁盘空间预检 ----
|
||||
echo "--- 1) 磁盘空间检查 ---"
|
||||
BACKUP_DISK=$(df -m "$BACKUP_ROOT" 2>/dev/null | tail -1 | awk '{print $4}')
|
||||
if [ -z "$BACKUP_DISK" ]; then
|
||||
BACKUP_DISK=$(df -m /home 2>/dev/null | tail -1 | awk '{print $4}')
|
||||
fi
|
||||
if [ -z "$BACKUP_DISK" ] || [ "$BACKUP_DISK" -lt "$REQUIRED_FREE_MB" ]; then
|
||||
fail "磁盘空间不足: 可用 ${BACKUP_DISK:-?}MB, 需要 ≥ ${REQUIRED_FREE_MB}MB"
|
||||
exit 1
|
||||
fi
|
||||
pass "磁盘可用空间: ${BACKUP_DISK}MB"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo "[backup] $(date) 开始备份容器卷到 $BACKUP_DIR"
|
||||
echo ""
|
||||
echo "--- 2) 卷备份 ---"
|
||||
|
||||
declare -A VOL_SIZES
|
||||
TOTAL_BYTES=0
|
||||
|
||||
# 备份 4 个卷
|
||||
for vol in zhuiguang_ai_bot_data zhuiguang_ai_bot_public zhuiguang_ai_bot_logs zhuiguang_ai_bot_prisma; do
|
||||
if ! docker volume inspect "$vol" >/dev/null 2>&1; then
|
||||
echo "[backup] WARN: 卷 $vol 不存在,跳过"
|
||||
warn "卷 $vol 不存在,跳过"
|
||||
continue
|
||||
fi
|
||||
echo "[backup] 备份卷 $vol ..."
|
||||
docker run --rm \
|
||||
|
||||
echo " [备份] $vol → ${vol}.tgz"
|
||||
TAR_OUTPUT=$(docker run --rm \
|
||||
-v "${vol}:/src:ro" \
|
||||
-v "${BACKUP_DIR}:/dst" \
|
||||
alpine:3.19 \
|
||||
tar czf "/dst/${vol}.tgz" -C /src . 2>&1 | tail -3
|
||||
echo "[backup] -> ${vol}.tgz"
|
||||
tar czf "/dst/${vol}.tgz" -C /src . 2>&1) || true
|
||||
|
||||
if [ -f "${BACKUP_DIR}/${vol}.tgz" ]; then
|
||||
SIZE=$(ls -lh "${BACKUP_DIR}/${vol}.tgz" | awk '{print $5}')
|
||||
pass "${vol}.tgz (${SIZE})"
|
||||
VOL_SIZES[$vol]=$SIZE
|
||||
TOTAL_BYTES=$((TOTAL_BYTES + $(stat -c%s "${BACKUP_DIR}/${vol}.tgz" 2>/dev/null || echo 0)))
|
||||
else
|
||||
fail "${vol}.tgz 创建失败"
|
||||
fi
|
||||
done
|
||||
|
||||
# 同时备份 .env(敏感文件单独加密存)
|
||||
# ---- 3) 完整性校验 ----
|
||||
echo ""
|
||||
echo "--- 3) 完整性校验 ---"
|
||||
for vol in "${!VOL_SIZES[@]}"; do
|
||||
TGZ_PATH="${BACKUP_DIR}/${vol}.tgz"
|
||||
if tar tzf "$TGZ_PATH" >/dev/null 2>&1; then
|
||||
FILE_COUNT=$(tar tzf "$TGZ_PATH" | wc -l)
|
||||
pass "${vol}.tgz 校验通过 (${FILE_COUNT} 个文件)"
|
||||
else
|
||||
fail "${vol}.tgz 校验失败"
|
||||
fi
|
||||
done
|
||||
|
||||
# ---- 4) .env 备份 ----
|
||||
echo ""
|
||||
echo "--- 4) 环境变量备份 ---"
|
||||
if [ -f "$PROJECT/env/.env.production" ]; then
|
||||
cp "$PROJECT/env/.env.production" "${BACKUP_DIR}/.env.production"
|
||||
echo "[backup] .env.production 已备份"
|
||||
chmod 600 "${BACKUP_DIR}/.env.production"
|
||||
pass ".env.production 已备份"
|
||||
else
|
||||
warn ".env.production 不存在: $PROJECT/env/.env.production"
|
||||
fi
|
||||
|
||||
# 备份清单
|
||||
# ---- 5) 备份清单 ----
|
||||
echo ""
|
||||
echo "--- 5) 备份清单 ---"
|
||||
{
|
||||
echo "# 备份清单 - $(date)"
|
||||
echo "STAMP: $STAMP"
|
||||
ls -lh "$BACKUP_DIR"
|
||||
echo "# 追光AI 卷备份清单"
|
||||
echo "# 时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "# 标识: $STAMP"
|
||||
echo ""
|
||||
ls -lh "$BACKUP_DIR" | tail -n +2
|
||||
} > "$BACKUP_DIR/INDEX.txt"
|
||||
|
||||
echo "[backup] 完成。清单:"
|
||||
ls -lh "$BACKUP_DIR"
|
||||
ls -lh "$BACKUP_DIR" | tail -n +2 | sed 's/^/ /'
|
||||
|
||||
# 清理 7 天前的旧备份
|
||||
echo "[backup] 清理 $KEEP_DAYS 天前的旧备份 ..."
|
||||
find "$BACKUP_ROOT" -maxdepth 1 -type d -mtime +$KEEP_DAYS -exec rm -rf {} + 2>/dev/null || true
|
||||
# ---- 6) 清理旧备份 ----
|
||||
echo ""
|
||||
echo "--- 6) 清理 ${KEEP_DAYS} 天前的旧备份 ---"
|
||||
OLD_COUNT=$(find "$BACKUP_ROOT" -maxdepth 1 -type d -mtime +$KEEP_DAYS 2>/dev/null | wc -l)
|
||||
if [ "$OLD_COUNT" -gt 0 ]; then
|
||||
find "$BACKUP_ROOT" -maxdepth 1 -type d -mtime +$KEEP_DAYS -exec rm -rf {} + 2>/dev/null || true
|
||||
pass "清理了 ${OLD_COUNT} 个过期备份"
|
||||
else
|
||||
pass "无需清理"
|
||||
fi
|
||||
|
||||
echo "[backup] DONE"
|
||||
# ---- 汇总 ----
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " 备份完成"
|
||||
echo " 结果: ${PASS} 通过 / ${WARN} 警告 / ${FAIL} 失败"
|
||||
if [ "$FAIL" -eq 0 ]; then
|
||||
echo " 🟢 状态: 正常"
|
||||
else
|
||||
echo " 🔴 状态: 有 ${FAIL} 项失败,请检查"
|
||||
fi
|
||||
echo " 路径: $BACKUP_DIR"
|
||||
echo " 总大小: $(numfmt --to=iec ${TOTAL_BYTES} 2>/dev/null || echo "${TOTAL_BYTES} 字节")"
|
||||
echo "========================================"
|
||||
|
||||
exit $FAIL
|
||||
|
||||
Reference in New Issue
Block a user