# === 使用阿里云镜像源加速构建 === RUN sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list && \ sed -i 's|http://security.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list
echo"[$(date +'%F %T')] ===== 开始 PostgreSQL 备份任务 =====" | tee -a "$LOG_FILE"
# === 测试数据库连接 === test_db_connection() { export PGPASSWORD="$POSTGRES_PASSWORD" echo"[$(date +'%F %T')] [INFO] 测试数据库连接..." | tee -a "$LOG_FILE" if ! psql -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d postgres -c "SELECT 1;" > /dev/null 2>&1; then echo"[$(date +'%F %T')] [ERROR] 无法连接到 PostgreSQL 服务器" | tee -a "$LOG_FILE" echo"[$(date +'%F %T')] [INFO] 请检查: " | tee -a "$LOG_FILE" echo" - 主机: $POSTGRES_HOST" | tee -a "$LOG_FILE" echo" - 端口: $POSTGRES_PORT" | tee -a "$LOG_FILE" echo" - 用户: $POSTGRES_USER" | tee -a "$LOG_FILE" echo" - 密码: ******" | tee -a "$LOG_FILE" echo" - 网络连接" | tee -a "$LOG_FILE" return 1 fi echo"[$(date +'%F %T')] [OK] 数据库连接成功" | tee -a "$LOG_FILE" return 0 }
# === 格式化时间函数 === format_time() { local ELAPSED=$1 local H=$((ELAPSED / 3600)) local M=$(((ELAPSED % 3600) / 60)) local S=$((ELAPSED % 60)) local OUTPUT="" [[ $H -gt 0 ]] && OUTPUT+="${H} 小时 " [[ $M -gt 0 ]] && OUTPUT+="${M} 分 " OUTPUT+="${S} 秒" echo"$OUTPUT" }
# === 单个数据库备份函数 === perform_backup() { local DB_NAME="$1" local OUTPUT_FILE="$2" local RETRY=0 local MAX_RETRY=3 local START_TIME=$(date +%s) local TMP_LOG="/tmp/postgres_backup_${DB_NAME}_$$.log" local TMP_SQL="/tmp/backup_${DB_NAME}_$$.sql"
export PGPASSWORD="$POSTGRES_PASSWORD"
while [[ $RETRY -lt $MAX_RETRY ]]; do echo"[$(date +'%F %T')] [INFO] 正在导出数据库: $DB_NAME (尝试第 $((RETRY + 1)) 次)" | tee -a "$LOG_FILE" # 使用更详细的错误输出 if pg_dump -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" \ -F p -c --verbose "$DB_NAME" > "$TMP_SQL" 2> "$TMP_LOG"; then if [[ -s "$TMP_SQL" ]]; then if gzip -c "$TMP_SQL" > "$OUTPUT_FILE"; then END_TIME=$(date +%s) DURATION=$((END_TIME - START_TIME)) FILE_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1) echo"[$(date +'%F %T')] [OK] 备份成功: $OUTPUT_FILE" | tee -a "$LOG_FILE" echo"[$(date +'%F %T')] [INFO] 耗时: $(format_time "$DURATION"),文件大小: $FILE_SIZE" | tee -a "$LOG_FILE" rm -f "$TMP_SQL""$TMP_LOG" return 0 else echo"[$(date +'%F %T')] [ERROR] 压缩失败: $DB_NAME" | tee -a "$LOG_FILE" fi else echo"[$(date +'%F %T')] [WARN] 导出的 SQL 文件为空: $DB_NAME" | tee -a "$LOG_FILE" fi fi # 输出详细错误信息 if [[ -f "$TMP_LOG" ]]; then echo"[$(date +'%F %T')] [ERROR] 备份错误详情:" | tee -a "$LOG_FILE" cat"$TMP_LOG" | tee -a "$LOG_FILE" fi if [[ $RETRY -lt $((MAX_RETRY - 1)) ]]; then echo"[$(date +'%F %T')] [WARN] 第 $((RETRY + 1)) 次备份失败,5 秒后重试..." | tee -a "$LOG_FILE" sleep 5 fi RETRY=$((RETRY + 1)) done
echo"[$(date +'%F %T')] [ERROR] 备份失败: $DB_NAME,放弃重试。" | tee -a "$LOG_FILE" FAILED_DATABASES+=("$DB_NAME") return 1 }
# === 主备份逻辑 ===
# 首先测试数据库连接 if ! test_db_connection; then exit 1 fi
# 1. 备份所有数据库 if [[ "$ALL_DATABASES" == "true" ]]; then BACKUP_FILE="$BACKUP_DIR/all_databases_${TIMESTAMP}.sql.gz" echo"[$(date +'%F %T')] [INFO] 备份所有数据库到 $BACKUP_FILE" | tee -a "$LOG_FILE" export PGPASSWORD="$POSTGRES_PASSWORD" if pg_dumpall -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" \ --verbose > /tmp/all_databases.sql 2>> "$LOG_FILE"; then if [[ -s /tmp/all_databases.sql ]]; then if gzip -c /tmp/all_databases.sql > "$BACKUP_FILE"; then echo"[$(date +'%F %T')] [OK] 所有数据库备份成功: $BACKUP_FILE" | tee -a "$LOG_FILE" else echo"[$(date +'%F %T')] [ERROR] 压缩所有数据库备份失败" | tee -a "$LOG_FILE" STATUS=1 fi else echo"[$(date +'%F %T')] [ERROR] 所有数据库备份文件为空" | tee -a "$LOG_FILE" STATUS=1 fi rm -f /tmp/all_databases.sql else echo"[$(date +'%F %T')] [ERROR] 所有数据库备份失败" | tee -a "$LOG_FILE" STATUS=1 fi
# 2. 自动获取数据库列表并分别备份 elif [[ "$AUTO_DB_BACKUP" == "true" ]]; then echo"[$(date +'%F %T')] [INFO] 获取数据库列表..." | tee -a "$LOG_FILE" export PGPASSWORD="$POSTGRES_PASSWORD" # 排除模板数据库和默认系统数据库 DB_LIST=$(psql -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d postgres \ -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname NOT IN ('postgres');" 2>> "$LOG_FILE") if [[ -z "$DB_LIST" ]]; then echo"[$(date +'%F %T')] [WARN] 未找到可备份的数据库" | tee -a "$LOG_FILE" else echo"[$(date +'%F %T')] [INFO] 找到数据库: $(echo $DB_LIST | tr '\n' ' ')" | tee -a "$LOG_FILE" for DB in$DB_LIST; do BACKUP_FILE="$BACKUP_DIR/${DB}_$TIMESTAMP.sql.gz" echo"[$(date +'%F %T')] [INFO] 开始备份数据库: $DB" | tee -a "$LOG_FILE" perform_backup "$DB""$BACKUP_FILE" || STATUS=1 done fi
# 3. 备份指定数据库 elif [[ -n "$POSTGRES_DB" ]]; then BACKUP_FILE="$BACKUP_DIR/${POSTGRES_DB}_$TIMESTAMP.sql.gz" echo"[$(date +'%F %T')] [INFO] 备份指定数据库: $POSTGRES_DB 到 $BACKUP_FILE" | tee -a "$LOG_FILE" perform_backup "$POSTGRES_DB""$BACKUP_FILE" || STATUS=1
# 4. 未指定任何备份方式 else echo"[$(date +'%F %T')] [WARN] 未设置数据库备份目标。" | tee -a "$LOG_FILE" echo"请设置以下环境变量之一:" | tee -a "$LOG_FILE" echo" - POSTGRES_DB=数据库名" | tee -a "$LOG_FILE" echo" - ALL_DATABASES=true" | tee -a "$LOG_FILE" echo" - AUTO_DB_BACKUP=true" | tee -a "$LOG_FILE" exit 1 fi
# === 清理过期文件 === echo"[$(date +'%F %T')] [INFO] 正在清理 $RETENTION_DAYS 天前的备份文件..." | tee -a "$LOG_FILE" find "$BACKUP_DIR" -type f -name "*.sql.gz" -mtime "+$RETENTION_DAYS" -delete 2>/dev/null || true
# === 输出失败数据库列表(如有) === if [[ ${#FAILED_DATABASES[@]} -gt 0 ]]; then echo"[$(date +'%F %T')] [ERROR] 以下数据库备份失败:" | tee -a "$LOG_FILE" for DB in"${FAILED_DATABASES[@]}"; do echo" - $DB" | tee -a "$LOG_FILE" done fi
# === 输出总耗时 === TOTAL_TIME=$(format_time "$SECONDS") if [[ $STATUS -eq 0 ]]; then echo"[$(date +'%F %T')] [SUCCESS] 备份任务完成,总耗时: $TOTAL_TIME" | tee -a "$LOG_FILE" else echo"[$(date +'%F %T')] [WARNING] 备份任务完成,但有错误,总耗时: $TOTAL_TIME" | tee -a "$LOG_FILE" fi
echo"[$(date +'%F %T')] ===== PostgreSQL 备份任务结束 ===== (状态码: $STATUS)" | tee -a "$LOG_FILE"