265 lines
7.3 KiB
Bash
265 lines
7.3 KiB
Bash
#!/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
|