GuDingZiChan/scripts/backup-equipment.sh

138 lines
4.3 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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:-${MYSQL_ROOT_PASSWORD:-}}"
UPLOADS_PATH="${UPLOADS_PATH:-/app/uploads}"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
BACKUP_DIR="${BACKUP_ROOT}/${TIMESTAMP}"
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 -exec basename {} \; \
| awk '/^[0-9]{8}_[0-9]{6}$/' \
| sort \
| while IFS= read -r dir_name; do
printf '%s/%s\n' "${BACKUP_ROOT}" "${dir_name}"
done
)
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
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
if [ -z "${DB_PASSWORD}" ]; then
echo ">>> 未从 DB_PASSWORD / MYSQL_ROOT_PASSWORD 读取到数据库密码,尝试读取 ${DB_CONTAINER} 容器环境"
if ! container_env="$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "${DB_CONTAINER}")"; then
echo "无法读取数据库容器环境: ${DB_CONTAINER}" >&2
exit 1
fi
while IFS= read -r env_line; do
case "${env_line}" in
MYSQL_ROOT_PASSWORD=*)
DB_PASSWORD="${env_line#MYSQL_ROOT_PASSWORD=}"
break
;;
esac
done <<< "${container_env}"
unset container_env env_line
fi
if [ -z "${DB_PASSWORD}" ]; then
echo "数据库密码缺失:请设置 DB_PASSWORD 或 MYSQL_ROOT_PASSWORD;容器 ${DB_CONTAINER} 中也未找到 MYSQL_ROOT_PASSWORD。" >&2
exit 1
fi
mkdir -p "${BACKUP_DIR}/db" "${BACKUP_DIR}/uploads" "${BACKUP_DIR}/config"
echo "备份目录: ${BACKUP_DIR}"
echo "保留策略: 最近 ${KEEP_BACKUPS} 份"
echo ">>> 备份数据库 ${DB_NAME}"
docker exec -e "MYSQL_PWD=${DB_PASSWORD}" "${DB_CONTAINER}" \
mysqldump -u"${DB_USER}" --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}"