备份需求
每天凌晨把docker中的mysql库进行备份,并将备份后的文件加密压缩。
备份shell
经过AI简单交互,得到一个shell如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
| #!/bin/bash
# MySQL备份脚本 - 企业级版(支持日志文件、压缩、加密和跳过指定表) # 解密: openssl enc -d -aes-256-cbc -in /path/to/backup.sql.gz.enc -out /path/to/backup.sql.gz # 再解压: gzip -d /path/to/backup.sql.gz CONTAINER_NAME="mysql" BACKUP_DIR="/path/to/backups" LOG_FILE="${BACKUP_DIR}/backup.log" USERNAME="用户" PASSWORD="密码" MAX_BACKUPS=30 # 保存备份数量 USE_SINGLE_TRANSACTION=1 # 1=使用事务备份(InnoDB) 0=跳过锁表 ENABLE_ENCRYPTION=1 # 1=启用加密 0=禁用加密 ENCRYPTION_ALGO="aes-256-cbc" ENCRYPTION_PASSWORD="${PASSWORD}" # 目前和数据库密码一致,也可以修改。留空则交互式输入
# 要备份的数据库列表(空格分隔) BACKUP_DATABASES="db1 db2"
# 要跳过的表列表(格式:数据库名.表名,空格分隔) SKIP_TABLES=( "db1.log_table" "db2.large_table" )
# 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # 无颜色
# 日志函数 - 同时输出到终端和日志文件 log_info() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" echo -e "${GREEN}[INFO]${NC} $1" echo "$msg" >> "$LOG_FILE" }
log_warn() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $1" echo -e "${YELLOW}[WARN]${NC} $1" echo "$msg" >> "$LOG_FILE" }
log_error() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" echo -e "${RED}[ERROR]${NC} $1" echo "$msg" >> "$LOG_FILE" }
# 创建备份目录和日志文件 mkdir -p "$BACKUP_DIR" || { log_error "无法创建备份目录: $BACKUP_DIR"; exit 1; } touch "$LOG_FILE" || { log_error "无法创建日志文件: $LOG_FILE"; exit 1; }
# 输出备份开始信息 log_info "=====================================" log_info "开始执行MySQL数据库备份脚本" log_info "备份目录: $BACKUP_DIR" log_info "日志文件: $LOG_FILE" log_info "====================================="
# 检查依赖命令 for cmd in docker gzip openssl; do command -v $cmd >/dev/null 2>&1 || { log_error "需要安装 $cmd 命令"; exit 1; } done
# 检查容器是否运行 docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true" || { log_error "容器 $CONTAINER_NAME 未运行" exit 1 }
# 验证数据库连接 docker exec "$CONTAINER_NAME" mysql -h 127.0.0.1 -u"$USERNAME" -p"$PASSWORD" -e "SELECT 1" >/dev/null 2>&1 || { log_error "无法连接到MySQL,请检查用户名和密码" exit 1 }
# 构建--ignore-table参数 build_ignore_tables() { local db_name="$1" local ignore_params="" for table_spec in "${SKIP_TABLES[@]}"; do db_part="${table_spec%%.*}" table_part="${table_spec##*.}" if [ "$db_part" = "$db_name" ]; then ignore_params+=" --ignore-table=${db_name}.${table_part}" fi done echo "$ignore_params" }
# 加密函数 encrypt_file() { local input_file="$1" local output_file="${input_file}.enc" log_info "正在加密: $input_file" if [ -z "$ENCRYPTION_PASSWORD" ]; then # 交互式输入密码 openssl enc -$ENCRYPTION_ALGO -salt -in "$input_file" -out "$output_file" 2>> "$LOG_FILE" else # 使用预设密码(不安全,建议仅用于自动化脚本) openssl enc -$ENCRYPTION_ALGO -salt -pass "pass:$ENCRYPTION_PASSWORD" -in "$input_file" -out "$output_file" 2>> "$LOG_FILE" fi if [ $? -eq 0 ]; then log_info "加密完成: $output_file" rm -f "$input_file" # 删除未加密的文件 return 0 else log_error "加密失败" rm -f "$output_file" # 删除可能不完整的加密文件 return 1 fi }
# 主备份流程 for DB_NAME in $BACKUP_DATABASES; do BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql" FINAL_FILE="$BACKUP_FILE" log_info "开始备份数据库 '$DB_NAME'" # 构建忽略表参数 IGNORE_PARAMS=$(build_ignore_tables "$DB_NAME") # 打印跳过的表信息 # if [ -n "$IGNORE_PARAMS" ]; then # log_info "将跳过以下表: $(echo "$IGNORE_PARAMS" | sed 's/ --ignore-table=/-/g' | sed 's/^-//')" # fi # 执行备份 if [ $USE_SINGLE_TRANSACTION -eq 1 ]; then docker exec "$CONTAINER_NAME" mysqldump -h 127.0.0.1 -u"$USERNAME" -p"$PASSWORD" --single-transaction --databases "$DB_NAME" $IGNORE_PARAMS > "$BACKUP_FILE" 2>> "$LOG_FILE" else docker exec "$CONTAINER_NAME" mysqldump -h 127.0.0.1 -u"$USERNAME" -p"$PASSWORD" --skip-lock-tables --databases "$DB_NAME" $IGNORE_PARAMS > "$BACKUP_FILE" 2>> "$LOG_FILE" fi # 检查备份是否成功 if [ $? -ne 0 ]; then log_error "备份数据库 '$DB_NAME' 失败" rm -f "$BACKUP_FILE" continue fi # 获取备份文件大小 FILE_SIZE=$(du -h "$BACKUP_FILE" | awk '{print $1}') log_info "备份文件大小: $FILE_SIZE" # 压缩文件 log_info "正在压缩备份文件" gzip -9 "$BACKUP_FILE" 2>> "$LOG_FILE" || { log_error "压缩失败" rm -f "$BACKUP_FILE" continue } BACKUP_FILE="${BACKUP_FILE}.gz" # 获取压缩后文件大小 GZIP_SIZE=$(du -h "$BACKUP_FILE" | awk '{print $1}') log_info "压缩后文件大小: $GZIP_SIZE" # 加密文件 if [ $ENABLE_ENCRYPTION -eq 1 ]; then encrypt_file "$BACKUP_FILE" || continue FINAL_FILE="${BACKUP_FILE}.enc" # 获取加密后文件大小 ENC_SIZE=$(du -h "$FINAL_FILE" | awk '{print $1}') log_info "加密后文件大小: $ENC_SIZE" else FINAL_FILE="$BACKUP_FILE" fi log_info "数据库 '$DB_NAME' 备份完成: $FINAL_FILE" # 清理旧备份 log_info "清理旧备份..." if [ $ENABLE_ENCRYPTION -eq 1 ]; then old_backups=$(ls -t "${BACKUP_DIR}/${DB_NAME}"*".sql.gz.enc" 2>/dev/null | tail -n +$((MAX_BACKUPS + 1))) else old_backups=$(ls -t "${BACKUP_DIR}/${DB_NAME}"*".sql.gz" 2>/dev/null | tail -n +$((MAX_BACKUPS + 1))) fi if [ -n "$old_backups" ]; then echo "$old_backups" | xargs -r rm -f log_info "已清理 $(echo "$old_backups" | wc -l) 个旧备份文件" else log_info "没有需要清理的旧备份文件" fi done
# 输出备份完成信息 log_info "=====================================" log_info "所有数据库备份完成!" log_info "日志文件位置: $LOG_FILE" log_info "====================================="
|
恢复命令
配置好crontab就可以用了,后续解密、解压如下:
1 2 3 4 5 6 7 8
| openssl enc -d -aes-256-cbc -in /path/to/backup.sql.gz.enc -out /path/to/backup.sql.gz
gzip -d backup.sql.gz
docker exec -i CONTAINER_NAME mysql -u USER_NAME -pPASSWORD DBNAME < backup.sql
|