#!/usr/bin/env bash # # Gitea 远程备份脚本 # Author: System Administrator # Version: 1.0.0 # License: MIT # # 功能描述:通过SSH远程执行Gitea备份操作,并将备份文件同步到本地 # 依赖要求:ssh, rsync, docker, date, grep, awk 等基础工具 set -euo pipefail IFS=$'\n\t' ################################################################################ # 全局常量定义区 ################################################################################ readonly REMOTE_PORT="22333" readonly REMOTE_HOST="t0" readonly SCRIPT_DIR="/root/wdd/backup" readonly REMOTE_GITEA_CONTAINER="gitea-gitea-1" readonly REMOTE_GITEA_CONFIG="/bitnami/gitea/custom/conf/app.ini" readonly REMOTE_BACKUP_SOURCE="/data/gitea/gitea_data/data/tmp/gitea-dump-*.zip" readonly LOCAL_BACKUP_TARGET="/data/t0_150_230_198_103/gitea/" # > 日志配置 readonly LOG_DIR="${SCRIPT_DIR}/logs" readonly LOG_FILE="${LOG_DIR}/gitea_backup_$(date +%Y%m%d).log" # 日志级别常量 readonly LOG_LEVEL_DEBUG=0 readonly LOG_LEVEL_INFO=1 readonly LOG_LEVEL_WARN=2 readonly LOG_LEVEL_ERROR=3 # 当前日志级别(默认INFO) CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO} ################################################################################ # 函数声明区 ################################################################################ # 输出格式化日志信息(同时输出到控制台和日志文件) # @param level string 日志级别(DEBUG/INFO/WARN/ERROR) # @param message string 日志消息内容 # @return void # @require CURRENT_LOG_LEVEL, LOG_FILE log_message() { local level="$1" local message="$2" local timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') local log_entry="" case "$level" in "DEBUG") if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_DEBUG} ]; then log_entry="[DEBUG][${timestamp}] ${message}" echo "${log_entry}" echo "${log_entry}" >> "${LOG_FILE}" fi ;; "INFO") if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_INFO} ]; then log_entry="[INFO][${timestamp}] ${message}" echo "${log_entry}" echo "${log_entry}" >> "${LOG_FILE}" fi ;; "WARN") if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_WARN} ]; then log_entry="[WARN][${timestamp}] ${message}" echo "${log_entry}" >&2 echo "${log_entry}" >> "${LOG_FILE}" fi ;; "ERROR") if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_ERROR} ]; then log_entry="[ERROR][${timestamp}] ${message}" echo "${log_entry}" >&2 echo "${log_entry}" >> "${LOG_FILE}" fi ;; *) log_entry="[UNKNOWN][${timestamp}] ${message}" echo "${log_entry}" >&2 echo "${log_entry}" >> "${LOG_FILE}" ;; esac } ### # 执行远程SSH命令 # @param command string 需要执行的远程命令 # @return int 命令执行退出码 # @require REMOTE_HOST, REMOTE_PORT execute_remote_command() { local command="$1" local exit_code log_message "DEBUG" "执行远程命令: ${command}" # > 通过SSH连接到远程主机执行命令 ssh -p "${REMOTE_PORT}" "${REMOTE_HOST}" "${command}" exit_code=$? if [ ${exit_code} -ne 0 ]; then log_message "ERROR" "远程命令执行失败,退出码: ${exit_code}" return ${exit_code} fi return 0 } ### # 执行Gitea备份操作 # @return int 操作执行状态码 # @require REMOTE_GITEA_CONTAINER, REMOTE_GITEA_CONFIG perform_gitea_backup() { local backup_command="docker exec -i ${REMOTE_GITEA_CONTAINER} /opt/bitnami/gitea/bin/gitea dump -c ${REMOTE_GITEA_CONFIG}" log_message "INFO" "开始执行Gitea备份..." # > 执行Gitea dump命令生成备份文件 if ! execute_remote_command "${backup_command}"; then log_message "ERROR" "Gitea备份命令执行失败" return 1 fi log_message "INFO" "Gitea备份命令执行成功" return 0 } ### # 重命名备份文件(添加时间戳) # @return int 操作执行状态码 # @require REMOTE_GITEA_CONTAINER rename_backup_file() { local rename_command="docker exec -i ${REMOTE_GITEA_CONTAINER} /bin/sh -c \"mv /opt/bitnami/gitea/gitea-dump-*.zip /opt/bitnami/gitea/data/tmp/gitea-dump-\$(date +%Y%m%d-%H%M%S).zip\"" log_message "INFO" "开始重命名备份文件..." # > 在容器内重命名备份文件,添加时间戳 if ! execute_remote_command "${rename_command}"; then log_message "ERROR" "备份文件重命名失败" return 1 fi log_message "INFO" "备份文件重命名成功" return 0 } ### # 同步备份文件到本地 # @return int 操作执行状态码 # @require REMOTE_HOST, REMOTE_PORT, REMOTE_BACKUP_SOURCE, LOCAL_BACKUP_TARGET sync_backup_to_local() { log_message "INFO" "开始同步备份文件到本地..." # > 创建本地目标目录(如果不存在) if [ ! -d "${LOCAL_BACKUP_TARGET}" ]; then mkdir -p "${LOCAL_BACKUP_TARGET}" log_message "DEBUG" "创建本地目录: ${LOCAL_BACKUP_TARGET}" fi # > 使用rsync同步文件,保留关键属性 rsync -avz -e "ssh -p ${REMOTE_PORT}" \ "${REMOTE_HOST}:${REMOTE_BACKUP_SOURCE}" \ "${LOCAL_BACKUP_TARGET}" local exit_code=$? if [ ${exit_code} -ne 0 ]; then log_message "ERROR" "rsync同步失败,退出码: ${exit_code}" return ${exit_code} fi log_message "INFO" "备份文件同步成功" return 0 } ### # 清理远程备份文件 # @return int 操作执行状态码 # @require REMOTE_BACKUP_SOURCE cleanup_remote_backup() { local cleanup_command="rm -f ${REMOTE_BACKUP_SOURCE}" log_message "INFO" "开始清理远程备份文件..." # > 删除远程主机上的临时备份文件 if ! execute_remote_command "${cleanup_command}"; then log_message "ERROR" "远程备份文件清理失败" return 1 fi log_message "INFO" "远程备份文件清理成功" return 0 } ### # 主执行函数 - 协调整个备份流程 # @return int 脚本执行最终状态码 main() { local overall_success=true log_message "INFO" "=== Gitea备份流程开始 ===" # 切换到工作目录 cd "${SCRIPT_DIR}" || { log_message "ERROR" "无法切换到工作目录: ${SCRIPT_DIR}" return 1 } # 执行备份流程 if ! perform_gitea_backup; then overall_success=false fi if ! rename_backup_file; then overall_success=false fi if ! sync_backup_to_local; then overall_success=false fi if ! cleanup_remote_backup; then overall_success=false fi # 汇总执行结果 if [ "${overall_success}" = true ]; then log_message "INFO" "=== Gitea备份流程完成 ===" return 0 else log_message "ERROR" "=== Gitea备份流程部分失败 ===" return 1 fi } ################################################################################ # 异常处理设置 ################################################################################ # 设置trap捕获信号 trap 'log_message "ERROR" "脚本被中断"; exit 1' INT TERM ################################################################################ # 主执行流程 ################################################################################ # 函数调用关系: # main -> perform_gitea_backup -> execute_remote_command # -> rename_backup_file -> execute_remote_command # -> sync_backup_to_local # -> cleanup_remote_backup -> execute_remote_command # 执行主函数 if main; then log_message "INFO" "脚本执行成功" exit 0 else log_message "ERROR" "脚本执行失败" exit 1 fi