提交 e0b0e7a5 authored 作者: 陈泽健's avatar 陈泽健

feat(server-check): 添加Redis容器修复和Emqx容器检测功能

- 实现Redis容器异常时的日志导出、远端修复和复检流程
- 添加Emqx容器异常检测和日志导出功能
- 优化issue_handler.sh中Redis容器修复脚本的交互模式和参数处理
- 更新PRD文档中的Redis修复交互模式说明和Emqx容器检测需求
- 完善服务自检需求文档中的检测功能详细描述和执行要求
上级 04c07cb7
......@@ -667,4 +667,13 @@ cd /data/services/api/java-meeting/java-meeting-extapi/
旧平台:
路径:/var/www/redis/data,将此目录下数据删除后重启uredis容器
新统一平台:
路径:/data/middleware/redis/data,将此目录下数据删除后重启uredis容器
\ No newline at end of file
路径:/data/middleware/redis/data,将此目录下数据删除后重启uredis容器
交互模式与非交互模式说明:
- 默认执行(不带额外参数)时,脚本以交互模式运行:
1)在删除 data 目录之前,提示用户“是否确认删除 redis data 下所有数据”,必须输入 yes 才会继续执行;
2)适合现场运维人员手动执行,避免误删数据。
- 当通过服务自检脚本自动调用时,使用非交互模式:
1)调用示例:./issue_handler.sh --action redis_container_exception --non-interactive --yes
2)非交互模式下,如果未同时指定 --yes,脚本应出于安全考虑直接退出,不删除任何数据;
3)只有在同时传入 --non-interactive 和 --yes 时,才会自动执行删除 data 并重启 uredis 的操作。
\ No newline at end of file
......@@ -1987,115 +1987,94 @@ show_disk_partition_info() {
# 补充redis服务异常时修复操作
redis_container_exception() {
log_info "开始修复 redis 容器启动异常问题"
local non_interactive=0
local auto_yes=0
# 解析参数
while [[ $# -gt 0 ]]; do
case "$1" in
--non-interactive)
non_interactive=1
shift
;;
--yes|-y)
auto_yes=1
shift
;;
*)
break
;;
esac
done
# 1. 检测平台类型(new / standard / unknown)
local platform
platform=$(detect_platform)
log_info "检测到平台类型: $platform"
echo "=== 修复 Redis 容器文件损坏问题 (redis_container_exception) ==="
# 2. 检查 docker 与 uredis 容器
if ! command -v docker >/dev/null 2>&1; then
log_error "系统中未安装 docker,无法处理 redis 容器异常"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (未安装 docker)"
return 1
fi
if ! docker ps -a --format '{{.Names}}' | grep -q '^uredis$'; then
log_error "未找到 uredis 容器,请确认 redis 容器已创建"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (未找到 uredis 容器)"
# 1. 检测新旧平台
local redis_data_path="/var/www/redis/data"
if [[ -d "/data/middleware/redis/data" ]]; then
redis_data_path="/data/middleware/redis/data"
echo "检测到新统一平台,Redis 数据目录:$redis_data_path"
elif [[ -d "/var/www/redis/data" ]]; then
echo "检测到标准版平台,Redis 数据目录:$redis_data_path"
else
echo "未找到 Redis 数据目录,请手动确认 /var/www/redis 或 /data/middleware/redis 是否存在"
return 1
fi
# 3. 先尝试正常重启 uredis 容器
log_info "尝试重启 uredis 容器..."
if docker restart uredis >/dev/null 2>&1; then
sleep 5
if docker ps --format '{{.Names}}' | grep -q '^uredis$'; then
log_info "uredis 容器重启成功,无需清理数据"
show_operation_summary "修复 redis 容器异常" "${GREEN}完成${NC} (容器重启成功)"
# 2. 先尝试简单重启 uredis 容器
echo "尝试重启 uredis 容器..."
if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
docker restart uredis
sleep 3
if docker ps --format '{{.Names}}' | grep -qw uredis; then
echo "uredis 容器重启成功,无需清空数据目录。"
return 0
else
log_warn "uredis 容器重启后未处于运行状态,将按 PRD 执行数据清理流程"
echo "uredis 容器重启失败,将按步骤 3 清空数据目录后再尝试重启。"
fi
else
log_warn "直接重启 uredis 容器失败,将按 PRD 执行数据清理流程"
echo "未找到 uredis 容器,后续清数据并尝试重新创建/启动时可能失败,请手动排查。"
fi
# 4. 确定 redis data 目录(按平台分支)
local redis_data_dir=""
if [ "$platform" = "new" ]; then
redis_data_dir="/data/middleware/redis/data"
elif [ "$platform" = "standard" ]; then
redis_data_dir="/var/www/redis/data"
# 3. 删除 redis data 下所有数据(高风险操作,需要确认)
if [[ $non_interactive -eq 0 ]]; then
# 交互模式:要求用户手动确认
echo "警告:将要删除 Redis 数据目录下的所有数据:$redis_data_path"
read -r -p "确认执行此操作吗?(yes/NO): " answer
if [[ "$answer" != "yes" ]]; then
echo "用户取消操作,不执行数据删除。"
return 1
fi
else
# 未识别平台时,根据目录存在性猜测
if [ -d "/data/middleware/redis/data" ]; then
redis_data_dir="/data/middleware/redis/data"
log_warn "平台类型 unknown,使用新平台 redis data 目录: $redis_data_dir"
elif [ -d "/var/www/redis/data" ]; then
redis_data_dir="/var/www/redis/data"
log_warn "平台类型 unknown,使用旧平台 redis data 目录: $redis_data_dir"
else
log_error "无法确定 redis data 目录,请确认平台及目录结构"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (未找到 redis data 目录)"
# 非交互模式:必须带 --yes 才继续
if [[ $auto_yes -ne 1 ]]; then
echo "非交互模式下未提供 --yes,出于安全考虑,取消操作。"
return 1
fi
echo "非交互模式 + --yes:将删除 Redis 数据目录:$redis_data_path"
fi
log_info "将使用 redis data 目录: $redis_data_dir"
if [ ! -d "$redis_data_dir" ]; then
log_error "redis data 目录不存在: $redis_data_dir"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (redis data 目录不存在)"
echo "清空 Redis 数据目录:$redis_data_path"
rm -rf "${redis_data_path:?}/"* || {
echo "删除 $redis_data_path 下数据失败,请检查权限。"
return 1
fi
# 5. 确认是否清空 redis data 目录(支持非交互)
local do_clear_data=false
log_warn "准备删除 redis data 目录下所有数据,这将清空当前 redis 数据。"
log_info "目录: $redis_data_dir"
}
if [ "$NON_INTERACTIVE" = "1" ] && [ "$ASSUME_YES" = "1" ]; then
log_info "非交互模式,自动确认清空 redis data 目录"
do_clear_data=true
# 再次尝试启动/重启 uredis 容器
if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
echo "尝试重启 uredis 容器..."
docker restart uredis
else
if confirm_action "是否确认删除该目录下所有数据并重新启动 uredis 容器?"; then
do_clear_data=true
else
log_info "用户取消删除 redis data 数据"
fi
echo "未找到 uredis 容器,尝试 docker start uredis..."
docker start uredis || true
fi
if [ "$do_clear_data" != true ]; then
show_operation_summary "修复 redis 容器异常" "${YELLOW}取消${NC} (用户取消删除数据)"
sleep 3
if docker ps --format '{{.Names}}' | grep -qw uredis; then
echo "uredis 容器启动成功,Redis 文件损坏修复完成。"
return 0
fi
# 6. 停止容器并清空数据目录
log_info "停止 uredis 容器..."
docker stop uredis >/dev/null 2>&1 || log_warn "停止 uredis 容器失败,可能已停止"
log_info "清空 redis data 目录内容: $redis_data_dir"
# 防止变量为空导致 rm -rf / 等危险:加 :? 保护
rm -rf "${redis_data_dir:?}/"* "${redis_data_dir:?}"/.[!.]* "${redis_data_dir:?}"/..?* 2>/dev/null || true
# 7. 重启 uredis 容器
log_info "重新启动 uredis 容器..."
if docker start uredis >/dev/null 2>&1; then
sleep 5
if docker ps --format '{{.Names}}' | grep -q '^uredis$'; then
log_info "uredis 容器已在清空数据后成功启动"
show_operation_summary "修复 redis 容器异常" "${GREEN}完成${NC} (已清空 data 并重启 uredis)"
return 0
else
log_error "清空数据后 uredis 容器仍未处于运行状态,请检查容器日志"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (清空 data 后容器仍未运行)"
return 1
fi
else
log_error "清空数据后启动 uredis 容器失败,请检查 docker / 容器配置"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (docker start uredis 失败)"
echo "uredis 容器仍未启动成功,请人工进一步排查。"
return 1
fi
}
......
......@@ -19,13 +19,55 @@
#### 检测需求
##### 1、SSH连接(✅ 已实现):
支持预设服务器列表和手动输入(IP/端口/用户名/密码)
功能描述:
负责与目标服务器建立 SSH 连接,支持多种连接方式,作为所有后续检测和修复操作的基础。
具体要求:
1)支持两种接入方式:
- 预设服务器列表:从本地配置中读取服务器信息(IP / 端口 / 用户名 / 密码),供用户选择;
- 手动输入模式:由用户在脚本运行时手动输入服务器 IP、端口、用户名、密码。
2)连接前进行基本参数校验(IP 格式、端口范围等),避免明显的输入错误。
3)连接失败时,需要在日志中记录详细原因(认证失败 / 超时 / 网络不可达等),并给出友好提示。
##### 2、平台识别(✅ 已实现):
自动检测目标服务器平台类型(检测 /data/services 目录,如果没有则是传统平台)
功能描述:
在成功连接目标服务器后,自动识别其所属平台类型,用于后续按平台选择不同的检测路径和目录结构。
识别规则:
- 新统一平台:
- 特征:存在 `/data/services` 目录;
- 目录结构主要以 `/data/` 为根路径。
- 传统平台:
- 特征:不存在 `/data/services` 目录;
- 目录结构主要以 `/var/www/` 为根路径。
具体要求:
1)识别结果需要在日志中明确打印(例如:“平台类型:新统一平台”或“平台类型:传统平台”)。
2)将平台类型保存到全局变量或上下文中,供后续检测函数统一使用(如文件路径、容器路径等)。
3)若无法明确识别(目录缺失或权限不足),需给出清晰的错误提示,并中止依赖平台判断的后续检测。
##### 3、系统识别(✅ 已实现):
自动检测目标服务器的系统类型(检测容器分为三种:ujava、upython、upython_voice,如果有ujava则有会议预定系统、python对应运维集控系统、upython_voice对应转录系统)
功能描述:
在完成平台识别后,对目标服务器上的业务系统类型进行识别,
主要通过容器信息判断当前服务器是否部署了会议预定系统、运维集控系统、转录系统等。
识别规则(基于容器):
- ujava 容器存在:
- 说明:部署了会议预定系统(Java 会议相关服务);
- upython 容器存在:
- 说明:部署了运维集控系统(Python CMDB / 运维相关服务);
- upython_voice 容器存在:
- 说明:部署了转录系统(语音转写 / 语音相关服务)。
具体要求:
1)通过 `docker ps` / `docker ps -a` 等命令获取容器列表,根据容器名称包含 `ujava`、`upython`、`upython_voice` 进行判断。
2)在日志中打印当前服务器安装的系统类型组合,例如:
- “检测到系统类型:会议预定系统 + 运维集控系统”;
- “检测到系统类型:仅部署会议预定系统”。
3)识别结果用于后续有条件的检测步骤:
- 若无 ujava 容器,则跳过会议预定系统相关检测;
- 若无 upython 容器,则跳过运维集控系统相关检测;
- 若无 upython_voice 容器,则跳过转录系统相关检测。
##### 4、服务进程检测(✅ 已实现):
根据平台类型不同需要分别在不同的位置进行检测,具体如下:
......@@ -90,21 +132,47 @@
tcp6 0 0 :::11211 :::* LISTEN 105/memcached
##### 5、DNS解析问题(✅ 已实现):
检测目标服务器的DNS配置,能否正常进行解析等相关操作,解析异常则进行远程修复操作,参考NTP远程修复,不需要判断传统或是新统一平台。
功能描述:
检测目标服务器当前的 DNS 配置是否正常,能否进行域名解析。
当解析异常时,参考 NTP 远程修复的方式,自动执行修复脚本(无需区分新统一平台或传统平台)。
具体要求:
1)通过常用命令(如 nslookup / dig / ping 域名等)验证 DNS 是否可用;
2)当检测到解析失败或严重异常时:
- 在日志中详细记录异常表现(如“无法解析域名”、“超时”、“无可用 DNS 服务器”等);
- 调用 Upload_the_repair_script,远程执行 issue_handler.sh 中的 DNS 修复函数;
3)修复完成后,重新进行一次 DNS 解析测试,并将复检结果记录到日志和自检报告中:
- 成功:标记为“DNS 解析修复成功”;
- 失败:标记为“DNS 解析仍异常,需要人工排查”。
##### 6、服务器资源分析(✅ 已实现):
检查目标服务器的磁盘空间情况、内存使用情况、cpu使用情况、防火墙开放端口情况、服务器架构以及操作系统记录
防火墙修复函数名称:Upload_the_repair_script
功能描述:函数只做上传脚本及脚本调用,上传当前目录下的issue_handler.sh脚本,将脚本上传到目标服务器,并且通过传入的修复函数,调用issue_handler对应的函数来修复。
功能描述:
综合检测目标服务器的资源与基础环境状态,包括:
- 磁盘空间使用情况
- 内存使用情况
- CPU 使用情况
- 防火墙开放端口情况
- 服务器硬件架构与操作系统版本信息
防火墙修复:
1、目标服务器未开启防火墙时,判断为异常,并执行修复脚本;
2、目标服务器开启防火墙时,存在多种判断:
- 目标服务器是会议预定系统,那么需检测22、443、1883和8306是否开启,未开启就是异常,需要执行修复脚本;
- 目标服务器是运维集控系统,那么需检测22、8443、1883和8306是否开启,未开启就是异常,需要执行修复脚本;
3、远程修复完成后没有进行复检操作,并打印复检结果
1)修复函数名称:Upload_the_repair_script
- 该函数仅负责上传并调用 issue_handler.sh 中的具体防火墙修复函数(如 fix_port_access)。
2)检测与判定规则:
- 若目标服务器未开启防火墙服务:
- 判定为异常,记录到日志和报告中;
- 自动调用防火墙修复脚本尝试开启并配置必要端口。
- 若防火墙已开启,根据系统角色进行端口校验:
- 会议预定系统:
- 必须开放端口:22、443、1883、8306
- 运维集控系统:
- 必须开放端口:22、8443、1883、8306
- 任一必需端口未开放时,判定为异常,需要执行修复脚本。
3)修复执行与结果记录:
- 由 Upload_the_repair_script 远程执行 issue_handler.sh 中的防火墙修复函数,自动添加缺失端口规则;
- 修复完成后不强制要求再次自动复检,但需在日志中明确打印修复执行结果,
包括:尝试执行的命令、成功/失败状态及可能的错误原因,便于后续人工确认。
##### 7、服务日志导出(✅ 已实现):
将目标服务器上的服务日志采集,需判断传统平台还是新统一平台
......@@ -177,53 +245,85 @@
注意:如果遇到有172.17.0.1、127.0.0.1的IP地址以及和目标服务器IP地址一致的需判断为正确!检测日志需要打印出配置文件的路径,不要进行修改IP的操作!将单个配置文件内的IP地址检测收集结果后再标注打印,并增加配置项地址。日志需要更简洁明了!合法地址可以不打印出来。
##### 9、服务器NTP服务检测(✅ 已实现):
检测目标服务器上的ntp服务是否开启并能够正确进行同步时间操作,ntp服务应该分为:ntp和chronyd两种,并且需要检测目标服务器时间和北京时间是否一致。
注意:此检测步骤需要在日志导出之前执行。不要做ntp配置文件的修改操作,此脚本只做检测!
功能描述:
检测目标服务器上的时间同步服务是否正常,当前主要包括 ntp 和 chronyd 两种实现。
需要确认:
1)时间同步服务是否已安装并处于运行状态;
2)目标服务器时间与北京时间是否保持一致(误差在可接受范围内)。
具体要求:
1)优先检测 chronyd / ntpd 等服务状态,判断是否处于 active / running。
2)通过 date、timedatectl 或 ntpstat 等命令获取当前系统时间和同步状态。
3)将目标服务器时间与北京时间进行对比:
- 若时间差在约定范围内(例如 ±3 分钟),视为正常;
- 若时间差超出范围,视为异常,在日志和自检报告中进行标记。
4)检测过程中只做状态和时间差的检查,不对 ntp/chronyd 配置文件做任何修改操作。
注意:
- 此检测步骤需要在“服务日志导出”之前执行,避免因时间不准导致日志分析困难。
- 本脚本仅负责检测,不自动修改 NTP 相关配置,必要的修复由人工或其他修复脚本完成。
##### 10、文件权限检测(✅ 已实现):
检测目标服务器上的数据库用户权限、nginx用户权限、启动文件权限、redis配置文件权限以及rc.local文件权限;
功能描述:
检测目标服务器上与服务运行密切相关的关键文件权限,包括:
- 数据库用户权限
- nginx 运行用户及相关文件权限
- 各服务启动脚本(run.sh / start.sh)权限
- Redis / Emqx 等中间件配置文件权限
- 系统 rc.local 启动项权限
根据平台类型(传统平台 / 新统一平台)分别检查不同路径下的文件权限。
具体检测范围:
传统平台:
如果有ujava容器:
1、检测/var/www/java/api-java-meeting2.0目录下的run.sh启动脚本权限
2、检测/var/www/java/external-meeting-api目录下的run.sh启动脚本权限
3、检测/var/www/java目录下的start.sh启动脚本权限
如果有upython容器:
1、检测/var/www/html目录下的start.sh启动脚本权限
如果有cardtable容器:
1、检测/var/www/wifi-local目录下的config.ini、startDB.sh、wifi的文件权限
如果有paperless容器:
1、检测/var/www/paperless目录下的run.sh和start.sh文件的权限
共有
1、检测/var/www/redis目录下的redis-*.conf文件的权限
2、检测/var/www/emqx或是/var/www/emqx/config目录下的*.conf文件的权限
如果有 ujava 容器:
1)检测 /var/www/java/api-java-meeting2.0 目录下 run.sh 启动脚本权限
2)检测 /var/www/java/external-meeting-api 目录下 run.sh 启动脚本权限
3)检测 /var/www/java 目录下 start.sh 启动脚本权限
如果有 upython 容器:
1)检测 /var/www/html 目录下 start.sh 启动脚本权限
如果有 cardtable 容器:
1)检测 /var/www/wifi-local 目录下 config.ini、startDB.sh、wifi 的文件权限
如果有 paperless 容器:
1)检测 /var/www/paperless 目录下 run.sh 和 start.sh 文件权限
公共部分
1)检测 /var/www/redis 目录下 redis-*.conf 文件权限
2)检测 /var/www/emqx 或 /var/www/emqx/config 目录下 *.conf 文件权限
新统一平台:
如果有ujava容器:
1、检测/data/services/api/auth/auth-sso-auth目录下的run.sh文件权限
2、检测/data/services/api/auth/auth-sso-gatway目录下的run.sh文件权限
3、检测/data/services/api/auth/auth-sso-system目录下的run.sh文件权限
4、检测/data/services/api/java-meeting/java-meeting2.0的run.sh文件权限
5、检测/data/services/api/java-meeting/java-meeting3.0的run.sh文件权限
6、检测/data/services/api/java-meeting/java-meeting-extapi的run.sh文件权限
7、检测/data/services/api/java-meeting/java-message-scheduling的run.sh文件权限
8、检测/data/services/api/java-meeting/java-mqtt的run.sh文件权限
9、检测/data/services/api/java-meeting/java-quartz的run.sh文件权限
10、检测/data/services/api目录下的start.sh文件权限
11、检测/data/services/scripts目录下的*.sh脚本文件的权限
如果有upython容器:
1、检测/data/services/api/python-cmdb目录下的*.sh脚本文件的权限
如果有upython_voice容器:
1、检测/data/services/api/python-voice目录下的*.sh脚本文件的权限
如果有paperless容器:
1、检测/data/third_party/paperless目录下的run.sh和start.sh脚本文件的权限
如果有cardtable容器:
1、检测/data/third_party/wifi-local目录下的config.ini、startDB.sh、wifi的文件权限
共有:
1、检测/etc/rc.d/rc.local文件权限
2、数据库用户权限需要进入umysql容器内,数据库账号为root,密码为dNrprU&2S
注意:此检测函数需要在日志导出函数前执行,并且main主函数和日志记录函数都需要补充调用!将文件权限打印出来!
如果有 ujava 容器:
1)检测 /data/services/api/auth/auth-sso-auth 目录下 run.sh 文件权限
2)检测 /data/services/api/auth/auth-sso-gatway 目录下 run.sh 文件权限
3)检测 /data/services/api/auth/auth-sso-system 目录下 run.sh 文件权限
4)检测 /data/services/api/java-meeting/java-meeting2.0 目录下 run.sh 文件权限
5)检测 /data/services/api/java-meeting/java-meeting3.0 目录下 run.sh 文件权限
6)检测 /data/services/api/java-meeting/java-meeting-extapi 目录下 run.sh 文件权限
7)检测 /data/services/api/java-meeting/java-message-scheduling 目录下 run.sh 文件权限
8)检测 /data/services/api/java-meeting/java-mqtt 目录下 run.sh 文件权限
9)检测 /data/services/api/java-meeting/java-quartz 目录下 run.sh 文件权限
10)检测 /data/services/api 目录下 start.sh 文件权限
11)检测 /data/services/scripts 目录下所有 *.sh 脚本文件权限
如果有 upython 容器:
1)检测 /data/services/api/python-cmdb 目录下所有 *.sh 脚本文件权限
如果有 upython_voice 容器:
1)检测 /data/services/api/python-voice 目录下所有 *.sh 脚本文件权限
如果有 paperless 容器:
1)检测 /data/third_party/paperless 目录下 run.sh 和 start.sh 脚本文件权限
如果有 cardtable 容器:
1)检测 /data/third_party/wifi-local 目录下 config.ini、startDB.sh、wifi 的文件权限
公共部分(两种平台通用):
1)检测 /etc/rc.d/rc.local 文件权限
2)数据库用户权限:
- 进入 umysql 容器内检查;
- 数据库账号:root,密码:dNrprU&2S;
- 重点关注 ubains 和 devops 数据库对应用户及权限配置是否符合预期。
日志与执行顺序要求:
1)此检测函数需要在“服务日志导出”函数之前执行,确保日志中能完整记录权限情况。
2)main 主函数和日志记录函数中都需要补充对本检测函数的调用,并将各关键文件的权限信息打印到日志中。
3)仅做检测与记录,不在该步骤自动修改任何文件权限;具体权限调整由人工或其他修复脚本处理。
##### 11、现场数据备份(✅ 已实现):
函数名称:DataBakup
......@@ -268,6 +368,24 @@
修复脚本执行完成后,再次检查 uredis 容器状态:
- 如果 uredis 已处于运行状态,记录“Redis容器复检成功”到自检日志和md报告中;
- 如果 uredis 仍未运行,则记录“Redis容器复检失败,需要人工排查”到自检日志和md报告中。
emqx容器:
1、日志获取:
检测
新统一平台:
路径:/data/middleware/emqx/log/emqx.log.1
传统平台:
路径:/var/www/emqx/log/emqx.log.1
将上述log文件从目标服务器导出到本地自检结果目录中(例如logs/emqx/子目录),并在自检日志中记录导出结果。
2、异常判定:
如果 uemqx 容器未运行,并且没有其他名字中包含“emqx”的运行中容器,则判定为 Emqx 容器异常。
3、远端修复操作:
调用上传修复脚本函数 Upload_the_repair_script,将当前目录下的issue_handler.sh脚本上传到目标服务器,并执行其中的 emqx_container_exception 修复函数。
调用示例(逻辑示意):
./issue_handler.sh --action emqx_container_exception --non-interactive --yes
4、修复完成后的复检:
修复脚本执行完成后,再次检查 uemqx 容器状态:
- 如果 uemqx 已处于运行状态,记录“Emqx容器复检成功”到自检日志和md报告中;
- 如果 uemqx 仍未运行,则记录“Emqx容器复检失败,需要人工排查”到自检日志和md报告中。
##### 13、定时任务查询(待实现):
函数名称:Test-ScheduledTask
......@@ -284,9 +402,83 @@
##### 14、上传修复脚本(已实现NTP和防火墙修复):
函数名称:Upload_the_repair_script
功能描述:函数只做上传脚本及脚本调用,上传当前目录下的issue_handler.sh脚本,将脚本上传到目标服务器,并且通过传入的修复函数,调用issue_handler对应的函数来修复。
例如:当前判断目标服务器的NTP服务存在异常,那么调用issue_handler脚本中的fix_ntp_config函数,脚本执行:./issue_handler.sh --action fix_ntp_config
功能描述:
该函数仅负责:
1)将当前目录下的 issue_handler.sh 脚本上传到目标服务器指定目录;
2)在远端赋予执行权限并进行必要的换行符处理(如 dos2unix);
3)根据传入的修复动作名称(action),在远端调用 issue_handler.sh 中对应的修复函数。
使用方式:
- 由各检测函数在发现异常后调用 Upload_the_repair_script,并通过参数指定需要执行的修复动作。
- Upload_the_repair_script 内部会在远端执行类似如下命令:
./issue_handler.sh --action <修复函数名> [--platform <平台类型>] [--non-interactive --yes]
典型调用示例:
1)NTP 服务异常修复:
检测到目标服务器 NTP 服务配置或状态异常时:
调用 issue_handler.sh 中的 fix_ntp_config 函数:
./issue_handler.sh --action fix_ntp_config
2)防火墙端口异常修复:
检测到目标服务器防火墙未开启或必要端口未开放(如 22/443/1883/8443 等)时:
调用 issue_handler.sh 中的 fix_port_access 函数(通常以非交互方式执行):
./issue_handler.sh --action fix_port_access --non-interactive --yes
3)对外后端服务未启动修复:
检测到对外后端服务进程(ubains-meeting-api-1.0-SNAPSHOT.jar)未启动时:
调用 issue_handler.sh 中的 fix_external_service_disconnect 函数:
./issue_handler.sh --action fix_external_service_disconnect --non-interactive --yes
4)Redis 容器启动异常修复:
在容器信息收集中检测到 Redis 容器异常(uredis 未运行,且无其他 redis 容器)时:
调用 issue_handler.sh 中的 redis_container_exception 函数(以非交互方式执行,自动清理 data 并重启):
./issue_handler.sh --action redis_container_exception --non-interactive --yes
5)Emqx 容器启动异常修复(预留):
在容器信息收集中检测到 Emqx 容器异常(uemqx 未运行,且无其他 emqx 容器)时:
预期调用 issue_handler.sh 中的 emqx_container_exception 函数(同样支持非交互方式):
./issue_handler.sh --action emqx_container_exception --non-interactive --yes
说明:
- Upload_the_repair_script 不直接实现具体修复逻辑,只负责:
“上传脚本 → 远端准备 → 拼接命令行 → 触发执行”
- 具体修复行为(如修改配置、重启服务、清理数据目录等)均在 issue_handler.sh 中的各修复函数内部实现。
- 对于高风险操作(如 Redis data 目录清理),自检场景采用非交互模式(--non-interactive --yes),
手工运维场景则可直接在服务器上交互式执行 issue_handler.sh,由运维人员确认后再进行删除或修改操作。
##### 15、服务自检报告输出(✅ 已实现):
将服务自检的所有操作步骤与结果输出到日志文件中!自检报告需要补充成md格式!检测报告输出需要带有明显图标,标识错误或正常状态
\ No newline at end of file
功能描述:
将本次服务自检过程中所有检测项的执行步骤、检测结果、异常说明及修复情况统一输出为报告文件,
既包含完整日志记录,也生成便于阅读的 Markdown 自检报告。
具体要求:
1)完整日志输出:
- 将脚本执行过程中的关键信息(开始/结束时间、目标服务器信息、各检测项结果等)
持久化到日志文件(.log),便于后续排查。
- 日志内容应包含每个检测函数的开始/结束标记、检测结论和异常详情。
2)Markdown 自检报告输出:
- 生成一份结构化的自检报告(.md),按检测顺序分章节展示各检测项的结果,
包括:检测项名称、检测说明、检测结果(成功/失败/警告)、简要结论。
- 每条检测结果前需增加明显的图标标识状态:
- ✅ 表示检测通过 / 状态正常
- ⚠️ 表示存在风险 / 需关注
- ❌ 表示检测失败 / 状态异常
- 对于异常项,需要在报告中简要说明异常原因,必要时附上建议处理方向。
3)报告内容范围:
- 至少包含以下模块的检测结果:
- SSH 连接与平台识别
- 系统识别与服务进程检测
- DNS / NTP / 防火墙 / 资源使用情况
- 配置文件 IP 检测、文件权限检测
- 服务日志导出、现场数据备份
- 容器信息收集及容器异常(Redis / Emqx 等)处理结果
4)输出形式与存放位置:
- 日志文件和 Markdown 报告文件建议按服务器 IP + 时间戳命名,方便区分多次自检结果。
- 报告文件统一存放在脚本所在目录下的指定输出目录中(例如:logs/ 或 reports/ 子目录)。
5)可读性要求:
- 报告结构清晰,分级标题明确(如:一级为大模块,二级为具体检测项)。
- 对终端用户(非开发人员)也能直观理解当前服务器健康状况以及需要关注的问题。
\ No newline at end of file
......@@ -1645,34 +1645,27 @@ function Test-ContainerInformation {
}
# =====================================================================
# Redis 容器异常:日志导出 + 远端修复 + 复检 (需求文档第12点)
# Redis 容器异常:日志导出 + 远端修复 + 复检
# =====================================================================
# 1) Redis 日志路径根据平台判断
$platformType = $Global:PlatformType # 你前面平台识别时应已设置此全局变量:'new' 或 'old'
$redisLogPath = "/var/www/redis/data/redis.log" # 传统平台默认路径
$platformType = $Global:PlatformType
$redisLogPath = "/var/www/redis/data/redis.log"
if ($platformType -eq "new") {
# 新统一平台路径(根据文档):/data/middleware/redis/data/redis.log
$redisLogPath = "/data/middleware/redis/data/redis.log"
}
# 本地自检结果目录:形如 output\<IP>\logs\redis\
# 本地目录
$baseOutputDir = Join-Path -Path $PSScriptRoot -ChildPath "output"
$serverDir = Join-Path -Path $baseOutputDir -ChildPath $Server.IP
$redisOutDir = Join-Path -Path $serverDir -ChildPath "logs\redis"
if (-not (Test-Path $redisOutDir)) {
New-Item -Path $redisOutDir -ItemType Directory -Force | Out-Null
}
$localRedisLog = Join-Path -Path $redisOutDir -ChildPath "redis.log"
Write-Log -Level "INFO" -Message "[Redis] 准备导出 redis 日志: $redisLogPath -> $localRedisLog"
# 2) 从远端导出 redis.log 到本地
# 这里假定你已有一个封装的远程下载函数,例如 Download-RemoteFile
# 如果你实际用的是 Get-SFTPItem 或者其他名字,请替换为你的函数名和参数。
$exportOk = $false
try {
$downloadRes = Download-RemoteFile -Server $Server -RemotePath $redisLogPath -LocalPath $localRedisLog -ErrorAction Stop
......@@ -1694,90 +1687,151 @@ function Test-ContainerInformation {
}
# 3) Redis 容器异常判定
# 条件:uredis 容器未运行,且没有其他名字中包含 'redis' 的运行中容器
$redisRunning = $runningContainers | Where-Object { $_.Name -match '(?i)redis' }
$uredisRunning = $redisRunning | Where-Object { $_.Name -eq 'uredis' }
$uredisStopped = $stoppedContainers | Where-Object { $_.Name -eq 'uredis' }
$redisNeedRepair = $false
if (-not $uredisRunning -and $uredisStopped) {
if (-not $redisRunning) {
# uredis 存在但未运行,且没有其他 redis 命名的运行中容器
$redisNeedRepair = $true
}
}
if (-not $redisNeedRepair) {
# 只有 Redis 需要修复时,才准备 repairItem
$repairItem = $null
if ($redisNeedRepair) {
Write-Log -Level "ERROR" -Message "[Redis] 检测到 Redis 容器异常:uredis 未运行,且无其他 redis 命名容器运行,开始执行远端修复"
$repairItem = [ordered]@{
Check = "Redis容器修复"
Status = "未执行"
Details = ""
Success = $false
}
try {
$serverForRepair = @{
IP = $Server.IP
User = $Server.User
Pass = $Server.Pass
Port = $Server.Port
}
$repairRes = Upload_the_repair_script -Server $serverForRepair -Action "redis_container_exception" -Platform "auto" -RemoteDir "/home/repair_scripts"
if ($repairRes -and $repairRes['Success']) {
Write-Log -Level "SUCCESS" -Message "[Redis] 远端 Redis 容器修复脚本执行成功 (redis_container_exception)"
$repairItem.Status = "已执行"
$repairItem.Details = "远端脚本执行成功 (redis_container_exception)"
$repairItem.Success = $true
# 复检
Write-Log -Level "INFO" -Message "[Redis] 修复后进行 uredis 容器复检..."
$checkCmd = "docker ps --format '{{.Names}}' | grep -w 'uredis' || echo 'NO_UREDIS'"
$recheck = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
if ($recheck.ExitCode -eq 0 -and -not ($recheck.Output -contains 'NO_UREDIS')) {
Write-Log -Level "SUCCESS" -Message "[Redis] Redis容器复检成功:uredis 已处于运行状态"
$repairItem.Details += " | 复检成功:uredis 已运行"
} else {
Write-Log -Level "WARN" -Message "[Redis] Redis容器复检失败:仍未检测到运行中的 uredis 容器,需要人工排查"
$repairItem.Status = "部分成功"
$repairItem.Details += " | 复检失败:uredis 仍未运行,请人工排查"
}
} else {
$errMsg = "未知错误"
if ($repairRes -is [hashtable]) {
if ($repairRes.ContainsKey('Error') -and $repairRes['Error']) { $errMsg = [string]::Join(' ', $repairRes['Error']) }
elseif ($repairRes.ContainsKey('Output') -and $repairRes['Output']) { $errMsg = [string]::Join(' ', $repairRes['Output']) }
elseif ($repairRes.ContainsKey('Message') -and $repairRes['Message']) { $errMsg = $repairRes['Message'] }
} elseif ($repairRes) {
$errMsg = $repairRes.ToString()
}
Write-Log -Level "ERROR" -Message "[Redis] 远端 Redis 容器修复执行失败:$errMsg"
$repairItem.Status = "失败"
$repairItem.Details = "远程修复失败:$errMsg"
}
}
catch {
Write-Log -Level "ERROR" -Message "[Redis] 调用 Upload_the_repair_script 异常:$($_.Exception.Message)"
$repairItem.Status = "异常"
$repairItem.Details = "调用修复脚本异常:$($_.Exception.Message)"
}
# 把 Redis 修复结果写入 $results
$results += $repairItem
}
else {
Write-Log -Level "INFO" -Message "[Redis] 未检测到需要自动修复的 Redis 容器异常"
return $results
}
Write-Log -Level "ERROR" -Message "[Redis] 检测到 Redis 容器异常:uredis 未运行,且无其他 redis 命名容器运行,开始执行远端修复"
# =====================================================================
# Emqx 容器异常:日志导出 + 异常判定(远端修复暂不实现)
# =====================================================================
# 1) emqx 日志路径根据平台判断
$emqxLogPath = "/var/www/emqx/log/emqx.log.1"
if ($platformType -eq "new") {
$emqxLogPath = "/data/middleware/emqx/log/emqx.log.1"
}
# 4) 远端修复:上传 issue_handler.sh 并执行 redis_container_exception (非交互模式)
$repairItem = [ordered]@{
Check = "Redis容器修复"
Status = "未执行"
Details = ""
Success = $false
$emqxOutDir = Join-Path -Path $serverDir -ChildPath "logs\emqx"
if (-not (Test-Path $emqxOutDir)) {
New-Item -Path $emqxOutDir -ItemType Directory -Force | Out-Null
}
$localEmqxLog = Join-Path -Path $emqxOutDir -ChildPath "emqx.log.1"
Write-Log -Level "INFO" -Message "[Emqx] 准备导出 emqx 日志: $emqxLogPath -> $localEmqxLog"
$emqxExportOk = $false
try {
$serverForRepair = @{
IP = $Server.IP
User = $Server.User
Pass = $Server.Pass
Port = $Server.Port
$emqxDl = Download-RemoteFile -Server $Server -RemotePath $emqxLogPath -LocalPath $localEmqxLog -ErrorAction Stop
if ($emqxDl -and $emqxDl.Success) {
Write-Log -Level "SUCCESS" -Message "[Emqx] emqx.log.1 导出成功,已保存到本地: $localEmqxLog"
$emqxExportOk = $true
} else {
Write-Log -Level "WARN" -Message "[Emqx] emqx.log.1 导出失败(返回结果不成功),请检查远端文件是否存在:$emqxLogPath"
}
} catch {
Write-Log -Level "WARN" -Message "[Emqx] emqx.log.1 导出异常:$($_.Exception.Message)"
}
# Upload_the_repair_script 内部应拼出:
# ./issue_handler.sh --action redis_container_exception --non-interactive --yes
$repairRes = Upload_the_repair_script -Server $serverForRepair -Action "redis_container_exception" -Platform "auto" -RemoteDir "/home/repair_scripts"
$results += @{
Check = "Emqx日志导出"
Status = ($emqxExportOk ? "完成" : "失败")
Details = $emqxExportOk ? "emqx.log.1 导出成功 -> $localEmqxLog" : "emqx.log.1 导出失败或文件不存在 ($emqxLogPath)"
Success = $emqxExportOk
}
if ($repairRes -and $repairRes['Success']) {
Write-Log -Level "SUCCESS" -Message "[Redis] 远端 Redis 容器修复脚本执行成功 (redis_container_exception)"
$repairItem.Status = "已执行"
$repairItem.Details = "远端脚本执行成功 (redis_container_exception)"
$repairItem.Success = $true
# 5) 修复完成后的复检
Write-Log -Level "INFO" -Message "[Redis] 修复后进行 uredis 容器复检..."
$checkCmd = "docker ps --format '{{.Names}}' | grep -w 'uredis' || echo 'NO_UREDIS'"
$recheck = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
if ($recheck.ExitCode -eq 0 -and -not ($recheck.Output -contains 'NO_UREDIS')) {
Write-Log -Level "SUCCESS" -Message "[Redis] Redis容器复检成功:uredis 已处于运行状态"
$repairItem.Details += " | 复检成功:uredis 已运行"
} else {
Write-Log -Level "WARN" -Message "[Redis] Redis容器复检失败:仍未检测到运行中的 uredis 容器,需要人工排查"
$repairItem.Status = "部分成功"
$repairItem.Details += " | 复检失败:uredis 仍未运行,请人工排查"
}
} else {
$errMsg = "未知错误"
if ($repairRes -is [hashtable]) {
if ($repairRes.ContainsKey('Error') -and $repairRes['Error']) { $errMsg = [string]::Join(' ', $repairRes['Error']) }
elseif ($repairRes.ContainsKey('Output') -and $repairRes['Output']) { $errMsg = [string]::Join(' ', $repairRes['Output']) }
elseif ($repairRes.ContainsKey('Message') -and $repairRes['Message']) { $errMsg = $repairRes['Message'] }
} elseif ($repairRes) {
$errMsg = $repairRes.ToString()
}
# 3) Emqx 容器异常判定
$emqxRunning = $runningContainers | Where-Object { $_.Name -match '(?i)emqx' }
$uemqxRunning = $emqxRunning | Where-Object { $_.Name -eq 'uemqx' }
$uemqxStopped = $stoppedContainers | Where-Object { $_.Name -eq 'uemqx' }
Write-Log -Level "ERROR" -Message "[Redis] 远端 Redis 容器修复执行失败:$errMsg"
$repairItem.Status = "失败"
$repairItem.Details = "远程修复失败:$errMsg"
$emqxNeedRepair = $false
if (-not $uemqxRunning -and $uemqxStopped) {
if (-not $emqxRunning) {
$emqxNeedRepair = $true
}
}
catch {
Write-Log -Level "ERROR" -Message "[Redis] 调用 Upload_the_repair_script 异常:$($_.Exception.Message)"
$repairItem.Status = "异常"
$repairItem.Details = "调用修复脚本异常:$($_.Exception.Message)"
}
$results += $repairItem
if (-not $emqxNeedRepair) {
Write-Log -Level "INFO" -Message "[Emqx] 未检测到 Emqx 容器异常(或存在其他 emqx 容器运行中)"
}
else {
Write-Log -Level "ERROR" -Message "[Emqx] 检测到 Emqx 容器异常:uemqx 未运行,且无其他 emqx 命名容器运行"
$results += @{
Check = "Emqx容器状态"
Status = "异常"
Details = "检测到 Emqx 容器异常:uemqx 未运行,且无其他 emqx 容器;暂未自动修复,请人工排查"
Success = $false
}
}
return $results
}
# ================================
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论