GuDingZiChan/scripts/backup-equipment.sh

109 lines
3.4 KiB
Bash

#!/bin/bash
# 固定资产系统备份脚本
set -euo pipefail
PROJECT_DIR="${PROJECT_DIR:-/opt/equipment-management}"
PROJECT_NAME="${PROJECT_NAME:-equipment-management}"
BACKUP_ROOT="${BACKUP_ROOT:-/opt/backups/equipment-management}"
KEEP_BACKUPS="${KEEP_BACKUPS:-3}"
DB_CONTAINER="${DB_CONTAINER:-equipment-mysql}"
BACKEND_CONTAINER="${BACKEND_CONTAINER:-equipment-backend}"
DB_NAME="${DB_NAME:-equipment_management}"
DB_USER="${DB_USER:-root}"
DB_PASSWORD="${DB_PASSWORD:-jsjj8888}"
UPLOADS_PATH="${UPLOADS_PATH:-/app/uploads}"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
BACKUP_DIR="${BACKUP_ROOT}/${TIMESTAMP}"
mkdir -p "${BACKUP_DIR}"
mkdir -p "${BACKUP_DIR}/db" "${BACKUP_DIR}/uploads" "${BACKUP_DIR}/config"
cleanup_on_error() {
echo "备份失败,清理不完整目录: ${BACKUP_DIR}"
rm -rf "${BACKUP_DIR}"
}
prune_old_backups() {
if ! [[ "${KEEP_BACKUPS}" =~ ^[0-9]+$ ]]; then
echo "KEEP_BACKUPS 不是有效数字: ${KEEP_BACKUPS}"
exit 1
fi
mapfile -t existing_backups < <(find "${BACKUP_ROOT}" -mindepth 1 -maxdepth 1 -type d | sort)
local total="${#existing_backups[@]}"
if [ "${total}" -le "${KEEP_BACKUPS}" ]; then
echo ">>> 当前备份数量 ${total},无需清理"
return
fi
local delete_count=$((total - KEEP_BACKUPS))
echo ">>> 仅保留最近 ${KEEP_BACKUPS} 份备份,清理 ${delete_count} 份旧备份"
for backup_dir in "${existing_backups[@]:0:${delete_count}}"; do
echo " 删除: ${backup_dir}"
rm -rf "${backup_dir}"
done
}
trap cleanup_on_error ERR
echo "备份目录: ${BACKUP_DIR}"
echo "保留策略: 最近 ${KEEP_BACKUPS} 份"
if ! docker inspect "${DB_CONTAINER}" >/dev/null 2>&1; then
echo "数据库容器不存在: ${DB_CONTAINER}"
exit 1
fi
if ! docker inspect "${BACKEND_CONTAINER}" >/dev/null 2>&1; then
echo "后端容器不存在: ${BACKEND_CONTAINER}"
exit 1
fi
echo ">>> 备份数据库 ${DB_NAME}"
docker exec "${DB_CONTAINER}" \
sh -lc "exec mysqldump -u${DB_USER} -p'${DB_PASSWORD}' --single-transaction --routines --triggers ${DB_NAME}" \
| gzip > "${BACKUP_DIR}/db/${DB_NAME}.sql.gz"
echo ">>> 备份上传目录 ${UPLOADS_PATH}"
docker exec "${BACKEND_CONTAINER}" \
sh -lc "if [ -d '${UPLOADS_PATH}' ]; then tar -C '${UPLOADS_PATH}' -czf - .; else printf '' | gzip; fi" \
> "${BACKUP_DIR}/uploads/uploads.tar.gz"
echo ">>> 备份部署配置"
cp "${PROJECT_DIR}/docker-compose.yml" "${BACKUP_DIR}/config/docker-compose.yml"
cp "${PROJECT_DIR}/deploy.sh" "${BACKUP_DIR}/config/deploy.sh"
cp "${PROJECT_DIR}/Dockerfile" "${BACKUP_DIR}/config/Dockerfile"
if [ -d "${PROJECT_DIR}/frontend/public/branding" ]; then
cp -R "${PROJECT_DIR}/frontend/public/branding" "${BACKUP_DIR}/config/branding"
fi
cat > "${BACKUP_DIR}/backup-info.txt" <<EOF
timestamp=${TIMESTAMP}
project_name=${PROJECT_NAME}
project_dir=${PROJECT_DIR}
db_container=${DB_CONTAINER}
backend_container=${BACKEND_CONTAINER}
db_name=${DB_NAME}
uploads_path=${UPLOADS_PATH}
hostname=$(hostname)
EOF
echo ">>> 生成校验信息"
(
cd "${BACKUP_DIR}"
if [ -d config/branding ]; then
find config/branding -type f -print0 | xargs -0 sha256sum > branding-sha256.tmp
else
: > branding-sha256.tmp
fi
cat <(sha256sum db/${DB_NAME}.sql.gz uploads/uploads.tar.gz config/docker-compose.yml config/deploy.sh config/Dockerfile) branding-sha256.tmp > SHA256SUMS
rm -f branding-sha256.tmp
)
prune_old_backups
trap - ERR
echo "备份完成: ${BACKUP_DIR}"