- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
145 lines
4.1 KiB
Bash
Executable File
145 lines
4.1 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# 考陪练系统数据库自动备份脚本
|
||
# 作者: AI Assistant
|
||
# 日期: 2025-09-23
|
||
|
||
set -e # 遇到错误立即退出
|
||
|
||
# 配置变量
|
||
BACKUP_DIR="/root/aiedu/kaopeilian-backend/backups"
|
||
CONTAINER_NAME="kaopeilian_mysql"
|
||
DB_NAME="kaopeilian"
|
||
DB_USER="root"
|
||
DB_PASSWORD="Kaopeilian2025!@#"
|
||
DATE=$(date +%Y%m%d_%H%M%S)
|
||
BACKUP_FILE="${BACKUP_DIR}/kaopeilian_backup_${DATE}.sql"
|
||
LOG_FILE="${BACKUP_DIR}/backup.log"
|
||
RETENTION_DAYS=30 # 保留30天的备份
|
||
|
||
# 创建备份目录
|
||
mkdir -p "${BACKUP_DIR}"
|
||
|
||
# 日志函数
|
||
log() {
|
||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
|
||
}
|
||
|
||
# 检查Docker容器是否运行
|
||
check_container() {
|
||
if ! docker ps | grep -q "${CONTAINER_NAME}"; then
|
||
log "ERROR: MySQL容器 ${CONTAINER_NAME} 未运行"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 执行数据库备份
|
||
backup_database() {
|
||
log "开始备份Docker容器中的数据库 ${DB_NAME}..."
|
||
|
||
# 检查容器是否运行
|
||
if ! docker ps | grep -q "${CONTAINER_NAME}"; then
|
||
log "ERROR: MySQL容器 ${CONTAINER_NAME} 未运行"
|
||
return 1
|
||
fi
|
||
|
||
# 使用docker exec执行mysqldump,将输出重定向到宿主机文件
|
||
if docker exec "${CONTAINER_NAME}" mysqldump \
|
||
-u"${DB_USER}" \
|
||
-p"${DB_PASSWORD}" \
|
||
--single-transaction \
|
||
--routines \
|
||
--triggers \
|
||
--events \
|
||
--hex-blob \
|
||
--default-character-set=utf8mb4 \
|
||
--lock-tables=false \
|
||
--add-drop-database \
|
||
--create-options \
|
||
"${DB_NAME}" > "${BACKUP_FILE}" 2>/dev/null; then
|
||
|
||
# 检查备份文件大小
|
||
if [ -f "${BACKUP_FILE}" ] && [ -s "${BACKUP_FILE}" ]; then
|
||
BACKUP_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||
log "备份完成: ${BACKUP_FILE} (大小: ${BACKUP_SIZE})"
|
||
|
||
# 验证备份文件内容(检查是否包含CREATE DATABASE语句)
|
||
if grep -q "CREATE DATABASE" "${BACKUP_FILE}"; then
|
||
log "备份文件验证成功"
|
||
return 0
|
||
else
|
||
log "WARNING: 备份文件可能不完整"
|
||
return 0 # 仍然算作成功,但记录警告
|
||
fi
|
||
else
|
||
log "ERROR: 备份文件为空或不存在"
|
||
rm -f "${BACKUP_FILE}"
|
||
return 1
|
||
fi
|
||
else
|
||
log "ERROR: 数据库备份失败"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 压缩备份文件
|
||
compress_backup() {
|
||
if [ -f "${BACKUP_FILE}" ]; then
|
||
log "压缩备份文件..."
|
||
gzip "${BACKUP_FILE}"
|
||
COMPRESSED_FILE="${BACKUP_FILE}.gz"
|
||
COMPRESSED_SIZE=$(du -h "${COMPRESSED_FILE}" | cut -f1)
|
||
log "压缩完成: ${COMPRESSED_FILE} (大小: ${COMPRESSED_SIZE})"
|
||
fi
|
||
}
|
||
|
||
# 清理过期备份
|
||
cleanup_old_backups() {
|
||
log "清理 ${RETENTION_DAYS} 天前的备份文件..."
|
||
find "${BACKUP_DIR}" -name "kaopeilian_backup_*.sql.gz" -mtime +${RETENTION_DAYS} -delete
|
||
find "${BACKUP_DIR}" -name "kaopeilian_backup_*.sql" -mtime +${RETENTION_DAYS} -delete
|
||
log "过期备份清理完成"
|
||
}
|
||
|
||
# 发送备份状态通知(可选)
|
||
send_notification() {
|
||
local status=$1
|
||
local message=$2
|
||
|
||
# 这里可以集成邮件、钉钉、微信等通知方式
|
||
log "通知: ${status} - ${message}"
|
||
|
||
# 示例:写入状态文件供监控系统读取
|
||
echo "{\"timestamp\":\"$(date -Iseconds)\",\"status\":\"${status}\",\"message\":\"${message}\"}" > "${BACKUP_DIR}/backup_status.json"
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
log "========== 数据库备份开始 =========="
|
||
|
||
# 检查容器状态
|
||
check_container
|
||
|
||
# 执行备份
|
||
if backup_database; then
|
||
# 压缩备份
|
||
compress_backup
|
||
|
||
# 清理过期备份
|
||
cleanup_old_backups
|
||
|
||
# 发送成功通知
|
||
send_notification "SUCCESS" "数据库备份成功完成"
|
||
log "========== 数据库备份完成 =========="
|
||
exit 0
|
||
else
|
||
# 发送失败通知
|
||
send_notification "FAILED" "数据库备份失败"
|
||
log "========== 数据库备份失败 =========="
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 执行主函数
|
||
main "$@"
|