782 lines
24 KiB
Bash
Executable File
782 lines
24 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# QAUP 综合备份和恢复脚本
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
BACKUP_ROOT="$PROJECT_ROOT/backup"
|
|
|
|
# 颜色输出
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
print_message() {
|
|
local color=$1
|
|
local message=$2
|
|
echo -e "${color}${message}${NC}"
|
|
}
|
|
|
|
# 获取时间戳
|
|
get_timestamp() {
|
|
date '+%Y%m%d_%H%M%S'
|
|
}
|
|
|
|
# 创建备份目录
|
|
create_backup_dirs() {
|
|
local timestamp=$1
|
|
local backup_dir="$BACKUP_ROOT/$timestamp"
|
|
|
|
mkdir -p "$backup_dir"/{database,config,uploads,logs,system}
|
|
echo "$backup_dir"
|
|
}
|
|
|
|
# 数据库备份
|
|
backup_database() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
|
|
print_message $BLUE "备份数据库..."
|
|
|
|
# 检查数据库容器状态
|
|
if ! docker ps --format "{{.Names}}" | grep -q "qaup-postgres"; then
|
|
print_message $RED "数据库容器未运行"
|
|
return 1
|
|
fi
|
|
|
|
# 创建数据库备份
|
|
local db_backup_file="$backup_dir/database/qaup_database_$timestamp.sql"
|
|
|
|
if docker exec qaup-postgres pg_dump -U postgres -d qaup --verbose --clean --if-exists --create > "$db_backup_file"; then
|
|
# 压缩备份文件
|
|
gzip "$db_backup_file"
|
|
|
|
# 创建数据库元信息
|
|
cat > "$backup_dir/database/metadata.json" << EOF
|
|
{
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"database_version": "$(docker exec qaup-postgres psql -U postgres -t -c 'SELECT version();' | xargs)",
|
|
"database_size": "$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT pg_size_pretty(pg_database_size('qaup'));" | xargs)",
|
|
"table_count": "$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" | xargs)",
|
|
"backup_file": "qaup_database_$timestamp.sql.gz",
|
|
"backup_size": "$(du -h "$db_backup_file.gz" | cut -f1)"
|
|
}
|
|
EOF
|
|
|
|
print_message $GREEN "✓ 数据库备份完成: $(du -h "$db_backup_file.gz" | cut -f1)"
|
|
return 0
|
|
else
|
|
print_message $RED "✗ 数据库备份失败"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 配置文件备份
|
|
backup_config() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
|
|
print_message $BLUE "备份配置文件..."
|
|
|
|
# 备份环境配置
|
|
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
cp "$PROJECT_ROOT/.env" "$backup_dir/config/env_$timestamp"
|
|
print_message $GREEN "✓ 环境配置已备份"
|
|
fi
|
|
|
|
# 备份 Docker Compose 配置
|
|
cp "$PROJECT_ROOT"/docker-compose*.yml "$backup_dir/config/" 2>/dev/null || true
|
|
|
|
# 备份应用配置
|
|
if [ -d "$PROJECT_ROOT/config" ]; then
|
|
cp -r "$PROJECT_ROOT/config" "$backup_dir/config/app_config"
|
|
print_message $GREEN "✓ 应用配置已备份"
|
|
fi
|
|
|
|
# 备份 Docker 配置
|
|
cp -r "$PROJECT_ROOT/docker" "$backup_dir/config/docker_config"
|
|
|
|
# 备份 SSL 证书
|
|
if [ -d "$PROJECT_ROOT/ssl" ]; then
|
|
cp -r "$PROJECT_ROOT/ssl" "$backup_dir/config/ssl_certs"
|
|
print_message $GREEN "✓ SSL 证书已备份"
|
|
fi
|
|
|
|
# 创建配置元信息
|
|
cat > "$backup_dir/config/metadata.json" << EOF
|
|
{
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"project_version": "$(grep version $PROJECT_ROOT/pom.xml | head -1 | sed 's/.*<version>\(.*\)<\/version>.*/\1/' | xargs)",
|
|
"docker_compose_files": $(ls "$PROJECT_ROOT"/docker-compose*.yml | wc -l),
|
|
"config_files_count": $(find "$backup_dir/config" -type f | wc -l)
|
|
}
|
|
EOF
|
|
|
|
print_message $GREEN "✓ 配置文件备份完成"
|
|
}
|
|
|
|
# 上传文件备份
|
|
backup_uploads() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
|
|
print_message $BLUE "备份上传文件..."
|
|
|
|
local uploads_dir="$PROJECT_ROOT/data/uploads"
|
|
|
|
if [ -d "$uploads_dir" ] && [ "$(ls -A "$uploads_dir" 2>/dev/null)" ]; then
|
|
# 创建上传文件备份
|
|
tar -czf "$backup_dir/uploads/uploads_$timestamp.tar.gz" -C "$PROJECT_ROOT/data" uploads/
|
|
|
|
# 创建上传文件元信息
|
|
cat > "$backup_dir/uploads/metadata.json" << EOF
|
|
{
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"files_count": $(find "$uploads_dir" -type f | wc -l),
|
|
"total_size": "$(du -sh "$uploads_dir" | cut -f1)",
|
|
"backup_file": "uploads_$timestamp.tar.gz",
|
|
"backup_size": "$(du -h "$backup_dir/uploads/uploads_$timestamp.tar.gz" | cut -f1)"
|
|
}
|
|
EOF
|
|
|
|
print_message $GREEN "✓ 上传文件备份完成: $(du -h "$backup_dir/uploads/uploads_$timestamp.tar.gz" | cut -f1)"
|
|
else
|
|
print_message $YELLOW "⚠ 上传目录为空,跳过备份"
|
|
echo '{"backup_time": "'$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')'", "status": "empty"}' > "$backup_dir/uploads/metadata.json"
|
|
fi
|
|
}
|
|
|
|
# 日志备份
|
|
backup_logs() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
local days=${3:-7} # 默认备份最近7天的日志
|
|
|
|
print_message $BLUE "备份日志文件(最近 $days 天)..."
|
|
|
|
local logs_dir="$PROJECT_ROOT/logs"
|
|
|
|
if [ -d "$logs_dir" ]; then
|
|
# 备份最近的日志文件
|
|
find "$logs_dir" -name "*.log" -mtime -$days -exec cp {} "$backup_dir/logs/" \; 2>/dev/null || true
|
|
|
|
# 备份容器日志
|
|
local containers=("qaup-app" "qaup-nginx" "qaup-postgres" "qaup-redis")
|
|
for container in "${containers[@]}"; do
|
|
if docker ps --format "{{.Names}}" | grep -q "$container"; then
|
|
docker logs --since "${days}d" "$container" > "$backup_dir/logs/${container}_$timestamp.log" 2>&1 || true
|
|
fi
|
|
done
|
|
|
|
# 压缩日志备份
|
|
if [ "$(ls -A "$backup_dir/logs" 2>/dev/null)" ]; then
|
|
tar -czf "$backup_dir/logs/logs_$timestamp.tar.gz" -C "$backup_dir/logs" . --exclude="*.tar.gz"
|
|
find "$backup_dir/logs" -name "*.log" -delete
|
|
|
|
# 创建日志元信息
|
|
cat > "$backup_dir/logs/metadata.json" << EOF
|
|
{
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"days_included": $days,
|
|
"backup_file": "logs_$timestamp.tar.gz",
|
|
"backup_size": "$(du -h "$backup_dir/logs/logs_$timestamp.tar.gz" | cut -f1)"
|
|
}
|
|
EOF
|
|
|
|
print_message $GREEN "✓ 日志备份完成: $(du -h "$backup_dir/logs/logs_$timestamp.tar.gz" | cut -f1)"
|
|
else
|
|
print_message $YELLOW "⚠ 未找到符合条件的日志文件"
|
|
fi
|
|
else
|
|
print_message $YELLOW "⚠ 日志目录不存在"
|
|
fi
|
|
}
|
|
|
|
# 系统信息备份
|
|
backup_system_info() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
|
|
print_message $BLUE "备份系统信息..."
|
|
|
|
# 收集系统信息
|
|
{
|
|
echo "=== 系统信息备份 ==="
|
|
echo "备份时间: $(date)"
|
|
echo "主机名: $(hostname)"
|
|
echo "操作系统: $(uname -a)"
|
|
echo ""
|
|
|
|
echo "=== Docker 信息 ==="
|
|
docker version
|
|
echo ""
|
|
docker-compose version
|
|
echo ""
|
|
|
|
echo "=== 容器状态 ==="
|
|
docker ps -a
|
|
echo ""
|
|
|
|
echo "=== 镜像信息 ==="
|
|
docker images
|
|
echo ""
|
|
|
|
echo "=== 网络信息 ==="
|
|
docker network ls
|
|
echo ""
|
|
|
|
echo "=== 卷信息 ==="
|
|
docker volume ls
|
|
echo ""
|
|
|
|
echo "=== 系统资源 ==="
|
|
free -h
|
|
echo ""
|
|
df -h
|
|
echo ""
|
|
|
|
echo "=== 网络端口 ==="
|
|
netstat -tuln | grep -E ":(80|443|8080|5432|6379) "
|
|
echo ""
|
|
|
|
} > "$backup_dir/system/system_info_$timestamp.txt"
|
|
|
|
# 备份 Docker Compose 状态
|
|
docker-compose ps > "$backup_dir/system/compose_status_$timestamp.txt" 2>/dev/null || true
|
|
|
|
# 创建系统元信息
|
|
cat > "$backup_dir/system/metadata.json" << EOF
|
|
{
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"hostname": "$(hostname)",
|
|
"os_version": "$(uname -r)",
|
|
"docker_version": "$(docker --version | cut -d' ' -f3 | sed 's/,//')",
|
|
"compose_version": "$(docker-compose --version | cut -d' ' -f3 | sed 's/,//')",
|
|
"running_containers": $(docker ps -q | wc -l),
|
|
"total_containers": $(docker ps -aq | wc -l)
|
|
}
|
|
EOF
|
|
|
|
print_message $GREEN "✓ 系统信息备份完成"
|
|
}
|
|
|
|
# 完整备份
|
|
full_backup() {
|
|
local timestamp=$(get_timestamp)
|
|
local backup_dir=$(create_backup_dirs "$timestamp")
|
|
|
|
print_message $GREEN "========================================="
|
|
print_message $GREEN "开始完整备份: $timestamp"
|
|
print_message $GREEN "========================================="
|
|
|
|
local backup_success=true
|
|
|
|
# 执行各项备份
|
|
backup_database "$backup_dir" "$timestamp" || backup_success=false
|
|
backup_config "$backup_dir" "$timestamp" || backup_success=false
|
|
backup_uploads "$backup_dir" "$timestamp" || backup_success=false
|
|
backup_logs "$backup_dir" "$timestamp" || backup_success=false
|
|
backup_system_info "$backup_dir" "$timestamp" || backup_success=false
|
|
|
|
# 创建备份清单
|
|
create_backup_manifest "$backup_dir" "$timestamp"
|
|
|
|
# 压缩整个备份
|
|
print_message $BLUE "压缩备份文件..."
|
|
local backup_archive="$BACKUP_ROOT/qaup_full_backup_$timestamp.tar.gz"
|
|
tar -czf "$backup_archive" -C "$BACKUP_ROOT" "$(basename "$backup_dir")"
|
|
|
|
# 计算校验和
|
|
sha256sum "$backup_archive" > "$backup_archive.sha256"
|
|
|
|
# 清理临时目录
|
|
rm -rf "$backup_dir"
|
|
|
|
if [ "$backup_success" = true ]; then
|
|
print_message $GREEN "========================================="
|
|
print_message $GREEN "完整备份完成!"
|
|
print_message $GREEN "========================================="
|
|
echo ""
|
|
print_message $BLUE "备份信息:"
|
|
echo " 备份文件: $backup_archive"
|
|
echo " 文件大小: $(du -h "$backup_archive" | cut -f1)"
|
|
echo " 校验文件: $backup_archive.sha256"
|
|
echo ""
|
|
|
|
# 记录备份历史
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') FULL_BACKUP SUCCESS $backup_archive $(du -b "$backup_archive" | cut -f1)" >> "$BACKUP_ROOT/backup_history.log"
|
|
|
|
return 0
|
|
else
|
|
print_message $RED "备份过程中出现错误,请检查日志"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 创建备份清单
|
|
create_backup_manifest() {
|
|
local backup_dir=$1
|
|
local timestamp=$2
|
|
|
|
cat > "$backup_dir/MANIFEST.json" << EOF
|
|
{
|
|
"backup_id": "$timestamp",
|
|
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
|
"backup_type": "full",
|
|
"version": "1.0",
|
|
"components": {
|
|
"database": {
|
|
"included": $([ -f "$backup_dir/database/metadata.json" ] && echo "true" || echo "false"),
|
|
"metadata_file": "database/metadata.json"
|
|
},
|
|
"config": {
|
|
"included": $([ -f "$backup_dir/config/metadata.json" ] && echo "true" || echo "false"),
|
|
"metadata_file": "config/metadata.json"
|
|
},
|
|
"uploads": {
|
|
"included": $([ -f "$backup_dir/uploads/metadata.json" ] && echo "true" || echo "false"),
|
|
"metadata_file": "uploads/metadata.json"
|
|
},
|
|
"logs": {
|
|
"included": $([ -f "$backup_dir/logs/metadata.json" ] && echo "true" || echo "false"),
|
|
"metadata_file": "logs/metadata.json"
|
|
},
|
|
"system": {
|
|
"included": $([ -f "$backup_dir/system/metadata.json" ] && echo "true" || echo "false"),
|
|
"metadata_file": "system/metadata.json"
|
|
}
|
|
},
|
|
"total_size": "$(du -sh "$backup_dir" | cut -f1)",
|
|
"file_count": $(find "$backup_dir" -type f | wc -l)
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# 恢复数据库
|
|
restore_database() {
|
|
local backup_file=$1
|
|
|
|
print_message $BLUE "恢复数据库..."
|
|
|
|
if [ ! -f "$backup_file" ]; then
|
|
print_message $RED "备份文件不存在: $backup_file"
|
|
return 1
|
|
fi
|
|
|
|
# 检查数据库容器状态
|
|
if ! docker ps --format "{{.Names}}" | grep -q "qaup-postgres"; then
|
|
print_message $RED "数据库容器未运行"
|
|
return 1
|
|
fi
|
|
|
|
# 确认恢复操作
|
|
print_message $YELLOW "警告: 此操作将覆盖现有数据库数据"
|
|
read -p "确定要继续吗? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
print_message $BLUE "恢复操作已取消"
|
|
return 1
|
|
fi
|
|
|
|
# 停止应用服务以避免数据冲突
|
|
print_message $BLUE "停止应用服务..."
|
|
docker stop qaup-app || true
|
|
|
|
# 恢复数据库
|
|
if [[ "$backup_file" == *.gz ]]; then
|
|
# 解压并恢复
|
|
if gunzip -c "$backup_file" | docker exec -i qaup-postgres psql -U postgres; then
|
|
print_message $GREEN "✓ 数据库恢复成功"
|
|
else
|
|
print_message $RED "✗ 数据库恢复失败"
|
|
return 1
|
|
fi
|
|
else
|
|
# 直接恢复
|
|
if docker exec -i qaup-postgres psql -U postgres < "$backup_file"; then
|
|
print_message $GREEN "✓ 数据库恢复成功"
|
|
else
|
|
print_message $RED "✗ 数据库恢复失败"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# 重启应用服务
|
|
print_message $BLUE "重启应用服务..."
|
|
docker start qaup-app
|
|
|
|
# 等待服务就绪
|
|
sleep 10
|
|
if curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
|
print_message $GREEN "✓ 应用服务已恢复正常"
|
|
else
|
|
print_message $YELLOW "⚠ 应用服务可能需要更多时间启动"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# 恢复配置文件
|
|
restore_config() {
|
|
local backup_dir=$1
|
|
|
|
print_message $BLUE "恢复配置文件..."
|
|
|
|
if [ ! -d "$backup_dir" ]; then
|
|
print_message $RED "备份目录不存在: $backup_dir"
|
|
return 1
|
|
fi
|
|
|
|
# 备份当前配置
|
|
local current_backup="$PROJECT_ROOT/config_backup_$(get_timestamp)"
|
|
mkdir -p "$current_backup"
|
|
|
|
[ -f "$PROJECT_ROOT/.env" ] && cp "$PROJECT_ROOT/.env" "$current_backup/"
|
|
[ -d "$PROJECT_ROOT/config" ] && cp -r "$PROJECT_ROOT/config" "$current_backup/"
|
|
[ -d "$PROJECT_ROOT/docker" ] && cp -r "$PROJECT_ROOT/docker" "$current_backup/"
|
|
|
|
print_message $BLUE "当前配置已备份到: $current_backup"
|
|
|
|
# 恢复配置文件
|
|
if [ -f "$backup_dir/env_"* ]; then
|
|
cp "$backup_dir"/env_* "$PROJECT_ROOT/.env"
|
|
print_message $GREEN "✓ 环境配置已恢复"
|
|
fi
|
|
|
|
if [ -d "$backup_dir/app_config" ]; then
|
|
rm -rf "$PROJECT_ROOT/config"
|
|
cp -r "$backup_dir/app_config" "$PROJECT_ROOT/config"
|
|
print_message $GREEN "✓ 应用配置已恢复"
|
|
fi
|
|
|
|
if [ -d "$backup_dir/docker_config" ]; then
|
|
rm -rf "$PROJECT_ROOT/docker"
|
|
cp -r "$backup_dir/docker_config" "$PROJECT_ROOT/docker"
|
|
# 恢复脚本执行权限
|
|
find "$PROJECT_ROOT/docker" -name "*.sh" -exec chmod +x {} \;
|
|
print_message $GREEN "✓ Docker 配置已恢复"
|
|
fi
|
|
|
|
if [ -d "$backup_dir/ssl_certs" ]; then
|
|
rm -rf "$PROJECT_ROOT/ssl"
|
|
cp -r "$backup_dir/ssl_certs" "$PROJECT_ROOT/ssl"
|
|
chmod 600 "$PROJECT_ROOT/ssl"/*.key 2>/dev/null || true
|
|
print_message $GREEN "✓ SSL 证书已恢复"
|
|
fi
|
|
|
|
print_message $GREEN "✓ 配置文件恢复完成"
|
|
return 0
|
|
}
|
|
|
|
# 恢复上传文件
|
|
restore_uploads() {
|
|
local backup_file=$1
|
|
|
|
print_message $BLUE "恢复上传文件..."
|
|
|
|
if [ ! -f "$backup_file" ]; then
|
|
print_message $RED "备份文件不存在: $backup_file"
|
|
return 1
|
|
fi
|
|
|
|
# 备份当前上传文件
|
|
local uploads_dir="$PROJECT_ROOT/data/uploads"
|
|
if [ -d "$uploads_dir" ] && [ "$(ls -A "$uploads_dir" 2>/dev/null)" ]; then
|
|
local current_backup="$PROJECT_ROOT/uploads_backup_$(get_timestamp).tar.gz"
|
|
tar -czf "$current_backup" -C "$PROJECT_ROOT/data" uploads/
|
|
print_message $BLUE "当前上传文件已备份到: $current_backup"
|
|
fi
|
|
|
|
# 恢复上传文件
|
|
mkdir -p "$PROJECT_ROOT/data"
|
|
if tar -xzf "$backup_file" -C "$PROJECT_ROOT/data"; then
|
|
print_message $GREEN "✓ 上传文件恢复成功"
|
|
return 0
|
|
else
|
|
print_message $RED "✗ 上传文件恢复失败"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 完整恢复
|
|
full_restore() {
|
|
local backup_archive=$1
|
|
|
|
if [ ! -f "$backup_archive" ]; then
|
|
print_message $RED "备份文件不存在: $backup_archive"
|
|
return 1
|
|
fi
|
|
|
|
print_message $GREEN "========================================="
|
|
print_message $GREEN "开始完整恢复"
|
|
print_message $GREEN "========================================="
|
|
|
|
# 验证备份文件
|
|
if [ -f "$backup_archive.sha256" ]; then
|
|
print_message $BLUE "验证备份文件完整性..."
|
|
if sha256sum -c "$backup_archive.sha256"; then
|
|
print_message $GREEN "✓ 备份文件完整性验证通过"
|
|
else
|
|
print_message $RED "✗ 备份文件完整性验证失败"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# 解压备份文件
|
|
local temp_dir="/tmp/qaup_restore_$(get_timestamp)"
|
|
mkdir -p "$temp_dir"
|
|
|
|
print_message $BLUE "解压备份文件..."
|
|
if tar -xzf "$backup_archive" -C "$temp_dir"; then
|
|
print_message $GREEN "✓ 备份文件解压成功"
|
|
else
|
|
print_message $RED "✗ 备份文件解压失败"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# 查找备份目录
|
|
local backup_dir=$(find "$temp_dir" -maxdepth 1 -type d -name "*" | grep -v "^$temp_dir$" | head -1)
|
|
|
|
if [ ! -d "$backup_dir" ]; then
|
|
print_message $RED "未找到有效的备份目录"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# 读取备份清单
|
|
if [ -f "$backup_dir/MANIFEST.json" ]; then
|
|
print_message $BLUE "备份清单信息:"
|
|
cat "$backup_dir/MANIFEST.json" | grep -E '"backup_time"|"backup_type"|"total_size"' | sed 's/^ / /'
|
|
echo ""
|
|
fi
|
|
|
|
# 确认恢复操作
|
|
print_message $YELLOW "警告: 此操作将覆盖现有系统数据"
|
|
read -p "确定要继续完整恢复吗? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
print_message $BLUE "恢复操作已取消"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
local restore_success=true
|
|
|
|
# 恢复配置文件
|
|
if [ -d "$backup_dir/config" ]; then
|
|
restore_config "$backup_dir/config" || restore_success=false
|
|
fi
|
|
|
|
# 恢复数据库
|
|
if [ -f "$backup_dir/database"/*.sql.gz ]; then
|
|
restore_database "$backup_dir/database"/*.sql.gz || restore_success=false
|
|
elif [ -f "$backup_dir/database"/*.sql ]; then
|
|
restore_database "$backup_dir/database"/*.sql || restore_success=false
|
|
fi
|
|
|
|
# 恢复上传文件
|
|
if [ -f "$backup_dir/uploads"/*.tar.gz ]; then
|
|
restore_uploads "$backup_dir/uploads"/*.tar.gz || restore_success=false
|
|
fi
|
|
|
|
# 清理临时文件
|
|
rm -rf "$temp_dir"
|
|
|
|
if [ "$restore_success" = true ]; then
|
|
print_message $GREEN "========================================="
|
|
print_message $GREEN "完整恢复完成!"
|
|
print_message $GREEN "========================================="
|
|
echo ""
|
|
print_message $BLUE "建议执行以下操作:"
|
|
echo " 1. 重启所有服务: ./deploy.sh restart"
|
|
echo " 2. 验证系统状态: ./docker/monitor.sh status"
|
|
echo " 3. 检查应用功能: 访问前端页面测试"
|
|
echo ""
|
|
|
|
# 记录恢复历史
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') FULL_RESTORE SUCCESS $backup_archive" >> "$BACKUP_ROOT/restore_history.log"
|
|
|
|
return 0
|
|
else
|
|
print_message $RED "恢复过程中出现错误,请检查系统状态"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 列出备份文件
|
|
list_backups() {
|
|
print_message $BLUE "可用的备份文件:"
|
|
echo ""
|
|
|
|
if [ -d "$BACKUP_ROOT" ]; then
|
|
local backup_files=($(find "$BACKUP_ROOT" -name "qaup_full_backup_*.tar.gz" -type f | sort -r))
|
|
|
|
if [ ${#backup_files[@]} -eq 0 ]; then
|
|
print_message $YELLOW "未找到备份文件"
|
|
return
|
|
fi
|
|
|
|
local index=1
|
|
for backup_file in "${backup_files[@]}"; do
|
|
local filename=$(basename "$backup_file")
|
|
local size=$(du -h "$backup_file" | cut -f1)
|
|
local date=$(echo "$filename" | grep -o '[0-9]\{8\}_[0-9]\{6\}' | sed 's/_/ /')
|
|
|
|
echo "$index. $filename"
|
|
echo " 大小: $size"
|
|
echo " 时间: $date"
|
|
|
|
# 显示校验和状态
|
|
if [ -f "$backup_file.sha256" ]; then
|
|
echo " 校验: 可用"
|
|
else
|
|
echo " 校验: 不可用"
|
|
fi
|
|
|
|
echo ""
|
|
index=$((index + 1))
|
|
done
|
|
else
|
|
print_message $YELLOW "备份目录不存在: $BACKUP_ROOT"
|
|
fi
|
|
}
|
|
|
|
# 清理旧备份
|
|
cleanup_old_backups() {
|
|
local keep_days=${1:-30}
|
|
|
|
print_message $BLUE "清理 $keep_days 天前的备份文件..."
|
|
|
|
if [ ! -d "$BACKUP_ROOT" ]; then
|
|
print_message $YELLOW "备份目录不存在"
|
|
return
|
|
fi
|
|
|
|
local deleted_count=0
|
|
|
|
# 清理备份文件
|
|
while IFS= read -r -d '' file; do
|
|
rm -f "$file" "$file.sha256"
|
|
deleted_count=$((deleted_count + 1))
|
|
print_message $GREEN "已删除: $(basename "$file")"
|
|
done < <(find "$BACKUP_ROOT" -name "qaup_full_backup_*.tar.gz" -mtime +$keep_days -print0)
|
|
|
|
if [ $deleted_count -eq 0 ]; then
|
|
print_message $GREEN "没有需要清理的备份文件"
|
|
else
|
|
print_message $GREEN "已清理 $deleted_count 个旧备份文件"
|
|
fi
|
|
}
|
|
|
|
# 显示帮助信息
|
|
show_help() {
|
|
echo "QAUP 备份和恢复脚本"
|
|
echo ""
|
|
echo "用法: $0 [命令] [参数]"
|
|
echo ""
|
|
echo "备份命令:"
|
|
echo " full 完整备份"
|
|
echo " database 仅备份数据库"
|
|
echo " config 仅备份配置文件"
|
|
echo " uploads 仅备份上传文件"
|
|
echo ""
|
|
echo "恢复命令:"
|
|
echo " restore <backup_file> 完整恢复"
|
|
echo " restore-db <db_file> 仅恢复数据库"
|
|
echo " restore-config <dir> 仅恢复配置"
|
|
echo " restore-uploads <file> 仅恢复上传文件"
|
|
echo ""
|
|
echo "管理命令:"
|
|
echo " list 列出备份文件"
|
|
echo " cleanup [days] 清理旧备份 (默认30天)"
|
|
echo ""
|
|
echo "示例:"
|
|
echo " $0 full # 完整备份"
|
|
echo " $0 restore backup/qaup_full_backup_*.tar.gz # 完整恢复"
|
|
echo " $0 list # 列出备份"
|
|
echo " $0 cleanup 7 # 清理7天前的备份"
|
|
}
|
|
|
|
# 主函数
|
|
main() {
|
|
# 确保备份目录存在
|
|
mkdir -p "$BACKUP_ROOT"
|
|
|
|
if [ $# -eq 0 ]; then
|
|
show_help
|
|
exit 0
|
|
fi
|
|
|
|
local command=$1
|
|
shift
|
|
|
|
case $command in
|
|
full)
|
|
full_backup
|
|
;;
|
|
database)
|
|
local timestamp=$(get_timestamp)
|
|
local backup_dir=$(create_backup_dirs "$timestamp")
|
|
backup_database "$backup_dir" "$timestamp"
|
|
;;
|
|
config)
|
|
local timestamp=$(get_timestamp)
|
|
local backup_dir=$(create_backup_dirs "$timestamp")
|
|
backup_config "$backup_dir" "$timestamp"
|
|
;;
|
|
uploads)
|
|
local timestamp=$(get_timestamp)
|
|
local backup_dir=$(create_backup_dirs "$timestamp")
|
|
backup_uploads "$backup_dir" "$timestamp"
|
|
;;
|
|
restore)
|
|
if [ $# -eq 0 ]; then
|
|
print_message $RED "请指定备份文件"
|
|
exit 1
|
|
fi
|
|
full_restore "$1"
|
|
;;
|
|
restore-db)
|
|
if [ $# -eq 0 ]; then
|
|
print_message $RED "请指定数据库备份文件"
|
|
exit 1
|
|
fi
|
|
restore_database "$1"
|
|
;;
|
|
restore-config)
|
|
if [ $# -eq 0 ]; then
|
|
print_message $RED "请指定配置备份目录"
|
|
exit 1
|
|
fi
|
|
restore_config "$1"
|
|
;;
|
|
restore-uploads)
|
|
if [ $# -eq 0 ]; then
|
|
print_message $RED "请指定上传文件备份"
|
|
exit 1
|
|
fi
|
|
restore_uploads "$1"
|
|
;;
|
|
list)
|
|
list_backups
|
|
;;
|
|
cleanup)
|
|
cleanup_old_backups "$@"
|
|
;;
|
|
help|--help|-h)
|
|
show_help
|
|
;;
|
|
*)
|
|
print_message $RED "未知命令: $command"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@" |