v3.7: 添加完整恢复模式 - 恢复时可选择完整恢复或仅恢复数据

This commit is contained in:
mango
2026-02-02 16:19:38 +08:00
parent e94f204c88
commit 7969abcfe4

View File

@@ -1,7 +1,7 @@
#!/bin/bash
#===============================================================================
# VPS 快照备份脚本 v3.6.6
# VPS 快照备份脚本 v3.7.6
# 支持: Ubuntu, Debian, CentOS, Alpine
# 功能: 智能识别应用 + Docker迁移 + 数据备份 + Telegram通知
#===============================================================================
@@ -19,7 +19,7 @@ LOG_FILE="/var/log/vps-snapshot.log"
print_banner() {
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ VPS 快照备份脚本 v3.6 ║"
echo "║ VPS 快照备份脚本 v3.7 ║"
echo "║ 智能识别 + Docker迁移 + 数据备份 ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"
@@ -498,11 +498,20 @@ create_snapshot() {
# 检测应用并备份
detect_apps > /dev/null
# Docker 数据
# Docker 数据 - 导出到临时目录
local tmp_dir="/tmp/snapshot_$$"
mkdir -p "$tmp_dir"
# 保存已安装包列表(用于完整恢复)
log "保存系统包列表..."
if command -v dpkg &>/dev/null; then
dpkg --get-selections | grep -v deinstall | awk '{print $1}' > "$tmp_dir/installed-packages.txt"
elif command -v rpm &>/dev/null; then
rpm -qa --qf '%{NAME}\n' > "$tmp_dir/installed-packages.txt"
elif command -v apk &>/dev/null; then
apk list -I 2>/dev/null | awk -F' ' '{print $1}' | sed 's/-[0-9].*//' > "$tmp_dir/installed-packages.txt"
fi
if command -v docker &>/dev/null && docker info &>/dev/null; then
docker_export "$tmp_dir"
fi
@@ -579,12 +588,118 @@ do_restore_local() {
[ ! -f "$snap_dir/$snap_file" ] && { error "文件不存在"; return 1; }
log "🔄 恢复快照: $snap_file"
# 选择恢复模式
echo ""
info "选择恢复模式:"
echo " 1) 完整恢复 (恢复到快照时状态,删除后来安装的软件)"
echo " 2) 仅恢复数据 (只恢复Docker和应用数据保留现有系统)"
echo ""
read -p "请选择 [1-2]: " restore_mode
case "$restore_mode" in
1) do_full_restore "$snap_dir/$snap_file" ;;
2) do_data_restore "$snap_dir/$snap_file" ;;
*) error "无效选择"; return 1 ;;
esac
}
# 完整恢复 - 恢复到快照时的完整状态
do_full_restore() {
local snap_file="$1"
log "🔄 完整恢复: $(basename $snap_file)"
# 解压到临时目录
local tmp_dir="/tmp/restore_$$"
mkdir -p "$tmp_dir"
tar -xzf "$snap_dir/$snap_file" -C "$tmp_dir"
tar -xzf "$snap_file" -C "$tmp_dir"
# 读取快照时的已安装包列表
if [ -f "$tmp_dir/installed-packages.txt" ]; then
log "恢复系统包状态..."
# 获取当前已安装的包
local current_pkgs="/tmp/current_pkgs_$$.txt"
if command -v dpkg &>/dev/null; then
dpkg --get-selections | grep -v deinstall | awk '{print $1}' > "$current_pkgs"
elif command -v rpm &>/dev/null; then
rpm -qa --qf '%{NAME}\n' > "$current_pkgs"
elif command -v apk &>/dev/null; then
apk list -I 2>/dev/null | awk -F' ' '{print $1}' | sed 's/-[0-9].*//' > "$current_pkgs"
fi
# 找出快照后新安装的包
if [ -f "$current_pkgs" ]; then
local new_pkgs=$(comm -23 <(sort "$current_pkgs") <(sort "$tmp_dir/installed-packages.txt"))
if [ -n "$new_pkgs" ]; then
log "删除快照后安装的软件包..."
echo "$new_pkgs" | head -20
[ $(echo "$new_pkgs" | wc -l) -gt 20 ] && echo "... 等共 $(echo "$new_pkgs" | wc -l) 个包"
if command -v apt-get &>/dev/null; then
echo "$new_pkgs" | xargs apt-get purge -y --auto-remove 2>/dev/null || true
elif command -v yum &>/dev/null; then
echo "$new_pkgs" | xargs yum remove -y 2>/dev/null || true
elif command -v apk &>/dev/null; then
echo "$new_pkgs" | xargs apk del 2>/dev/null || true
fi
fi
rm -f "$current_pkgs"
fi
fi
# 停止并删除所有Docker容器
if command -v docker &>/dev/null; then
log "清理现有 Docker 环境..."
docker stop $(docker ps -aq) 2>/dev/null || true
docker rm $(docker ps -aq) 2>/dev/null || true
docker system prune -af 2>/dev/null || true
fi
# 导入Docker镜像
if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
log "导入 Docker 镜像..."
gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load
fi
# 恢复Docker volumes
if [ -f "$tmp_dir/docker-volumes.tar.gz" ]; then
log "恢复 Docker Volumes..."
tar -xzf "$tmp_dir/docker-volumes.tar.gz" -C /var/lib/docker/volumes/ 2>/dev/null || true
fi
# 恢复docker-compose文件
if [ -f "$tmp_dir/docker-compose.yml" ]; then
cp "$tmp_dir/docker-compose.yml" /root/
fi
if [ -f "$tmp_dir/.env" ]; then
cp "$tmp_dir/.env" /root/
fi
# 恢复应用数据
if ls "$tmp_dir"/app-data_*.tar.gz &>/dev/null; then
log "恢复应用数据..."
tar -xzf "$tmp_dir"/app-data_*.tar.gz -C / 2>/dev/null || true
fi
# 启动Docker容器
if [ -f /root/docker-compose.yml ]; then
log "启动 Docker 容器..."
cd /root && docker compose up -d 2>/dev/null || docker-compose up -d 2>/dev/null || true
fi
rm -rf "$tmp_dir"
log "✅ 完整恢复完成"
}
# 仅恢复数据 - 保留现有系统
do_data_restore() {
local snap_file="$1"
log "🔄 恢复数据: $(basename $snap_file)"
# 解压到临时目录
local tmp_dir="/tmp/restore_$$"
mkdir -p "$tmp_dir"
tar -xzf "$snap_file" -C "$tmp_dir"
# 导入Docker
if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
@@ -599,7 +714,7 @@ do_restore_local() {
fi
rm -rf "$tmp_dir"
log "✅ 恢复完成"
log "✅ 数据恢复完成"
}
#===============================================================================