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

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

- 实现Redis容器异常时的日志导出、远端修复和复检流程
- 添加Emqx容器异常检测和日志导出功能
- 优化issue_handler.sh中Redis容器修复脚本的交互模式和参数处理
- 更新PRD文档中的Redis修复交互模式说明和Emqx容器检测需求
- 完善服务自检需求文档中的检测功能详细描述和执行要求
上级 04c07cb7
...@@ -668,3 +668,12 @@ cd /data/services/api/java-meeting/java-meeting-extapi/ ...@@ -668,3 +668,12 @@ cd /data/services/api/java-meeting/java-meeting-extapi/
路径:/var/www/redis/data,将此目录下数据删除后重启uredis容器 路径:/var/www/redis/data,将此目录下数据删除后重启uredis容器
新统一平台: 新统一平台:
路径:/data/middleware/redis/data,将此目录下数据删除后重启uredis容器 路径:/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() { ...@@ -1987,115 +1987,94 @@ show_disk_partition_info() {
# 补充redis服务异常时修复操作 # 补充redis服务异常时修复操作
redis_container_exception() { redis_container_exception() {
log_info "开始修复 redis 容器启动异常问题" local non_interactive=0
local auto_yes=0
# 1. 检测平台类型(new / standard / unknown) # 解析参数
local platform while [[ $# -gt 0 ]]; do
platform=$(detect_platform) case "$1" in
log_info "检测到平台类型: $platform" --non-interactive)
non_interactive=1
shift
;;
--yes|-y)
auto_yes=1
shift
;;
*)
break
;;
esac
done
# 2. 检查 docker 与 uredis 容器 echo "=== 修复 Redis 容器文件损坏问题 (redis_container_exception) ==="
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 # 1. 检测新旧平台
log_error "未找到 uredis 容器,请确认 redis 容器已创建" local redis_data_path="/var/www/redis/data"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (未找到 uredis 容器)" 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 return 1
fi fi
# 3. 先尝试正常重启 uredis 容器 # 2. 先尝试简单重启 uredis 容器
log_info "尝试重启 uredis 容器..." echo "尝试重启 uredis 容器..."
if docker restart uredis >/dev/null 2>&1; then if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
sleep 5 docker restart uredis
if docker ps --format '{{.Names}}' | grep -q '^uredis$'; then sleep 3
log_info "uredis 容器重启成功,无需清理数据" if docker ps --format '{{.Names}}' | grep -qw uredis; then
show_operation_summary "修复 redis 容器异常" "${GREEN}完成${NC} (容器重启成功)" echo "uredis 容器重启成功,无需清空数据目录。"
return 0 return 0
else else
log_warn "uredis 容器重启后未处于运行状态,将按 PRD 执行数据清理流程" echo "uredis 容器重启失败,将按步骤 3 清空数据目录后再尝试重启。"
fi fi
else else
log_warn "直接重启 uredis 容器失败,将按 PRD 执行数据清理流程" echo "未找到 uredis 容器,后续清数据并尝试重新创建/启动时可能失败,请手动排查。"
fi fi
# 4. 确定 redis data 目录(按平台分支) # 3. 删除 redis data 下所有数据(高风险操作,需要确认)
local redis_data_dir="" if [[ $non_interactive -eq 0 ]]; then
if [ "$platform" = "new" ]; then # 交互模式:要求用户手动确认
redis_data_dir="/data/middleware/redis/data" echo "警告:将要删除 Redis 数据目录下的所有数据:$redis_data_path"
elif [ "$platform" = "standard" ]; then read -r -p "确认执行此操作吗?(yes/NO): " answer
redis_data_dir="/var/www/redis/data" if [[ "$answer" != "yes" ]]; then
else echo "用户取消操作,不执行数据删除。"
# 未识别平台时,根据目录存在性猜测
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 目录)"
return 1 return 1
fi fi
fi else
# 非交互模式:必须带 --yes 才继续
log_info "将使用 redis data 目录: $redis_data_dir" if [[ $auto_yes -ne 1 ]]; then
echo "非交互模式下未提供 --yes,出于安全考虑,取消操作。"
if [ ! -d "$redis_data_dir" ]; then
log_error "redis data 目录不存在: $redis_data_dir"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (redis data 目录不存在)"
return 1 return 1
fi fi
echo "非交互模式 + --yes:将删除 Redis 数据目录:$redis_data_path"
fi
# 5. 确认是否清空 redis data 目录(支持非交互) echo "清空 Redis 数据目录:$redis_data_path"
local do_clear_data=false rm -rf "${redis_data_path:?}/"* || {
log_warn "准备删除 redis data 目录下所有数据,这将清空当前 redis 数据。" echo "删除 $redis_data_path 下数据失败,请检查权限。"
log_info "目录: $redis_data_dir" return 1
}
if [ "$NON_INTERACTIVE" = "1" ] && [ "$ASSUME_YES" = "1" ]; then # 再次尝试启动/重启 uredis 容器
log_info "非交互模式,自动确认清空 redis data 目录" if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
do_clear_data=true echo "尝试重启 uredis 容器..."
docker restart uredis
else else
if confirm_action "是否确认删除该目录下所有数据并重新启动 uredis 容器?"; then echo "未找到 uredis 容器,尝试 docker start uredis..."
do_clear_data=true docker start uredis || true
else
log_info "用户取消删除 redis data 数据"
fi
fi fi
if [ "$do_clear_data" != true ]; then sleep 3
show_operation_summary "修复 redis 容器异常" "${YELLOW}取消${NC} (用户取消删除数据)" if docker ps --format '{{.Names}}' | grep -qw uredis; then
echo "uredis 容器启动成功,Redis 文件损坏修复完成。"
return 0 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 else
log_error "清空数据后启动 uredis 容器失败,请检查 docker / 容器配置" echo "uredis 容器仍未启动成功,请人工进一步排查。"
show_operation_summary "修复 redis 容器异常" "${RED}失败${NC} (docker start uredis 失败)"
return 1 return 1
fi fi
} }
......
...@@ -1645,34 +1645,27 @@ function Test-ContainerInformation { ...@@ -1645,34 +1645,27 @@ function Test-ContainerInformation {
} }
# ===================================================================== # =====================================================================
# Redis 容器异常:日志导出 + 远端修复 + 复检 (需求文档第12点) # Redis 容器异常:日志导出 + 远端修复 + 复检
# ===================================================================== # =====================================================================
# 1) Redis 日志路径根据平台判断 # 1) Redis 日志路径根据平台判断
$platformType = $Global:PlatformType # 你前面平台识别时应已设置此全局变量:'new' 或 'old' $platformType = $Global:PlatformType
$redisLogPath = "/var/www/redis/data/redis.log" # 传统平台默认路径 $redisLogPath = "/var/www/redis/data/redis.log"
if ($platformType -eq "new") { if ($platformType -eq "new") {
# 新统一平台路径(根据文档):/data/middleware/redis/data/redis.log
$redisLogPath = "/data/middleware/redis/data/redis.log" $redisLogPath = "/data/middleware/redis/data/redis.log"
} }
# 本地自检结果目录:形如 output\<IP>\logs\redis\ # 本地目录
$baseOutputDir = Join-Path -Path $PSScriptRoot -ChildPath "output" $baseOutputDir = Join-Path -Path $PSScriptRoot -ChildPath "output"
$serverDir = Join-Path -Path $baseOutputDir -ChildPath $Server.IP $serverDir = Join-Path -Path $baseOutputDir -ChildPath $Server.IP
$redisOutDir = Join-Path -Path $serverDir -ChildPath "logs\redis" $redisOutDir = Join-Path -Path $serverDir -ChildPath "logs\redis"
if (-not (Test-Path $redisOutDir)) { if (-not (Test-Path $redisOutDir)) {
New-Item -Path $redisOutDir -ItemType Directory -Force | Out-Null New-Item -Path $redisOutDir -ItemType Directory -Force | Out-Null
} }
$localRedisLog = Join-Path -Path $redisOutDir -ChildPath "redis.log" $localRedisLog = Join-Path -Path $redisOutDir -ChildPath "redis.log"
Write-Log -Level "INFO" -Message "[Redis] 准备导出 redis 日志: $redisLogPath -> $localRedisLog" Write-Log -Level "INFO" -Message "[Redis] 准备导出 redis 日志: $redisLogPath -> $localRedisLog"
# 2) 从远端导出 redis.log 到本地
# 这里假定你已有一个封装的远程下载函数,例如 Download-RemoteFile
# 如果你实际用的是 Get-SFTPItem 或者其他名字,请替换为你的函数名和参数。
$exportOk = $false $exportOk = $false
try { try {
$downloadRes = Download-RemoteFile -Server $Server -RemotePath $redisLogPath -LocalPath $localRedisLog -ErrorAction Stop $downloadRes = Download-RemoteFile -Server $Server -RemotePath $redisLogPath -LocalPath $localRedisLog -ErrorAction Stop
...@@ -1694,28 +1687,22 @@ function Test-ContainerInformation { ...@@ -1694,28 +1687,22 @@ function Test-ContainerInformation {
} }
# 3) Redis 容器异常判定 # 3) Redis 容器异常判定
# 条件:uredis 容器未运行,且没有其他名字中包含 'redis' 的运行中容器
$redisRunning = $runningContainers | Where-Object { $_.Name -match '(?i)redis' } $redisRunning = $runningContainers | Where-Object { $_.Name -match '(?i)redis' }
$uredisRunning = $redisRunning | Where-Object { $_.Name -eq 'uredis' } $uredisRunning = $redisRunning | Where-Object { $_.Name -eq 'uredis' }
$uredisStopped = $stoppedContainers | Where-Object { $_.Name -eq 'uredis' } $uredisStopped = $stoppedContainers | Where-Object { $_.Name -eq 'uredis' }
$redisNeedRepair = $false $redisNeedRepair = $false
if (-not $uredisRunning -and $uredisStopped) { if (-not $uredisRunning -and $uredisStopped) {
if (-not $redisRunning) { if (-not $redisRunning) {
# uredis 存在但未运行,且没有其他 redis 命名的运行中容器
$redisNeedRepair = $true $redisNeedRepair = $true
} }
} }
if (-not $redisNeedRepair) { # 只有 Redis 需要修复时,才准备 repairItem
Write-Log -Level "INFO" -Message "[Redis] 未检测到需要自动修复的 Redis 容器异常" $repairItem = $null
return $results if ($redisNeedRepair) {
}
Write-Log -Level "ERROR" -Message "[Redis] 检测到 Redis 容器异常:uredis 未运行,且无其他 redis 命名容器运行,开始执行远端修复" Write-Log -Level "ERROR" -Message "[Redis] 检测到 Redis 容器异常:uredis 未运行,且无其他 redis 命名容器运行,开始执行远端修复"
# 4) 远端修复:上传 issue_handler.sh 并执行 redis_container_exception (非交互模式)
$repairItem = [ordered]@{ $repairItem = [ordered]@{
Check = "Redis容器修复" Check = "Redis容器修复"
Status = "未执行" Status = "未执行"
...@@ -1731,8 +1718,6 @@ function Test-ContainerInformation { ...@@ -1731,8 +1718,6 @@ function Test-ContainerInformation {
Port = $Server.Port Port = $Server.Port
} }
# 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" $repairRes = Upload_the_repair_script -Server $serverForRepair -Action "redis_container_exception" -Platform "auto" -RemoteDir "/home/repair_scripts"
if ($repairRes -and $repairRes['Success']) { if ($repairRes -and $repairRes['Success']) {
...@@ -1741,7 +1726,7 @@ function Test-ContainerInformation { ...@@ -1741,7 +1726,7 @@ function Test-ContainerInformation {
$repairItem.Details = "远端脚本执行成功 (redis_container_exception)" $repairItem.Details = "远端脚本执行成功 (redis_container_exception)"
$repairItem.Success = $true $repairItem.Success = $true
# 5) 修复完成后的复检 # 复检
Write-Log -Level "INFO" -Message "[Redis] 修复后进行 uredis 容器复检..." Write-Log -Level "INFO" -Message "[Redis] 修复后进行 uredis 容器复检..."
$checkCmd = "docker ps --format '{{.Names}}' | grep -w 'uredis' || echo 'NO_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 $recheck = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
...@@ -1775,9 +1760,78 @@ function Test-ContainerInformation { ...@@ -1775,9 +1760,78 @@ function Test-ContainerInformation {
$repairItem.Details = "调用修复脚本异常:$($_.Exception.Message)" $repairItem.Details = "调用修复脚本异常:$($_.Exception.Message)"
} }
# 把 Redis 修复结果写入 $results
$results += $repairItem $results += $repairItem
}
else {
Write-Log -Level "INFO" -Message "[Redis] 未检测到需要自动修复的 Redis 容器异常"
}
# =====================================================================
# Emqx 容器异常:日志导出 + 异常判定(远端修复暂不实现)
# =====================================================================
# 1) emqx 日志路径根据平台判断
$emqxLogPath = "/var/www/emqx/log/emqx.log.1"
if ($platformType -eq "new") {
$emqxLogPath = "/data/middleware/emqx/log/emqx.log.1"
}
$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 {
$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)"
}
$results += @{
Check = "Emqx日志导出"
Status = ($emqxExportOk ? "完成" : "失败")
Details = $emqxExportOk ? "emqx.log.1 导出成功 -> $localEmqxLog" : "emqx.log.1 导出失败或文件不存在 ($emqxLogPath)"
Success = $emqxExportOk
}
# 3) Emqx 容器异常判定
$emqxRunning = $runningContainers | Where-Object { $_.Name -match '(?i)emqx' }
$uemqxRunning = $emqxRunning | Where-Object { $_.Name -eq 'uemqx' }
$uemqxStopped = $stoppedContainers | Where-Object { $_.Name -eq 'uemqx' }
$emqxNeedRepair = $false
if (-not $uemqxRunning -and $uemqxStopped) {
if (-not $emqxRunning) {
$emqxNeedRepair = $true
}
}
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 return $results
} }
# ================================ # ================================
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论