提交 7203895b authored 作者: PGY's avatar PGY

feat(自动化部署脚本): 添加系统监控和维护脚本

添加了四个定时脚本用于系统维护:
- check_nacos_and_restart.sh: 检查 Nacos 服务状态,失败时重启
  ujava2 容器中的服务

- clear_deleted_files.sh: 定时清理被进程占用的已删除文件,
  通过发送 SIGHUP 信号释放文件句柄

- monitor_exapi_and_malan.sh: 监控 exapi 和 malan 服务进程,
  服务停止时自动启动

- mysql_backup.sh: MySQL 数据库备份脚本,支持多数据库备份
  和旧备份自动清理功能
上级 16b2332e
#!/bin/bash
# 宿主机上的脚本:检查 Nacos,失败则重启 ujava2 容器中的服务
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}
check_nacos() {
# 确保 127.0.0.1:8848 可访问(Nacos 端口已映射到宿主机)
if curl -sf http://127.0.0.1:8848/nacos/v1/console/health/readiness >/dev/null; then
return 0
else
return 1
fi
}
restart_in_container() {
log "Nacos NOT READY. Restarting services in container 'ujava2'..."
# 检查容器是否正在运行(精确匹配名称)
if ! docker ps --format '{{.Names}}' | grep -Fxq "ujava2"; then
log "ERROR: Container 'ujava2' is not running!"
return 1
fi
# 在容器内执行 start.sh
if docker exec ujava2 /var/www/java/start.sh; then
log "SUCCESS: Services restarted in ujava2."
else
log "ERROR: Failed to run start.sh in ujava2."
fi
}
# 主逻辑
if check_nacos; then
log "Nacos is healthy."
else
restart_in_container
fi
#!/bin/bash
# clear_deleted_files.sh - 定时清理被进程占用的已删除文件
LOG_FILE="/var/log/clear_deleted.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] 开始检查被占用的已删除文件..." >> "$LOG_FILE"
# 使用 lsof +L1 查找 link count 为 0(即已删除)但仍被打开的文件
DELETED_ENTRIES=$(lsof +L1 2>/dev/null | grep -v "COMMAND")
if [ -z "$DELETED_ENTRIES" ]; then
echo "[$DATE] 未发现被占用的已删除文件。" >> "$LOG_FILE"
exit 0
fi
echo "[$DATE] 发现以下被占用的已删除文件:" >> "$LOG_FILE"
echo "$DELETED_ENTRIES" >> "$LOG_FILE"
# 提取唯一 PID 列表
PIDS=$(echo "$DELETED_ENTRIES" | awk 'NR>1 {print $2}' | sort -u)
for PID in $PIDS; do
if ! kill -0 "$PID" 2>/dev/null; then
echo "[$DATE] PID $PID 已不存在,跳过。" >> "$LOG_FILE"
continue
fi
CMD=$(ps -p "$PID" -o comm= 2>/dev/null | tr -d ' ')
echo "[$DATE] 处理进程: PID=$PID, CMD=$CMD" >> "$LOG_FILE"
# 尝试发送 SIGHUP(适用于 Nginx、rsyslog 等支持 reload 的服务)
if kill -0 "$PID" 2>/dev/null; then
echo "[$DATE] 向 PID $PID 发送 SIGHUP 信号..." >> "$LOG_FILE"
kill -HUP "$PID" 2>/dev/null && continue
fi
# 若 SIGHUP 无效或不支持,记录警告(不强制 kill,避免服务中断)
echo "[$DATE] WARNING: 无法通过 SIGHUP 释放 PID $PID 的文件句柄。建议手动处理或配置 logrotate。" >> "$LOG_FILE"
done
echo "[$DATE] 检查完成。" >> "$LOG_FILE"
#!/bin/bash
# --- 配置区域 ---
# 日志文件路径
LOG_FILE="/var/log/monitor_exapi_and_malan.log"
# 定义要监控的服务及其相关信息
# 格式: "进程名:目录路径:启动脚本路径"
SERVICES=(
"ubains-meeting-api-1.0-SNAPSHOT.jar:/var/www/java/external-meeting-api:/var/www/java/external-meeting-api/run.sh"
"malan:/var/www/malan:/var/www/malan/run.sh"
)
# --- 函数定义 ---
# 记录日志的函数
log_message() {
local message="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> "$LOG_FILE"
}
# 检查进程是否运行
is_process_running() {
local process_name="$1"
# 使用 pgrep -f 搜索命令行中包含该名称的进程
# 对于 .jar 文件,通常搜索的是 java -jar ...<jar_name>
# 对于其他程序,搜索其进程名
if [[ "$process_name" == *.jar ]]; then
# 如果是 JAR 文件,搜索 java 进程中包含该 jar 名称的
pgrep -f "java.*$process_name" > /dev/null
else
# 否则直接搜索进程名
pgrep -x "$process_name" > /dev/null
fi
if [ $? -eq 0 ]; then
return 0 # 进程运行中
else
return 1 # 进程未运行
fi
}
# 检查目录是否存在
is_directory_exists() {
local dir_path="$1"
[ -d "$dir_path" ]
}
# 检查启动脚本是否存在且可执行
is_script_executable() {
local script_path="$1"
[ -x "$script_path" ]
}
# 启动服务
start_service() {
local service_name="$1"
local dir_path="$2"
local script_path="$3"
log_message "尝试启动服务 '$service_name'..."
# 切换到服务目录并执行启动脚本
# 使用子shell (cd ...) 以确保不影响当前脚本的工作目录
(
cd "$dir_path" || { log_message "错误: 无法切换到目录 '$dir_path'"; return 1; }
# 检查启动脚本是否存在且可执行
if ! is_script_executable "$script_path"; then
log_message "错误: 启动脚本 '$script_path' 不存在或不可执行。"
return 1
fi
# 执行启动脚本
# 使用 nohup 将进程与终端脱离,使其在后台持续运行
# 输出重定向到 startup.log 或 /dev/null
nohup "./$(basename "$script_path")" > startup.log 2>&1 &
# 等待一小段时间,让进程有机会启动
sleep 3
# 再次检查进程是否启动成功
if is_process_running "$service_name"; then
log_message "服务 '$service_name' 启动成功。"
return 0
else
log_message "错误: 服务 '$service_name' 启动失败。"
return 1
fi
)
}
# --- 主逻辑 ---
# 初始化日志
log_message "=== 服务监控脚本开始执行 ==="
# 遍历所有服务
for service_info in "${SERVICES[@]}"; do
# 解析服务信息
IFS=':' read -r service_name dir_path script_path <<< "$service_info"
# 去除可能的前后空格
service_name=$(echo "$service_name" | xargs)
dir_path=$(echo "$dir_path" | xargs)
script_path=$(echo "$script_path" | xargs)
log_message "检查服务: $service_name (目录: $dir_path, 启动脚本: $script_path)"
# 检查目录是否存在
if ! is_directory_exists "$dir_path"; then
log_message "跳过服务 '$service_name': 目录 '$dir_path' 不存在。"
continue # 跳过当前循环,检查下一个服务
fi
# 检查进程是否运行
if is_process_running "$service_name"; then
log_message "服务 '$service_name' 正在运行。"
else
log_message "服务 '$service_name' 未运行。"
# 尝试启动服务
start_service "$service_name" "$dir_path" "$script_path"
fi
done
log_message "=== 服务监控脚本执行完毕 ==="
#!/bin/bash
# ==================== 配置区 ====================
CONTAINER_NAME="umysql"
DB_USER="root"
PASSWORD_LIST=("dNrprU&2S" "Ubains@123") # 按顺序尝试
HOST_BACKUP_DIR="/opt/mysql" # 宿主机备份目录
LOG_FILE="/var/log/mysql_backup.log"
RETENTION_DAYS=30
TARGET_DBS=("devops" "devops_voice" "huazhao2" "nacos_mysql" "offline" "ubains" "wifi" "voice")
DATE=$(date +"%Y%m%d")
CONTAINER_TMP_DIR="/tmp/mysql_backup_$$.sql.gz" # 容器内临时目录(带PID防冲突)
# ==================== 日志函数 ====================
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# ==================== 初始化 ====================
mkdir -p "$HOST_BACKUP_DIR/$DATE"
touch "$LOG_FILE"
log "===== 开始备份任务 (容器: $CONTAINER_NAME) ====="
# 在容器内创建临时目录
docker exec "$CONTAINER_NAME" mkdir -p "$CONTAINER_TMP_DIR"
if [ $? -ne 0 ]; then
log "❌ 无法在容器内创建临时目录,请检查容器是否运行。"
exit 1
fi
# ==================== 尝试连接并获取存在的数据库 ====================
DB_PASSWORD=""
for pwd in "${PASSWORD_LIST[@]}"; do
if docker exec "$CONTAINER_NAME" mysql -u"$DB_USER" -p"$pwd" -Nse "SELECT 1;" >/dev/null 2>&1; then
DB_PASSWORD="$pwd"
log "✅ 使用密码成功连接数据库"
break
fi
done
if [ -z "$DB_PASSWORD" ]; then
log "❌ 所有密码尝试失败,无法连接数据库"
docker exec "$CONTAINER_NAME" rm -rf "$CONTAINER_TMP_DIR"
exit 1
fi
# 获取容器中实际存在的数据库列表
EXISTING_DBS_RAW=$(docker exec "$CONTAINER_NAME" mysql -u"$DB_USER" -p"$DB_PASSWORD" -Nse "SHOW DATABASES;" 2>/dev/null)
mapfile -t EXISTING_DBS <<< "$EXISTING_DBS_RAW"
declare -A DB_EXISTS
for db in "${EXISTING_DBS[@]}"; do
DB_EXISTS["$db"]=1
done
# ==================== 备份每个目标数据库 ====================
SUCCESS_COUNT=0
for db in "${TARGET_DBS[@]}"; do
if [[ -n "${DB_EXISTS[$db]}" ]]; then
CONTAINER_DUMP_FILE="$CONTAINER_TMP_DIR/${db}.sql.gz"
HOST_DUMP_FILE="$HOST_BACKUP_DIR/$DATE/${db}.sql.gz"
log "开始备份数据库: $db"
# 在容器内执行 mysqldump + gzip
docker exec "$CONTAINER_NAME" \
sh -c "mysqldump -u'$DB_USER' -p'$DB_PASSWORD' --single-transaction --routines --triggers --events '$db' | gzip > '$CONTAINER_DUMP_FILE'"
if [ $? -eq 0 ] && docker exec "$CONTAINER_NAME" [ -s "$CONTAINER_DUMP_FILE" ]; then
# 从容器复制到宿主机
docker cp "$CONTAINER_NAME:$CONTAINER_DUMP_FILE" "$HOST_DUMP_FILE"
if [ $? -eq 0 ] && [ -s "$HOST_DUMP_FILE" ]; then
log "✅ 备份成功: $db -> $HOST_DUMP_FILE"
((SUCCESS_COUNT++))
else
log "❌ 复制到宿主机失败: $db"
[ -f "$HOST_DUMP_FILE" ] && rm -f "$HOST_DUMP_FILE"
fi
else
log "❌ 容器内备份失败: $db"
fi
# 立即删除容器内的临时文件(无论成功与否)
docker exec "$CONTAINER_NAME" rm -f "$CONTAINER_DUMP_FILE"
else
log "⚠️ 跳过: 数据库 $db 不存在"
fi
done
# 清理容器内临时目录
docker exec "$CONTAINER_NAME" rm -rf "$CONTAINER_TMP_DIR"
log "本次共成功备份 $SUCCESS_COUNT 个数据库"
# ==================== 清理超过30天的旧备份 ====================
log "开始清理超过 $RETENTION_DAYS 天的旧备份..."
find "$HOST_BACKUP_DIR" -maxdepth 1 -type d -name "????????" -mtime +$RETENTION_DAYS -exec rm -rf {} + 2>/dev/null
log "===== 备份任务结束 ====="
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论