NAC_Blockchain/scripts/mongo_backup.sh

280 lines
9.5 KiB
Bash
Executable File
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
# ============================================================
# NAC Knowledge Engine - MongoDB每日备份脚本 v2
# 新增备份失败时自动发送Webhook告警企业微信/钉钉/飞书/通用)
# 数据库nac_knowledge_engine多语言合规知识库
# 备份目录:/opt/nac/backups/mongodb/
# 保留策略保留最近30天的备份自动清理旧备份
# 日志:/var/log/nac_mongo_backup.log
# ============================================================
set -euo pipefail
# ─── 配置 ────────────────────────────────────────────────────
BACKUP_BASE="/opt/nac/backups/mongodb"
LOG_FILE="/var/log/nac_mongo_backup.log"
RETENTION_DAYS=30
DB_NAME="nac_knowledge_engine"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_BASE}/${DATE}"
ENV_FILE="/opt/nac/services/nac-admin/.env"
# ─── 日志函数 ─────────────────────────────────────────────────
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
log_error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >&2
}
# ─── Webhook告警函数 ──────────────────────────────────────────
# 从.env文件读取Webhook配置并发送告警
send_webhook_alert() {
local TITLE="$1"
local CONTENT="$2"
local LEVEL="${3:-error}" # error | warning | info
# 从.env读取Webhook URL
local WECOM_URL DINGTALK_URL FEISHU_URL GENERIC_URL
WECOM_URL=$(grep "^NAC_NOTIFY_WECOM_WEBHOOK=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '[:space:]')
DINGTALK_URL=$(grep "^NAC_NOTIFY_DINGTALK_WEBHOOK=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '[:space:]')
FEISHU_URL=$(grep "^NAC_NOTIFY_FEISHU_WEBHOOK=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '[:space:]')
GENERIC_URL=$(grep "^NAC_NOTIFY_WEBHOOK_URL=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '[:space:]')
local SENT=0
local TIMESTAMP
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# 企业微信
if [ -n "${WECOM_URL}" ]; then
local WECOM_PAYLOAD
WECOM_PAYLOAD=$(cat <<EOF
{
"msgtype": "markdown",
"markdown": {
"content": "## 🚨 ${TITLE}\n\n${CONTENT}\n\n> 时间:${TIMESTAMP}\n> 服务NAC MongoDB备份\n> 级别:${LEVEL}"
}
}
EOF
)
if curl -s -X POST "${WECOM_URL}" \
-H "Content-Type: application/json" \
-d "${WECOM_PAYLOAD}" \
--connect-timeout 10 \
--max-time 15 \
-o /dev/null 2>>"${LOG_FILE}"; then
log "[Webhook] 企业微信告警发送成功"
SENT=$((SENT + 1))
else
log_error "[Webhook] 企业微信告警发送失败"
fi
fi
# 钉钉
if [ -n "${DINGTALK_URL}" ]; then
local DINGTALK_PAYLOAD
DINGTALK_PAYLOAD=$(cat <<EOF
{
"msgtype": "markdown",
"markdown": {
"title": "${TITLE}",
"text": "## 🚨 ${TITLE}\n\n${CONTENT}\n\n> 时间:${TIMESTAMP}\n> 服务NAC MongoDB备份\n> 级别:${LEVEL}"
},
"at": { "isAtAll": true }
}
EOF
)
if curl -s -X POST "${DINGTALK_URL}" \
-H "Content-Type: application/json" \
-d "${DINGTALK_PAYLOAD}" \
--connect-timeout 10 \
--max-time 15 \
-o /dev/null 2>>"${LOG_FILE}"; then
log "[Webhook] 钉钉告警发送成功"
SENT=$((SENT + 1))
else
log_error "[Webhook] 钉钉告警发送失败"
fi
fi
# 飞书
if [ -n "${FEISHU_URL}" ]; then
local COLOR
case "${LEVEL}" in
error) COLOR="red" ;;
warning) COLOR="orange" ;;
*) COLOR="blue" ;;
esac
local FEISHU_PAYLOAD
FEISHU_PAYLOAD=$(cat <<EOF
{
"msg_type": "interactive",
"card": {
"header": {
"title": { "tag": "plain_text", "content": "🚨 ${TITLE}" },
"template": "${COLOR}"
},
"elements": [
{
"tag": "div",
"text": { "tag": "lark_md", "content": "${CONTENT}\n\n**时间:** ${TIMESTAMP}\n**服务:** NAC MongoDB备份\n**级别:** ${LEVEL}" }
}
]
}
}
EOF
)
if curl -s -X POST "${FEISHU_URL}" \
-H "Content-Type: application/json" \
-d "${FEISHU_PAYLOAD}" \
--connect-timeout 10 \
--max-time 15 \
-o /dev/null 2>>"${LOG_FILE}"; then
log "[Webhook] 飞书告警发送成功"
SENT=$((SENT + 1))
else
log_error "[Webhook] 飞书告警发送失败"
fi
fi
# 通用Webhook
if [ -n "${GENERIC_URL}" ]; then
local GENERIC_PAYLOAD
GENERIC_PAYLOAD=$(cat <<EOF
{
"title": "${TITLE}",
"content": "${CONTENT}",
"level": "${LEVEL}",
"service": "nac_mongo_backup",
"timestamp": "${TIMESTAMP}"
}
EOF
)
if curl -s -X POST "${GENERIC_URL}" \
-H "Content-Type: application/json" \
-d "${GENERIC_PAYLOAD}" \
--connect-timeout 10 \
--max-time 15 \
-o /dev/null 2>>"${LOG_FILE}"; then
log "[Webhook] 通用Webhook告警发送成功"
SENT=$((SENT + 1))
else
log_error "[Webhook] 通用Webhook告警发送失败"
fi
fi
if [ "${SENT}" -eq 0 ]; then
log "[Webhook] 未配置任何Webhook告警仅记录到日志"
fi
}
# ─── 错误处理:捕获脚本异常退出 ──────────────────────────────
BACKUP_FAILED=0
trap 'if [ $? -ne 0 ] && [ "${BACKUP_FAILED}" -eq 0 ]; then
BACKUP_FAILED=1
log_error "备份脚本异常退出(非预期错误)"
send_webhook_alert \
"NAC MongoDB备份异常退出" \
"备份脚本在执行过程中发生未预期的错误,请立即检查服务器状态。\n\n**日志文件:** ${LOG_FILE}\n**备份目录:** ${BACKUP_BASE}" \
"error"
fi' EXIT
# ─── 主备份流程 ───────────────────────────────────────────────
log "======================================================"
log "NAC MongoDB备份开始 v2 - 数据库: ${DB_NAME}"
log "备份目录: ${BACKUP_DIR}"
# 创建备份目录
mkdir -p "${BACKUP_DIR}"
# 读取MongoDB连接URL
MONGO_URL=""
MONGO_URL_FILE="/opt/nac/services/nac-admin/mongo_url.conf"
if [ -f "${MONGO_URL_FILE}" ]; then
MONGO_URL=$(cat "${MONGO_URL_FILE}" | tr -d '[:space:]')
else
MONGO_URL=$(grep "^NAC_MONGO_URL=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'")
fi
if [ -z "${MONGO_URL}" ]; then
log_error "无法读取MongoDB连接URL备份失败"
BACKUP_FAILED=1
send_webhook_alert \
"NAC MongoDB备份失败 - 无法读取连接URL" \
"备份脚本无法从配置文件中读取MongoDB连接URL。\n\n**检查文件:** ${ENV_FILE}\n**所需变量:** NAC_MONGO_URL" \
"error"
exit 1
fi
# 执行mongodump备份
log "开始执行 mongodump..."
MONGODUMP_LOG="${BACKUP_DIR}/mongodump.log"
if mongodump \
--uri="${MONGO_URL}" \
--db="${DB_NAME}" \
--out="${BACKUP_DIR}" \
--gzip \
--quiet 2>"${MONGODUMP_LOG}"; then
# 计算备份大小
BACKUP_SIZE=$(du -sh "${BACKUP_DIR}" | cut -f1)
log "备份成功 - 大小: ${BACKUP_SIZE}"
# 创建备份清单文件
cat > "${BACKUP_DIR}/BACKUP_MANIFEST.txt" << EOF
NAC Knowledge Engine MongoDB备份清单 v2
========================================
备份时间: $(date '+%Y-%m-%d %H:%M:%S')
数据库名: ${DB_NAME}
备份大小: ${BACKUP_SIZE}
备份工具: mongodump $(mongodump --version 2>&1 | head -1 | awk '{print $3}' || echo "unknown")
备份内容:
$(ls -la "${BACKUP_DIR}/${DB_NAME}/" 2>/dev/null || echo " (无集合文件)")
EOF
log "备份清单已创建: ${BACKUP_DIR}/BACKUP_MANIFEST.txt"
# 备份成功时发送成功通知(可选,避免频繁通知可注释此段)
# send_webhook_alert "NAC MongoDB备份成功" "数据库 ${DB_NAME} 备份完成,大小: ${BACKUP_SIZE}" "info"
else
MONGODUMP_ERROR=$(cat "${MONGODUMP_LOG}" 2>/dev/null | head -5 || echo "无错误详情")
log_error "mongodump执行失败"
log_error "错误详情: ${MONGODUMP_ERROR}"
rm -rf "${BACKUP_DIR}"
BACKUP_FAILED=1
# 发送告警
send_webhook_alert \
"NAC MongoDB备份失败 - mongodump执行失败" \
"mongodump命令执行失败数据库 **${DB_NAME}** 备份未完成。\n\n**错误信息:** ${MONGODUMP_ERROR}\n\n**日志文件:** ${LOG_FILE}\n\n请立即检查MongoDB服务状态和网络连接。" \
"error"
exit 1
fi
# ─── 清理旧备份保留最近30天────────────────────────────────
log "清理${RETENTION_DAYS}天前的旧备份..."
DELETED_COUNT=0
while IFS= read -r -d '' old_dir; do
log " 删除旧备份: $(basename "${old_dir}")"
rm -rf "${old_dir}"
DELETED_COUNT=$((DELETED_COUNT + 1))
done < <(find "${BACKUP_BASE}" -maxdepth 1 -type d -mtime +${RETENTION_DAYS} -print0 2>/dev/null)
if [ "${DELETED_COUNT}" -gt 0 ]; then
log "已清理 ${DELETED_COUNT} 个旧备份"
else
log "无需清理旧备份"
fi
# ─── 备份统计 ─────────────────────────────────────────────────
TOTAL_BACKUPS=$(find "${BACKUP_BASE}" -maxdepth 1 -type d | wc -l)
TOTAL_SIZE=$(du -sh "${BACKUP_BASE}" 2>/dev/null | cut -f1)
log "当前备份总数: $((TOTAL_BACKUPS - 1)) 个,总占用: ${TOTAL_SIZE}"
log "NAC MongoDB备份完成 v2 ✓"
log "======================================================"
# 正常完成取消错误trap
BACKUP_FAILED=1 # 防止EXIT trap误触发
exit 0