#!/bin/bash # ============================================================================= # nextcloud备份脚本 # 功能:远程Nextcloud维护模式切换、数据库备份、文件同步及清理 # 版本:1.0.0 # 作者:Shell脚本工程师 # 许可证:MIT License # 依赖:ssh, rsync, docker (远程主机), mariadb-client (远程主机) # ============================================================================= set -euo pipefail IFS=$'\n\t' # > 全局常量定义 readonly SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_DIR="/root/wdd/backup" readonly LOCK_FILE="/root/wdd/backup/${SCRIPT_NAME}.lock" # > 远程主机配置 readonly REMOTE_HOST="s5" readonly REMOTE_PORT="22333" readonly REMOTE_USER="root" readonly REMOTE_NEXTCLOUD_DIR="/data/nextcloud" readonly REMOTE_DB_CONTAINER="nextcloud-db" readonly REMOTE_WEB_CONTAINER="nextcloud_web" # > 数据库配置 readonly DB_NAME="nextcloud" readonly DB_USER="nextcloud" readonly DB_PASSWORD="boge14@Level5" # > 本地配置 readonly LOCAL_BACKUP_DIR="/data/s5_146-56-159-175/nextcloud" # > 日志配置 readonly LOG_DIR="${SCRIPT_DIR}/logs" readonly LOG_FILE="${LOG_DIR}/nextcloud_backup_$(date +%Y%m%d).log" # > 颜色输出定义 readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' # ============================================================================= # 日志函数集 # ============================================================================= ### # 初始化日志系统 # @require 无 # @return 0 成功 | >0 失败 ### init_log_system() { mkdir -p "${LOG_DIR}" || return 1 touch "${LOG_FILE}" || return 1 return 0 } ### # 记录日志消息 # @param level string 日志级别(DEBUG/INFO/WARN/ERROR) # @param message string 日志消息 # @require LOG_FILE # @return 0 成功 ### log_message() { local level="$1" local message="$2" local timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') case "${level}" in "DEBUG") echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;; "INFO") echo -e "${GREEN}[INFO]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;; "WARN") echo -e "${YELLOW}[WARN]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" >&2 ;; "ERROR") echo -e "${RED}[ERROR]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" >&2 ;; *) echo "${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;; esac return 0 } # ============================================================================= # 工具函数集 # ============================================================================= ### # 检查命令是否存在 # @param command_name string 命令名称 # @require 无 # @return 0 存在 | 1 不存在 ### check_command() { local command_name="$1" if ! command -v "${command_name}" >/dev/null 2>&1; then log_message "ERROR" "命令不存在: ${command_name}" return 1 fi return 0 } ### # 执行远程SSH命令 # @param command string 要执行的命令 # @require REMOTE_HOST, REMOTE_PORT, REMOTE_USER # @return 远程命令的退出码 ### execute_remote_command() { local command="$1" ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "${command}" return $? } ### # 创建锁文件防止并发执行 # @require LOCK_FILE # @return 0 成功获取锁 | 1 锁已存在 ### acquire_lock() { if [ -e "${LOCK_FILE}" ]; then log_message "ERROR" "备份任务正在运行或异常退出,请检查锁文件: ${LOCK_FILE}" return 1 fi echo "$$" > "${LOCK_FILE}" trap 'release_lock' EXIT return 0 } ### # 释放锁文件 # @require LOCK_FILE # @return 0 成功 ### release_lock() { [ -e "${LOCK_FILE}" ] && rm -f "${LOCK_FILE}" return 0 } # ============================================================================= # Nextcloud核心备份函数 # ============================================================================= ### # 启用Nextcloud维护模式 # @require execute_remote_command, REMOTE_WEB_CONTAINER # @return 0 成功 | >0 失败 ### enable_maintenance_mode() { log_message "INFO" "启用Nextcloud维护模式..." local maintenance_cmd="docker exec -u www-data ${REMOTE_WEB_CONTAINER} php occ maintenance:mode --on" if ! execute_remote_command "${maintenance_cmd}"; then log_message "ERROR" "启用维护模式失败" return 1 fi log_message "INFO" "维护模式已启用" return 0 } ### # 禁用Nextcloud维护模式 # @require execute_remote_command, REMOTE_WEB_CONTAINER # @return 0 成功 | >0 失败 ### disable_maintenance_mode() { log_message "INFO" "禁用Nextcloud维护模式..." local maintenance_cmd="docker exec -u www-data ${REMOTE_WEB_CONTAINER} php occ maintenance:mode --off" if ! execute_remote_command "${maintenance_cmd}"; then log_message "ERROR" "禁用维护模式失败" return 1 fi log_message "INFO" "维护模式已禁用" return 0 } ### # 远程执行MariaDB数据库备份 # @require execute_remote_command, REMOTE_DB_CONTAINER, DB_NAME, DB_USER, DB_PASSWORD, REMOTE_NEXTCLOUD_DIR # @return 0 成功 | >0 失败 ### backup_database() { log_message "INFO" "开始数据库备份..." local backup_file="${REMOTE_NEXTCLOUD_DIR}/nextcloud-db_backup_$(date +%Y%m%d-%H%M%S).sql" local backup_cmd="docker exec ${REMOTE_DB_CONTAINER} mariadb-dump --single-transaction -h localhost -u ${DB_USER} -p'${DB_PASSWORD}' ${DB_NAME} > ${backup_file}" if ! execute_remote_command "${backup_cmd}"; then log_message "ERROR" "数据库备份失败" return 1 fi # > 验证备份文件是否创建成功 local verify_cmd="[ -f \"${backup_file}\" ] && echo \"exists\" || echo \"missing\"" if [ "$(execute_remote_command "${verify_cmd}")" != "exists" ]; then log_message "ERROR" "数据库备份文件创建失败" return 1 fi log_message "INFO" "数据库备份完成: ${backup_file}" return 0 } ### # 使用rsync同步Nextcloud文件到本地 # @require REMOTE_HOST, REMOTE_PORT, REMOTE_USER, REMOTE_NEXTCLOUD_DIR, LOCAL_BACKUP_DIR # @return 0 成功 | >0 失败 ### sync_nextcloud_files() { log_message "INFO" "开始同步Nextcloud文件到本地..." # > 创建本地暂存目录 mkdir -p "${LOCAL_BACKUP_DIR}" || { log_message "ERROR" "创建本地暂存目录失败: ${LOCAL_BACKUP_DIR}" return 1 } # > 构建rsync命令 local rsync_cmd="rsync -avz --progress -e 'ssh -p ${REMOTE_PORT}'" rsync_cmd+=" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_NEXTCLOUD_DIR}/" rsync_cmd+=" ${LOCAL_BACKUP_DIR}/" # > 执行rsync同步 if ! eval "${rsync_cmd}"; then log_message "ERROR" "Nextcloud文件同步失败" return 1 fi log_message "INFO" "Nextcloud文件同步完成" return 0 } ### # 远程删除数据库备份文件 # @require execute_remote_command, REMOTE_NEXTCLOUD_DIR # @return 0 成功 | >0 失败 ### remote_cleanup_backup() { log_message "INFO" "清理远程数据库备份文件..." local cleanup_cmd="rm -f ${REMOTE_NEXTCLOUD_DIR}/nextcloud-db_backup_*.sql" if ! execute_remote_command "${cleanup_cmd}"; then log_message "ERROR" "远程清理失败" return 1 fi log_message "INFO" "远程清理完成" return 0 } ### # 清理本地暂存目录 # @require LOCAL_BACKUP_DIR # @return 0 成功 ### local_cleanup() { log_message "INFO" "清理本地暂存目录..." [ -d "${LOCAL_BACKUP_DIR}" ] && rm -rf "${LOCAL_BACKUP_DIR}" return 0 } # ============================================================================= # 主执行流程 # ============================================================================= ### # 主备份流程 # @require 所有上述函数 # @return 0 成功 | >0 失败 ### main_backup_process() { log_message "INFO" "=== 开始Nextcloud备份任务 ===" # > 检查依赖命令 local required_commands=("ssh" "rsync") for cmd in "${required_commands[@]}"; do if ! check_command "${cmd}"; then return 1 fi done # > 执行备份流程 local steps=( enable_maintenance_mode backup_database sync_nextcloud_files remote_cleanup_backup disable_maintenance_mode # local_cleanup ) for step in "${steps[@]}"; do if ! "${step}"; then log_message "ERROR" "备份任务失败,正在尝试恢复..." # > 尝试禁用维护模式 disable_maintenance_mode || true return 1 fi done log_message "INFO" "=== Nextcloud备份任务完成 ===" return 0 } # ============================================================================= # 脚本入口点 # ============================================================================= # > 设置错误处理 trap 'log_message "ERROR" "脚本异常退出"; disable_maintenance_mode || true; release_lock; exit 1' ERR # > 主执行块 main() { if ! acquire_lock; then exit 1 fi if ! init_log_system; then log_message "ERROR" "日志系统初始化失败" exit 1 fi if ! main_backup_process; then log_message "ERROR" "备份任务执行失败" exit 1 fi release_lock exit 0 } # > 脚本执行入口 main "$@" # ============================================================================= # 函数调用关系图 # ============================================================================= # main # ├── acquire_lock # ├── init_log_system # └── main_backup_process # ├── check_command (多次调用) # ├── enable_maintenance_mode # │ └── execute_remote_command # ├── backup_database # │ └── execute_remote_command # ├── sync_nextcloud_files # ├── move_to_backup_dir # ├── remote_cleanup_backup # │ └── execute_remote_command # ├── disable_maintenance_mode # │ └── execute_remote_command # └── local_cleanup