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

feat(script): 添加传统平台服务检测与资源分析功能

- 实现传统平台 ujava 容器内外服务检测逻辑
- 添加 DNS 解析功能检测,支持域名解析与网络连通性测试
- 实现服务器资源分析,包括 CPU、内存、磁盘及防火墙状态监控
- 更新检测报告展示逻辑,支持新增检测项结果显示
- 完善传统平台 upython 容器端口检测配置
- 优化服务检测流程,区分新旧平台处理逻辑
上级 b33053c4
......@@ -26,7 +26,7 @@
##### 系统识别(✅ 已实现):
自动检测目标服务器的系统类型(检测容器分为三种:ujava、upython、upython_voice,如果有ujava则有会议预定系统、python对应运维集控系统、upython_voice对应转录系统)
##### 服务进程检测(❌ 开发进行中):
##### 服务进程检测(✅ 已实现):
根据平台类型不同需要分别在不同的位置进行检测,具体如下:
###### 新统一平台(✅ 已实现):
......@@ -61,7 +61,7 @@
tcp6 0 0 :::11211 :::* LISTEN 79/memcached
tcp6 0 0 :::80 :::* LISTEN 47/nginx: master pr
###### 传统平台(❌ 未实现):
###### 传统平台(✅ 已实现):
ujava后端服务分为容器内和容器外
需进入ujava2容器内检查,共有以下两个基础服务进程:
root 8 1 0 15:26 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
......@@ -78,8 +78,37 @@
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 105/memcached
tcp6 0 0 :::11211 :::* LISTEN 105/memcached
##### DNS解析问题(❌ 未实现):
##### DNS解析问题(✅ 已实现):
检测目标服务器的DNS配置,能否正常进行解析等相关操作
##### 服务器资源分析(❌ 未实现):
检查目标服务器的磁盘空间情况、内存使用情况以及cpu使用情况
\ No newline at end of file
##### 服务器资源分析(✅ 已实现):
检查目标服务器的磁盘空间情况、内存使用情况、cpu使用情况、防火墙开放端口情况、服务器架构以及操作系统记录
##### 服务日志导出(❌ 未实现):
将目标服务器上的服务日志采集,需判断传统平台还是新统一平台
传统平台:
如果有ujava容器:
1、将/var/www/java/api-java-meeting2.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出来命名为:对内后端_ubains-INFO-AND-ERROR.log
2、将/var/www/java/external-meeting-api/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出来命名为:对外后端_ubains-INFO-AND-ERROR.log
如果有upython容器:
1、将/var/www/html/log目录下的error.log、uinfo.log和uwsgi.log日志文件导出来命名都增加前缀:运维集控_error.log、运维集控_uinfo.log和运维集控_uwsgi.log
新统一平台:
如果有ujava容器:
1、将/data/services/api/auth/auth-sso-auth目录下的log.out日志文件导出来命名为auth_log.out
2、将/data/services/api/auth/auth-sso-gatway目录下的log.out日志文件导出来命名为gatway_log.out
3、将/data/services/api/auth/auth-sso-system目录下的log.out日志文件导出命名为system_log.out
4、将/data/services/api/java-meeting/java-meeting2.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对内2.0_ubains-INFO-AND-ERROR.log
5、将/data/services/api/java-meeting/java-meeting3.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对内3.0_ubains-INFO-AND-ERROR.log
6、将/data/services/api/java-meeting/java-meeting-extapi/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对外服务_ubains-INFO-AND-ERROR.log
7、将/data/services/api/java-meeting/java-message-scheduling/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为信息调度_ubains-INFO-AND-ERROR.log
8、将/data/services/api/java-meeting/java-mqtt/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为MQTT_ubains-INFO-AND-ERROR.log
9、将/data/services/api/java-meeting/java-quartz/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为定时任务_ubains-INFO-AND-ERROR.log
......@@ -141,6 +141,37 @@ $UpythonVoicePorts = @(
@{ Port = 80; Process = "nginx"; Description = "Nginx Web 服务 (80)" }
)
# ================================
# 传统平台配置
# ================================
# 传统平台 ujava 容器内服务(只有nginx和meeting服务)
$UjavaOldPlatformContainerServices = @{
"nginx" = "nginx: master process"
"meeting" = "ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
}
# 传统平台 ujava 宿主机服务
$UjavaOldPlatformHostServices = @{
"extapi" = "ubains-meeting-api-1.0-SNAPSHOT.jar"
}
# 传统平台 upython 容器内需要监听的端口
$UpythonOldPlatformPorts = @(
@{ Port = 8081; Process = "nginx"; Description = "Nginx 代理服务 (8081)" }
@{ Port = 8443; Process = "nginx"; Description = "Nginx HTTPS 服务 (8443)" }
@{ Port = 8000; Process = "uwsgi"; Description = "uWSGI 应用服务" }
@{ Port = 8002; Process = "httpd"; Description = "Apache HTTPD 服务" }
@{ Port = 11211; Process = "memcached"; Description = "Memcached 缓存服务" }
)
# DNS 测试域名列表
$DNSTestDomains = @(
"www.baidu.com"
"www.qq.com"
"www.aliyun.com"
)
# ================================
# 日志函数
# ================================
......@@ -651,6 +682,534 @@ function Test-UjavaHostServices {
return $results
}
# ================================
# 检测传统平台 ujava 容器内服务
# ================================
function Test-UjavaOldPlatformContainerServices {
param(
[hashtable]$Server,
[string]$ContainerName
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 检测传统平台 ujava 容器内服务 ($ContainerName) =========="
$results = @()
foreach ($serviceName in $UjavaOldPlatformContainerServices.Keys) {
$pattern = $UjavaOldPlatformContainerServices[$serviceName]
# 在容器内检查进程
$checkCmd = "docker exec $ContainerName ps aux 2>/dev/null | grep -v grep | grep '$pattern' | wc -l"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
$count = 0
try {
$outputLines = $result.Output -split "`n" | Where-Object { $_ -match '^\d+$' }
if ($outputLines) {
$count = [int]($outputLines | Select-Object -Last 1).Trim()
}
}
catch {
$count = 0
}
$status = if ($count -gt 0) { "运行中" } else { "未运行" }
$statusColor = if ($count -gt 0) { "SUCCESS" } else { "ERROR" }
$results += @{
Service = $serviceName
Pattern = $pattern
Status = $status
Running = ($count -gt 0)
}
$statusIcon = if ($count -gt 0) { "[OK]" } else { "[FAIL]" }
Write-Log -Level $statusColor -Message " $statusIcon $serviceName ($pattern): $status"
}
return $results
}
# ================================
# 检测传统平台 ujava 宿主机服务
# ================================
function Test-UjavaOldPlatformHostServices {
param(
[hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 检测传统平台 ujava 宿主机服务 =========="
$results = @()
foreach ($serviceName in $UjavaOldPlatformHostServices.Keys) {
$jarFileName = $UjavaOldPlatformHostServices[$serviceName]
# 在宿主机检查进程,传统平台路径在 /var/www/java/external-meeting-api
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep '/var/www/java/external-meeting-api' | wc -l"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
$count = 0
try {
$outputLines = $result.Output -split "`n" | Where-Object { $_ -match '^\d+$' }
if ($outputLines) {
$count = [int]($outputLines | Select-Object -Last 1).Trim()
}
}
catch {
$count = 0
}
# 如果没找到,尝试只匹配jar文件名(兼容性)
if ($count -eq 0) {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | wc -l"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
try {
$outputLines = $result.Output -split "`n" | Where-Object { $_ -match '^\d+$' }
if ($outputLines) {
$count = [int]($outputLines | Select-Object -Last 1).Trim()
}
}
catch {
$count = 0
}
}
$status = if ($count -gt 0) { "运行中" } else { "未运行" }
$statusColor = if ($count -gt 0) { "SUCCESS" } else { "ERROR" }
$results += @{
Service = $serviceName
Pattern = $jarFileName
Status = $status
Running = ($count -gt 0)
}
$statusIcon = if ($count -gt 0) { "[OK]" } else { "[FAIL]" }
Write-Log -Level $statusColor -Message " $statusIcon $serviceName ($jarFileName): $status"
}
return $results
}
# ================================
# 检测 DNS 解析
# ================================
function Test-DNSResolution {
param(
[hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 检测 DNS 解析功能 =========="
$results = @()
# 1. 检查 /etc/resolv.conf 配置文件
Write-Log -Level "INFO" -Message "检查 DNS 配置文件..."
$resolvCheck = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "cat /etc/resolv.conf 2>/dev/null | grep -E '^nameserver' | head -n 3"
$dnsServers = @()
if ($resolvCheck.ExitCode -eq 0 -and $resolvCheck.Output) {
$dnsLines = $resolvCheck.Output -split "`n" | Where-Object { $_ -match 'nameserver' }
foreach ($line in $dnsLines) {
if ($line -match 'nameserver\s+(\S+)') {
$dnsServers += $Matches[1]
}
}
if ($dnsServers.Count -gt 0) {
Write-Log -Level "SUCCESS" -Message " 检测到 DNS 服务器: $($dnsServers -join ', ')"
$results += @{
Check = "DNS配置"
Status = "正常"
Details = "DNS服务器: $($dnsServers -join ', ')"
Success = $true
}
}
else {
Write-Log -Level "WARN" -Message " 未检测到 DNS 服务器配置"
$results += @{
Check = "DNS配置"
Status = "异常"
Details = "未找到DNS服务器配置"
Success = $false
}
}
}
else {
Write-Log -Level "WARN" -Message " 无法读取 DNS 配置文件"
$results += @{
Check = "DNS配置"
Status = "异常"
Details = "无法读取 /etc/resolv.conf"
Success = $false
}
}
# 2. 测试 DNS 解析功能
Write-Log -Level "INFO" -Message "测试 DNS 解析功能..."
$dnsTestSuccess = 0
$dnsTestTotal = $DNSTestDomains.Count
foreach ($domain in $DNSTestDomains) {
$testCmd = "nslookup $domain 2>&1 | head -n 5 | grep -E 'Name:|Address:' | head -n 2"
$testResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $testCmd
if ($testResult.ExitCode -eq 0 -and $testResult.Output -match 'Name:|Address:') {
$dnsTestSuccess++
Write-Log -Level "SUCCESS" -Message " [OK] $domain : 解析成功"
}
else {
# 尝试使用 host 命令
$testCmd2 = "host $domain 2>&1 | head -n 1"
$testResult2 = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $testCmd2
if ($testResult2.ExitCode -eq 0 -and $testResult2.Output -match 'has address|has IPv4') {
$dnsTestSuccess++
Write-Log -Level "SUCCESS" -Message " [OK] $domain : 解析成功"
}
else {
Write-Log -Level "ERROR" -Message " [FAIL] $domain : 解析失败"
}
}
}
$dnsTestStatus = if ($dnsTestSuccess -eq $dnsTestTotal) { "正常" } elseif ($dnsTestSuccess -gt 0) { "部分正常" } else { "异常" }
$dnsTestColor = if ($dnsTestSuccess -eq $dnsTestTotal) { "SUCCESS" } elseif ($dnsTestSuccess -gt 0) { "WARN" } else { "ERROR" }
Write-Log -Level $dnsTestColor -Message " DNS 解析测试结果: $dnsTestSuccess/$dnsTestTotal 成功"
$results += @{
Check = "DNS解析"
Status = $dnsTestStatus
Details = "测试域名解析: $dnsTestSuccess/$dnsTestTotal 成功"
Success = ($dnsTestSuccess -gt 0)
SuccessCount = $dnsTestSuccess
TotalCount = $dnsTestTotal
}
# 3. 测试 ping 连通性(可选,验证DNS解析的IP是否可达)
Write-Log -Level "INFO" -Message "测试网络连通性..."
$pingSuccess = 0
$pingTotal = 0
foreach ($domain in $DNSTestDomains) {
$pingCmd = "ping -c 2 -W 2 $domain 2>&1 | grep -E 'packets transmitted|0% packet loss' | head -n 1"
$pingResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $pingCmd
$pingTotal++
if ($pingResult.ExitCode -eq 0 -and $pingResult.Output -match '0% packet loss|packets transmitted') {
$pingSuccess++
Write-Log -Level "SUCCESS" -Message " [OK] $domain : 网络连通正常"
}
else {
Write-Log -Level "WARN" -Message " [WARN] $domain : 网络连通异常或超时"
}
}
if ($pingTotal -gt 0) {
$pingStatus = if ($pingSuccess -eq $pingTotal) { "正常" } elseif ($pingSuccess -gt 0) { "部分正常" } else { "异常" }
$pingColor = if ($pingSuccess -eq $pingTotal) { "SUCCESS" } elseif ($pingSuccess -gt 0) { "WARN" } else { "ERROR" }
Write-Log -Level $pingColor -Message " 网络连通性测试结果: $pingSuccess/$pingTotal 成功"
$results += @{
Check = "网络连通性"
Status = $pingStatus
Details = "Ping测试: $pingSuccess/$pingTotal 成功"
Success = ($pingSuccess -gt 0)
SuccessCount = $pingSuccess
TotalCount = $pingTotal
}
}
return $results
}
# ================================
# 服务器资源分析
# ================================
function Test-ServerResources {
param(
[hashtable]$Server
)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 服务器资源分析 =========="
$results = @{
OS = $null
Architecture = $null
CPU = $null
Memory = $null
Disk = @()
Firewall = $null
}
# 1. 检测操作系统信息
Write-Log -Level "INFO" -Message "检测操作系统信息..."
$osCmd = "cat /etc/os-release 2>/dev/null | grep -E '^(NAME|VERSION)=' | head -n 2 || cat /etc/redhat-release 2>/dev/null || uname -o"
$osResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $osCmd
if ($osResult.ExitCode -eq 0 -and $osResult.Output) {
$osInfo = ($osResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join " | "
$osInfo = $osInfo -replace 'NAME=|VERSION=|"', ''
Write-Log -Level "SUCCESS" -Message " 操作系统: $osInfo"
$results.OS = @{
Info = $osInfo
Status = "正常"
Success = $true
}
}
else {
Write-Log -Level "WARN" -Message " 无法获取操作系统信息"
$results.OS = @{
Info = "未知"
Status = "未知"
Success = $false
}
}
# 2. 检测服务器架构
Write-Log -Level "INFO" -Message "检测服务器架构..."
$archCmd = "uname -m && uname -r"
$archResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $archCmd
if ($archResult.ExitCode -eq 0 -and $archResult.Output) {
$archLines = $archResult.Output -split "`n" | Where-Object { $_ -match '\S' }
$arch = if ($archLines.Count -ge 1) { $archLines[0].Trim() } else { "未知" }
$kernel = if ($archLines.Count -ge 2) { $archLines[1].Trim() } else { "未知" }
Write-Log -Level "SUCCESS" -Message " 架构: $arch | 内核: $kernel"
$results.Architecture = @{
Arch = $arch
Kernel = $kernel
Status = "正常"
Success = $true
}
}
else {
Write-Log -Level "WARN" -Message " 无法获取架构信息"
$results.Architecture = @{
Arch = "未知"
Kernel = "未知"
Status = "未知"
Success = $false
}
}
# 3. 检测 CPU 使用情况
Write-Log -Level "INFO" -Message "检测 CPU 使用情况..."
$cpuCmd = "top -bn1 | grep 'Cpu(s)' | awk '{print `$2+`$4}' 2>/dev/null || mpstat 1 1 2>/dev/null | tail -n 1 | awk '{print 100-`$NF}'"
$cpuResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cpuCmd
$cpuUsage = 0
if ($cpuResult.ExitCode -eq 0 -and $cpuResult.Output) {
$cpuLine = ($cpuResult.Output -split "`n" | Where-Object { $_ -match '^\d' } | Select-Object -First 1)
if ($cpuLine) {
try {
$cpuUsage = [math]::Round([double]$cpuLine.Trim(), 1)
}
catch {
$cpuUsage = 0
}
}
}
# 获取 CPU 核心数
$cpuCoresCmd = "nproc 2>/dev/null || grep -c processor /proc/cpuinfo"
$cpuCoresResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cpuCoresCmd
$cpuCores = 0
if ($cpuCoresResult.ExitCode -eq 0 -and $cpuCoresResult.Output) {
try {
$cpuCores = [int]($cpuCoresResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' } | Select-Object -First 1).Trim()
}
catch {
$cpuCores = 0
}
}
$cpuStatus = if ($cpuUsage -lt 70) { "正常" } elseif ($cpuUsage -lt 90) { "警告" } else { "危险" }
$cpuColor = if ($cpuUsage -lt 70) { "SUCCESS" } elseif ($cpuUsage -lt 90) { "WARN" } else { "ERROR" }
Write-Log -Level $cpuColor -Message " CPU 使用率: ${cpuUsage}% (核心数: $cpuCores) [$cpuStatus]"
$results.CPU = @{
Usage = $cpuUsage
Cores = $cpuCores
Status = $cpuStatus
Success = ($cpuUsage -lt 90)
}
# 4. 检测内存使用情况
Write-Log -Level "INFO" -Message "检测内存使用情况..."
$memCmd = 'free -m | grep Mem | awk ''{printf "%.1f %.1f %.1f", $2/1024, $3/1024, ($3/$2)*100}'''
$memResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $memCmd
$memTotal = 0
$memUsed = 0
$memPercent = 0
if ($memResult.ExitCode -eq 0 -and $memResult.Output) {
$memParts = ($memResult.Output -split "`n" | Where-Object { $_ -match '\S' } | Select-Object -First 1) -split '\s+'
if ($memParts.Count -ge 3) {
try {
$memTotal = [math]::Round([double]$memParts[0], 1)
$memUsed = [math]::Round([double]$memParts[1], 1)
$memPercent = [math]::Round([double]$memParts[2], 1)
}
catch {
$memTotal = 0
$memUsed = 0
$memPercent = 0
}
}
}
$memStatus = if ($memPercent -lt 70) { "正常" } elseif ($memPercent -lt 90) { "警告" } else { "危险" }
$memColor = if ($memPercent -lt 70) { "SUCCESS" } elseif ($memPercent -lt 90) { "WARN" } else { "ERROR" }
Write-Log -Level $memColor -Message " 内存使用: ${memUsed}GB / ${memTotal}GB (${memPercent}%) [$memStatus]"
$results.Memory = @{
Total = $memTotal
Used = $memUsed
Percent = $memPercent
Status = $memStatus
Success = ($memPercent -lt 90)
}
# 5. 检测磁盘空间情况
Write-Log -Level "INFO" -Message "检测磁盘空间情况..."
$diskCmd = "df -h | grep -E '^/dev/' | awk '{print `$1,`$2,`$3,`$5,`$6}'"
$diskResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $diskCmd
$diskList = @()
$diskWarning = $false
if ($diskResult.ExitCode -eq 0 -and $diskResult.Output) {
$diskLines = $diskResult.Output -split "`n" | Where-Object { $_ -match '\S' }
foreach ($line in $diskLines) {
$parts = $line -split '\s+'
if ($parts.Count -ge 5) {
$device = $parts[0]
$size = $parts[1]
$used = $parts[2]
$usePercent = $parts[3] -replace '%', ''
$mountPoint = $parts[4]
try {
$usePercentNum = [int]$usePercent
}
catch {
$usePercentNum = 0
}
$diskStatus = if ($usePercentNum -lt 70) { "正常" } elseif ($usePercentNum -lt 90) { "警告" } else { "危险" }
$diskColor = if ($usePercentNum -lt 70) { "SUCCESS" } elseif ($usePercentNum -lt 90) { "WARN" } else { "ERROR" }
if ($usePercentNum -ge 70) {
$diskWarning = $true
}
Write-Log -Level $diskColor -Message " 磁盘 $mountPoint : ${used}/${size} (${usePercent}%) [$diskStatus]"
$diskList += @{
Device = $device
Size = $size
Used = $used
Percent = $usePercentNum
MountPoint = $mountPoint
Status = $diskStatus
}
}
}
}
else {
Write-Log -Level "WARN" -Message " 无法获取磁盘信息"
}
$results.Disk = $diskList
# 6. 检测防火墙开放端口情况
Write-Log -Level "INFO" -Message "检测防火墙开放端口..."
# 先检测防火墙状态
$firewallStatusCmd = "systemctl is-active firewalld 2>/dev/null || service iptables status 2>/dev/null | head -n 1 || echo 'unknown'"
$firewallStatusResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $firewallStatusCmd
$firewallActive = $false
$firewallType = "unknown"
if ($firewallStatusResult.Output -match 'active') {
$firewallActive = $true
$firewallType = "firewalld"
}
elseif ($firewallStatusResult.Output -match 'running|OK') {
$firewallActive = $true
$firewallType = "iptables"
}
$openPorts = @()
if ($firewallActive) {
if ($firewallType -eq "firewalld") {
# 使用 firewalld 获取开放端口
$portsCmd = "firewall-cmd --list-ports 2>/dev/null && firewall-cmd --list-services 2>/dev/null"
$portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd
if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join ", "
}
}
else {
# 使用 iptables 获取开放端口
$portsCmd = "iptables -L INPUT -n 2>/dev/null | grep ACCEPT | grep -oP 'dpt:\d+' | cut -d: -f2 | sort -u | head -n 20"
$portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd
if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' }) -join ", "
}
}
Write-Log -Level "INFO" -Message " 防火墙状态: 已启用 ($firewallType)"
if ($openPorts) {
Write-Log -Level "INFO" -Message " 开放端口/服务: $openPorts"
}
else {
Write-Log -Level "WARN" -Message " 未检测到明确开放的端口"
}
}
else {
Write-Log -Level "WARN" -Message " 防火墙状态: 未启用或未安装"
$openPorts = "防火墙未启用"
}
$results.Firewall = @{
Active = $firewallActive
Type = $firewallType
OpenPorts = $openPorts
Status = if ($firewallActive) { "已启用" } else { "未启用" }
}
# 7. 检测系统负载
Write-Log -Level "INFO" -Message "检测系统负载..."
$loadCmd = "uptime | awk -F'load average:' '{print `$2}' | tr -d ' '"
$loadResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $loadCmd
if ($loadResult.ExitCode -eq 0 -and $loadResult.Output) {
$loadAvg = ($loadResult.Output -split "`n" | Where-Object { $_ -match '\S' } | Select-Object -First 1).Trim()
$loadParts = $loadAvg -split ','
if ($loadParts.Count -ge 1) {
$load1 = $loadParts[0].Trim()
Write-Log -Level "INFO" -Message " 系统负载 (1/5/15分钟): $loadAvg"
}
}
return $results
}
# ================================
# 检测容器内端口服务
# ================================
......@@ -711,7 +1270,9 @@ function Show-HealthReport {
[array]$UjavaContainerResults,
[array]$UjavaHostResults,
[array]$UpythonResults,
[array]$UpythonVoiceResults
[array]$UpythonVoiceResults,
[array]$DNSResults,
[hashtable]$ResourceResults
)
Write-Host ""
......@@ -817,6 +1378,67 @@ function Show-HealthReport {
Write-Host ""
}
# DNS 检测结果统计
if ($DNSResults -and $DNSResults.Count -gt 0) {
Write-Host "【DNS 解析检测结果】" -ForegroundColor Yellow
foreach ($r in $DNSResults) {
$statusColor = if ($r.Success) { "Green" } else { "Red" }
$statusIcon = if ($r.Success) { "[OK]" } else { "[FAIL]" }
Write-Host " $statusIcon $($r.Check): $($r.Status)" -ForegroundColor $statusColor
if ($r.Details) {
Write-Host " 详情: $($r.Details)" -ForegroundColor Gray
}
}
Write-Host ""
}
# 服务器资源分析结果
if ($ResourceResults) {
Write-Host "【服务器资源分析】" -ForegroundColor Yellow
# 操作系统信息
if ($ResourceResults.OS) {
Write-Host " 操作系统: $($ResourceResults.OS.Info)" -ForegroundColor Cyan
}
# 架构信息
if ($ResourceResults.Architecture) {
Write-Host " 系统架构: $($ResourceResults.Architecture.Arch) | 内核: $($ResourceResults.Architecture.Kernel)" -ForegroundColor Cyan
}
# CPU 使用情况
if ($ResourceResults.CPU) {
$cpuColor = if ($ResourceResults.CPU.Success) { "Green" } else { "Red" }
Write-Host " CPU 使用率: $($ResourceResults.CPU.Usage)% (核心数: $($ResourceResults.CPU.Cores)) [$($ResourceResults.CPU.Status)]" -ForegroundColor $cpuColor
}
# 内存使用情况
if ($ResourceResults.Memory) {
$memColor = if ($ResourceResults.Memory.Success) { "Green" } else { "Red" }
Write-Host " 内存使用: $($ResourceResults.Memory.Used)GB / $($ResourceResults.Memory.Total)GB ($($ResourceResults.Memory.Percent)%) [$($ResourceResults.Memory.Status)]" -ForegroundColor $memColor
}
# 磁盘使用情况
if ($ResourceResults.Disk -and $ResourceResults.Disk.Count -gt 0) {
Write-Host " 磁盘使用情况:" -ForegroundColor Cyan
foreach ($disk in $ResourceResults.Disk) {
$diskColor = if ($disk.Percent -lt 70) { "Green" } elseif ($disk.Percent -lt 90) { "Yellow" } else { "Red" }
Write-Host " $($disk.MountPoint): $($disk.Used)/$($disk.Size) ($($disk.Percent)%) [$($disk.Status)]" -ForegroundColor $diskColor
}
}
# 防火墙状态
if ($ResourceResults.Firewall) {
$fwColor = if ($ResourceResults.Firewall.Active) { "Green" } else { "Yellow" }
Write-Host " 防火墙状态: $($ResourceResults.Firewall.Status) ($($ResourceResults.Firewall.Type))" -ForegroundColor $fwColor
if ($ResourceResults.Firewall.OpenPorts -and $ResourceResults.Firewall.Active) {
Write-Host " 开放端口/服务: $($ResourceResults.Firewall.OpenPorts)" -ForegroundColor Gray
}
}
Write-Host ""
}
# 总结
Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host "【检测总结】" -ForegroundColor Yellow
......@@ -918,30 +1540,47 @@ function Main {
}
}
else {
# 传统平台检测:直接在宿主机检测服务
Write-Log -Level "INFO" -Message "传统平台:在宿主机检测服务"
# 传统平台检测:使用传统平台专用检测逻辑
Write-Log -Level "INFO" -Message "传统平台:使用传统平台检测逻辑"
# ujava 服务检测(传统平台在宿主机运行)
$ujavaContainerResults = Test-UjavaServices -Server $server -ContainerName $null -PlatformType $platformType
$ujavaHostResults = Test-UjavaHostServices -Server $server
# ujava 容器内服务检测(传统平台只有nginx和meeting)
if ($systemInfo.HasUjava) {
$ujavaContainerResults = Test-UjavaOldPlatformContainerServices -Server $server -ContainerName $systemInfo.UjavaContainer
}
else {
Write-Log -Level "WARN" -Message "未检测到 ujava 容器,跳过容器内服务检测"
}
# upython 检测(如果有容器)
# ujava 宿主机服务检测(传统平台extapi在/var/www/java/external-meeting-api)
$ujavaHostResults = Test-UjavaOldPlatformHostServices -Server $server
# upython 检测(传统平台使用不同的端口列表)
if ($systemInfo.HasUpython) {
$upythonResults = Test-ContainerPorts -Server $server -ContainerName $systemInfo.UpythonContainer -PortList $UpythonPorts -ServiceType "upython"
$upythonResults = Test-ContainerPorts -Server $server -ContainerName $systemInfo.UpythonContainer -PortList $UpythonOldPlatformPorts -ServiceType "upython (传统平台)"
}
# upython_voice 检测(如果有容器
# upython_voice 检测(传统平台通常没有,但如果有容器也检测
if ($systemInfo.HasUpythonVoice) {
$upythonVoiceResults = Test-ContainerPorts -Server $server -ContainerName $systemInfo.UpythonVoiceContainer -PortList $UpythonVoicePorts -ServiceType "upython_voice"
}
}
# DNS 解析检测(所有平台都需要检测)
Write-Host ""
$dnsResults = Test-DNSResolution -Server $server
# 服务器资源分析(所有平台都需要检测)
Write-Host ""
$resourceResults = Test-ServerResources -Server $server
# 生成检测报告
Show-HealthReport -Server $server -PlatformType $platformType -SystemInfo $systemInfo `
-UjavaContainerResults $ujavaContainerResults `
-UjavaHostResults $ujavaHostResults `
-UpythonResults $upythonResults `
-UpythonVoiceResults $upythonVoiceResults
-UpythonVoiceResults $upythonVoiceResults `
-DNSResults $dnsResults `
-ResourceResults $resourceResults
}
# 执行主函数
......
#Requires -Version 5.1
# ================================
# remote_update.ps1 - Remote Container Update Script (PowerShell Version)
# ================================
# Features:
# 1. Verify target server architecture is x86
# 2. Transfer image files and deployment scripts to remote directory
# 3. Stop old containers on remote server
# 4. Auto-detect target server platform type (new/traditional)
# 5. Auto-increment container numbers and execute remote deployment script
# 6. Clean up remote image files after deployment
#
# Dependencies:
# - plink.exe (PuTTY Link) - SSH command execution
# - pscp.exe (PuTTY SCP) - File transfer
# Please ensure these tools are installed and added to system PATH
# ================================
param(
[string]$RemoteDir = "/home/containerUpdate"
)
# Strict mode
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# ================================
# Global Configuration
# ================================
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$REMOTE_ARCH_ALLOW_REGEX = '^(x86_64|amd64|i386|i686)$'
$SSH_TIMEOUT = 30
# Preset server list
$ServerList = @{
"1" = @{
IP = "192.168.5.48"
User = "root"
Pass = "Ubains@123"
Desc = "Standard Reservation Server"
}
"2" = @{
IP = "192.168.5.67"
User = "root"
Pass = "Ubains@123"
Desc = "Oman Project Server"
}
"3" = @{
IP = "192.168.5.47"
User = "root"
Pass = "Ubains@1234"
Desc = "Test Release Server"
}
}
# Container and image mapping configuration
$ContainerOptions = @("ujava", "uemqx", "uredis", "upython", "unacos", "unginx")
$ContainerImage = @{
"ujava" = "java1.8.0_472.tar.gz"
"uemqx" = "uemqx5.8.4.tar.gz"
"uredis" = "redis8.2.2.tar.gz"
"upython" = "python_v15.tar.gz"
"unacos" = "nacos-server-v2.5.2.tar.gz"
"unginx" = "nginx-1.29.3.tar.gz"
}
# Container Docker image names (for version verification)
$ContainerDockerImage = @{
"ujava" = "139.9.60.86:5000/ujava:v6"
"uemqx" = "139.9.60.86:5000/uemqx:v2"
"uredis" = "139.9.60.86:5000/redis:v3"
"upython_new" = "139.9.60.86:5000/upython:v15"
"upython_old" = "139.9.60.86:5000/upython:v14"
"unacos" = "nacos-server:v2.5.2"
"unginx" = "nginx:1.29.3"
}
# Local directory configuration
$LOCAL_EMQX_DIR = "/data/middleware/emqx"
$LOCAL_PYTHON_DIR = "/data/services/api/python-cmdb"
$LOCAL_NGINX_DIR = "/data/middleware/nginx"
# Deployment script name
$DEPLOY_SCRIPT = "container_update.sh"
# ================================
# Log Function
# ================================
function Write-Log {
param(
[Parameter(Mandatory=$true)]
[ValidateSet("INFO", "WARN", "ERROR")]
[string]$Level,
[Parameter(Mandatory=$true)]
[string]$Message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$colorMap = @{
"INFO" = "Green"
"WARN" = "Yellow"
"ERROR" = "Red"
}
$color = $colorMap[$Level]
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
# ================================
# Check Dependencies
# ================================
function Test-Dependencies {
Write-Log -Level "INFO" -Message "Checking dependencies..."
$tools = @("plink", "pscp")
$missingTools = @()
foreach ($tool in $tools) {
try {
$null = Get-Command $tool -ErrorAction Stop
Write-Log -Level "INFO" -Message " [OK] $tool installed"
}
catch {
$missingTools += $tool
Write-Log -Level "ERROR" -Message " [FAIL] $tool not installed"
}
}
if ($missingTools.Count -gt 0) {
Write-Log -Level "ERROR" -Message "Missing tools: $($missingTools -join ', ')"
Write-Log -Level "ERROR" -Message "Please install PuTTY toolkit and ensure plink.exe and pscp.exe are in system PATH"
Write-Log -Level "ERROR" -Message "Download: https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html"
return $false
}
Write-Log -Level "INFO" -Message "[OK] All dependencies ready"
return $true
}
# ================================
# SSH Execute Remote Command
# ================================
function Invoke-SSHCommand {
param(
[string]$HostName,
[string]$User,
[string]$Pass,
[int]$Port = 22,
[string]$Command
)
$plinkArgs = @(
"-ssh",
"-P", $Port,
"-l", $User,
"-pw", $Pass,
"-batch",
$HostName,
$Command
)
$result = & plink @plinkArgs 2>&1
$exitCode = $LASTEXITCODE
return @{
Output = ($result -join "`n")
ExitCode = $exitCode
}
}
# ================================
# SCP File Transfer
# ================================
function Send-SCPFile {
param(
[string]$LocalPath,
[string]$RemoteHost,
[string]$RemoteUser,
[string]$RemotePass,
[int]$RemotePort = 22,
[string]$RemotePath
)
$pscpArgs = @(
"-P", $RemotePort,
"-l", $RemoteUser,
"-pw", $RemotePass,
"-batch",
$LocalPath,
"${RemoteUser}@${RemoteHost}:${RemotePath}"
)
$result = & pscp @pscpArgs 2>&1
$exitCode = $LASTEXITCODE
return @{
Output = ($result -join "`n")
ExitCode = $exitCode
}
}
# ================================
# SCP Directory Transfer (Recursive)
# ================================
function Send-SCPDirectory {
param(
[string]$LocalPath,
[string]$RemoteHost,
[string]$RemoteUser,
[string]$RemotePass,
[int]$RemotePort = 22,
[string]$RemotePath
)
$pscpArgs = @(
"-P", $RemotePort,
"-l", $RemoteUser,
"-pw", $RemotePass,
"-batch",
"-r",
$LocalPath,
"${RemoteUser}@${RemoteHost}:${RemotePath}"
)
$result = & pscp @pscpArgs 2>&1
$exitCode = $LASTEXITCODE
return @{
Output = ($result -join "`n")
ExitCode = $exitCode
}
}
# ================================
# Select Container
# ================================
function Select-Container {
Write-Log -Level "INFO" -Message "Available containers:"
for ($i = 0; $i -lt $ContainerOptions.Count; $i++) {
$idx = $i + 1
Write-Host " [$idx] $($ContainerOptions[$i])"
}
$containerKey = Read-Host "Enter container number"
if ($containerKey -notmatch '^[1-6]$') {
Write-Log -Level "ERROR" -Message "Invalid container number, please enter 1-6"
return $null
}
$selectedIndex = [int]$containerKey - 1
$selectedContainer = $ContainerOptions[$selectedIndex]
Write-Log -Level "INFO" -Message "Selected container: $selectedContainer"
return $selectedContainer
}
# ================================
# Select Server
# ================================
function Select-Server {
Write-Log -Level "INFO" -Message "Available target servers:"
foreach ($key in ($ServerList.Keys | Sort-Object)) {
$server = $ServerList[$key]
Write-Host " [$key] $($server.Desc) ($($server.IP) $($server.User))"
}
Write-Host " [0] Manual input server info"
$serverKey = Read-Host "Enter server number"
if ($serverKey -eq "0") {
Write-Log -Level "INFO" -Message "Entering manual input mode"
$remoteHost = Read-Host "Enter target server IP address"
if ([string]::IsNullOrEmpty($remoteHost)) {
Write-Log -Level "ERROR" -Message "Server IP address cannot be empty"
return $null
}
$sshPortInput = Read-Host "Enter SSH port [default 22]"
if ([string]::IsNullOrEmpty($sshPortInput)) {
$sshPort = 22
} else {
$sshPort = [int]$sshPortInput
}
$remoteUserInput = Read-Host "Enter username [default root]"
if ([string]::IsNullOrEmpty($remoteUserInput)) {
$remoteUser = "root"
} else {
$remoteUser = $remoteUserInput
}
$remotePassSecure = Read-Host "Enter password" -AsSecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($remotePassSecure)
$remotePass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
if ([string]::IsNullOrEmpty($remotePass)) {
Write-Log -Level "ERROR" -Message "Password cannot be empty"
return $null
}
Write-Log -Level "INFO" -Message "Configured target server: ${remoteUser}@${remoteHost}:${sshPort}"
return @{
IP = $remoteHost
User = $remoteUser
Pass = $remotePass
Port = $sshPort
Desc = "Manual input server"
}
}
elseif ($ServerList.ContainsKey($serverKey)) {
$server = $ServerList[$serverKey].Clone()
$server.Port = 22
Write-Log -Level "INFO" -Message "Selected $($server.Desc) ($($server.IP))"
return $server
}
else {
Write-Log -Level "ERROR" -Message "Number $serverKey does not exist, please re-run script"
return $null
}
}
# ================================
# Test SSH Connection
# ================================
function Test-SSHConnection {
param(
[hashtable]$Server
)
Write-Log -Level "INFO" -Message "Testing SSH connection..."
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "echo CONNECTION_OK"
if (($result.ExitCode -ne 0) -or ($result.Output -notmatch "CONNECTION_OK")) {
Write-Log -Level "ERROR" -Message "SSH connection failed!"
Write-Log -Level "ERROR" -Message "Output: $($result.Output)"
Write-Log -Level "ERROR" -Message "Please check: 1) IP address 2) Port 3) Password 4) Network connectivity"
return $false
}
Write-Log -Level "INFO" -Message "SSH connection test passed"
return $true
}
# ================================
# Verify Remote Architecture
# ================================
function Test-RemoteArchitecture {
param(
[hashtable]$Server
)
Write-Log -Level "INFO" -Message "Verifying remote architecture ($($Server.IP))"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "uname -m"
if ($result.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "Cannot get remote architecture, please check network or permissions"
Write-Log -Level "ERROR" -Message "SSH output: $($result.Output)"
return $null
}
$outputLines = $result.Output -split "`n" | Where-Object { $_ -match '\S' }
$arch = ($outputLines | Select-Object -Last 1).Trim()
if ($arch -notmatch $REMOTE_ARCH_ALLOW_REGEX) {
Write-Log -Level "ERROR" -Message "Remote architecture is $arch, not x86, operation terminated"
return $null
}
Write-Log -Level "INFO" -Message "Remote architecture $arch verified"
return $arch
}
# ================================
# Auto-detect Platform Type
# ================================
function Get-PlatformType {
param(
[hashtable]$Server
)
Write-Log -Level "INFO" -Message "Auto-detecting target server platform type..."
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "[ -d /data/services ] && echo 'NEW_PLATFORM' || echo 'OLD_PLATFORM'"
$outputLines = $result.Output -split "`n" | Where-Object { $_ -match '\S' }
$platformCheck = ($outputLines | Select-Object -Last 1).Trim()
if ($platformCheck -eq "NEW_PLATFORM") {
Write-Log -Level "INFO" -Message "[OK] Detected /data/services directory, identified as new unified platform"
return @{
Type = "new"
Flag = "--new-platform"
}
}
elseif ($platformCheck -eq "OLD_PLATFORM") {
Write-Log -Level "INFO" -Message "[OK] No /data/services directory detected, identified as traditional platform"
return @{
Type = "old"
Flag = ""
}
}
else {
Write-Log -Level "WARN" -Message "[WARN] Auto-detection failed, switching to manual confirmation mode"
$platformInput = Read-Host "Is target server new unified platform? (y/n)"
switch ($platformInput.ToLower()) {
"y" {
Write-Log -Level "INFO" -Message "Marked as new unified platform"
return @{
Type = "new"
Flag = "--new-platform"
}
}
"n" {
Write-Log -Level "INFO" -Message "Marked as traditional platform"
return @{
Type = "old"
Flag = ""
}
}
default {
Write-Log -Level "ERROR" -Message "Invalid input, please enter y or n"
return $null
}
}
}
}
# ================================
# Verify Remote Image Version
# ================================
function Test-RemoteImageVersion {
param(
[hashtable]$Server,
[string]$ContainerPrefix,
[string]$PlatformType
)
# Get expected image name based on container type and platform type
if ($ContainerPrefix -eq "upython") {
if ($PlatformType -eq "new") {
$expectedImage = $ContainerDockerImage["upython_new"]
} else {
$expectedImage = $ContainerDockerImage["upython_old"]
}
} else {
$expectedImage = $ContainerDockerImage[$ContainerPrefix]
}
if ([string]::IsNullOrEmpty($expectedImage)) {
Write-Log -Level "WARN" -Message "No image version configured for container $ContainerPrefix, skipping version check"
return $true
}
Write-Log -Level "INFO" -Message "Checking remote server image version..."
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "docker images --format '{{.Repository}}:{{.Tag}}'"
$remoteImages = $result.Output
$escapedImage = [regex]::Escape($expectedImage)
if ($remoteImages -match $escapedImage) {
Write-Log -Level "WARN" -Message "[WARN] Target image already exists on remote server: $expectedImage"
Write-Log -Level "WARN" -Message "[WARN] Target server may have already been updated"
# Check if related container is running
$containerCheckResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "docker ps --format '{{.Names}}' | grep -E '^${ContainerPrefix}([0-9]+)?$' | head -n1"
$runningContainer = $containerCheckResult.Output.Trim()
if (-not [string]::IsNullOrEmpty($runningContainer)) {
# Get image used by running container
$imageCheckResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "docker inspect --format '{{.Config.Image}}' '$runningContainer'"
$containerImage = $imageCheckResult.Output.Trim()
if ($containerImage -eq $expectedImage) {
Write-Log -Level "WARN" -Message "[WARN] Container $runningContainer is using target image $expectedImage"
Write-Log -Level "WARN" -Message "[WARN] Target server container is already latest version, no update needed"
$continueInput = Read-Host "Continue update anyway? (y/n)"
switch ($continueInput.ToLower()) {
"y" {
Write-Log -Level "INFO" -Message "User chose to continue update"
return $true
}
"n" {
Write-Log -Level "INFO" -Message "User chose to skip update"
return $false
}
default {
Write-Log -Level "ERROR" -Message "Invalid input, operation terminated"
return $false
}
}
}
}
$continueInput = Read-Host "Remote already has target image, continue update anyway? (y/n)"
switch ($continueInput.ToLower()) {
"y" {
Write-Log -Level "INFO" -Message "User chose to continue update"
return $true
}
"n" {
Write-Log -Level "INFO" -Message "User chose to skip update"
return $false
}
default {
Write-Log -Level "ERROR" -Message "Invalid input, operation terminated"
return $false
}
}
}
else {
Write-Log -Level "INFO" -Message "[OK] Target image $expectedImage not installed on remote server, continuing update"
return $true
}
}
# ================================
# Sync EMQX Directory
# ================================
function Sync-EmqxAssets {
param(
[hashtable]$Server,
[string]$RemoteTargetDir,
[string]$PlatformLabel
)
Write-Log -Level "INFO" -Message "Preparing to sync EMQX directory ($LOCAL_EMQX_DIR -> $($Server.IP):$RemoteTargetDir)"
# Create remote directory
$null = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "mkdir -p '$RemoteTargetDir'"
# Backup remote directory
$backupCmd = "set -e; TARGET_DIR='$RemoteTargetDir'; if [ -d `"`$TARGET_DIR`" ] && [ -n `"`$(ls -A `$TARGET_DIR 2>/dev/null)`" ]; then ts=`$(date +%Y%m%d_%H%M%S); backup=`"`${TARGET_DIR}_backup_`${ts}`"; cp -r `"`$TARGET_DIR`" `"`$backup`"; echo `$backup; fi"
$backupResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $backupCmd
$backupPath = $backupResult.Output.Trim()
if (-not [string]::IsNullOrEmpty($backupPath)) {
Write-Log -Level "INFO" -Message "Remote directory backed up to $backupPath"
}
else {
Write-Log -Level "INFO" -Message "Remote directory empty or does not exist, skipping backup"
}
# Use pscp to sync directory
Write-Log -Level "INFO" -Message "Using pscp to sync EMQX directory"
$localEmqxPath = Join-Path $SCRIPT_DIR "emqx"
if (Test-Path $localEmqxPath) {
$scpResult = Send-SCPDirectory -LocalPath $localEmqxPath -RemoteHost $Server.IP -RemoteUser $Server.User -RemotePass $Server.Pass -RemotePort $Server.Port -RemotePath $RemoteTargetDir
if ($scpResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "EMQX directory sync failed: $($scpResult.Output)"
return $false
}
}
else {
Write-Log -Level "WARN" -Message "Local EMQX directory does not exist: $localEmqxPath, skipping sync"
}
Write-Log -Level "INFO" -Message "EMQX directory sync completed ($PlatformLabel platform)"
return $true
}
# ================================
# Sync Nginx Directory
# ================================
function Sync-NginxAssets {
param(
[hashtable]$Server,
[string]$RemoteTargetDir,
[string]$PlatformLabel
)
Write-Log -Level "INFO" -Message "Preparing to sync Nginx directory ($LOCAL_NGINX_DIR -> $($Server.IP):$RemoteTargetDir)"
# Create remote directory
$null = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "mkdir -p '$RemoteTargetDir'"
# Backup remote directory
$backupCmd = "set -e; TARGET_DIR='$RemoteTargetDir'; if [ -d `"`$TARGET_DIR`" ] && [ -n `"`$(ls -A `$TARGET_DIR 2>/dev/null)`" ]; then ts=`$(date +%Y%m%d_%H%M%S); backup=`"`${TARGET_DIR}_backup_`${ts}`"; cp -r `"`$TARGET_DIR`" `"`$backup`"; echo `$backup; fi"
$backupResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $backupCmd
$backupPath = $backupResult.Output.Trim()
if (-not [string]::IsNullOrEmpty($backupPath)) {
Write-Log -Level "INFO" -Message "Remote Nginx directory backed up to $backupPath"
}
else {
Write-Log -Level "INFO" -Message "Remote Nginx directory empty or does not exist, skipping backup"
}
# Use pscp to sync directory
Write-Log -Level "INFO" -Message "Using pscp to sync Nginx directory"
$localNginxPath = Join-Path $SCRIPT_DIR "nginx"
if (Test-Path $localNginxPath) {
$scpResult = Send-SCPDirectory -LocalPath $localNginxPath -RemoteHost $Server.IP -RemoteUser $Server.User -RemotePass $Server.Pass -RemotePort $Server.Port -RemotePath $RemoteTargetDir
if ($scpResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "Nginx directory sync failed: $($scpResult.Output)"
return $false
}
}
else {
Write-Log -Level "WARN" -Message "Local Nginx directory does not exist: $localNginxPath, skipping sync"
}
Write-Log -Level "INFO" -Message "Nginx directory sync completed ($PlatformLabel platform)"
return $true
}
# ================================
# Find and Stop Remote Old Container
# ================================
function Stop-RemoteContainer {
param(
[hashtable]$Server,
[string]$ContainerPrefix
)
Write-Log -Level "INFO" -Message "Finding and stopping remote old container"
$result = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "docker ps --format '{{.Names}}' | grep -E '^${ContainerPrefix}([0-9]+)?$' | sort -V | tail -n1"
$currentContainer = $result.Output.Trim()
if (-not [string]::IsNullOrEmpty($currentContainer)) {
Write-Log -Level "INFO" -Message "Detected old container $currentContainer, stopping it"
$null = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "docker stop '$currentContainer'"
return $currentContainer
}
else {
Write-Log -Level "INFO" -Message "No running container matching $ContainerPrefix or ${ContainerPrefix}[number] found"
return $null
}
}
# ================================
# Determine New Container Name (Auto-increment)
# ================================
function Get-NewContainerName {
param(
[string]$CurrentContainer,
[string]$ContainerPrefix
)
if (-not [string]::IsNullOrEmpty($CurrentContainer)) {
# Extract container number
if ($CurrentContainer -match "${ContainerPrefix}(\d+)$") {
$lastNum = [int]$Matches[1]
}
else {
$lastNum = 0
}
$nextNum = $lastNum + 1
}
else {
$nextNum = 1
}
$newContainer = "${ContainerPrefix}${nextNum}"
Write-Log -Level "INFO" -Message "New container name determined: $newContainer"
return $newContainer
}
# ================================
# Clean Remote Image Files and Deployment Script
# ================================
function Clear-RemoteFiles {
param(
[hashtable]$Server,
[string]$RemoteImagePath,
[string]$RemoteTargetDir,
[string]$DeployName
)
Write-Log -Level "INFO" -Message "Starting cleanup of remote image files..."
$cleanupCmd = "set -e; if [ -f '$RemoteImagePath' ]; then rm -f '$RemoteImagePath'; echo 'Deleted image: $RemoteImagePath'; fi; if [ -f '$RemoteTargetDir/$DeployName' ]; then rm -f '$RemoteTargetDir/$DeployName'; echo 'Deleted script: $RemoteTargetDir/$DeployName'; fi; if [ -d '$RemoteTargetDir' ] && [ -z `"`$(ls -A '$RemoteTargetDir')`" ]; then rmdir '$RemoteTargetDir'; echo 'Deleted empty dir: $RemoteTargetDir'; fi"
$cleanupResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cleanupCmd
if ($cleanupResult.ExitCode -eq 0) {
Write-Log -Level "INFO" -Message "[OK] Remote image cleanup completed"
$cleanupResult.Output -split "`n" | Where-Object { $_ -match '\S' } | ForEach-Object {
Write-Log -Level "INFO" -Message " $_"
}
}
else {
Write-Log -Level "WARN" -Message "[WARN] Warning during remote cleanup, but does not affect deployment result"
}
}
# ================================
# Main Function
# ================================
function Main {
Write-Host ""
Write-Host "=================================================================="
Write-Host " Remote Container Update Tool (PowerShell Version)"
Write-Host "=================================================================="
Write-Host ""
# Check dependencies
if (-not (Test-Dependencies)) {
exit 1
}
# Check if deployment script exists
$deployScriptPath = Join-Path $SCRIPT_DIR $DEPLOY_SCRIPT
if (-not (Test-Path $deployScriptPath)) {
Write-Log -Level "ERROR" -Message "Deployment script $DEPLOY_SCRIPT does not exist"
exit 3
}
# Select container
$containerPrefix = Select-Container
if ($null -eq $containerPrefix) {
exit 9
}
# Get image file
$imageFile = $ContainerImage[$containerPrefix]
if ([string]::IsNullOrEmpty($imageFile)) {
Write-Log -Level "ERROR" -Message "Container $containerPrefix has no image mapping configured"
exit 10
}
# Check if image file exists
$localImagePath = Join-Path $SCRIPT_DIR $imageFile
$imageName = $imageFile
if (-not (Test-Path $localImagePath)) {
Write-Log -Level "ERROR" -Message "Image file $localImagePath does not exist, please place it in script directory and retry"
exit 2
}
Write-Log -Level "INFO" -Message "Image file: $localImagePath"
# Select server
$server = Select-Server
if ($null -eq $server) {
exit 7
}
# Test SSH connection
if (-not (Test-SSHConnection -Server $server)) {
exit 4
}
# Verify remote architecture
$arch = Test-RemoteArchitecture -Server $server
if ($null -eq $arch) {
exit 5
}
# Auto-detect platform type
$platform = Get-PlatformType -Server $server
if ($null -eq $platform) {
exit 6
}
# Verify remote image version
if (-not (Test-RemoteImageVersion -Server $server -ContainerPrefix $containerPrefix -PlatformType $platform.Type)) {
Write-Log -Level "INFO" -Message "User cancelled update, process ended"
exit 0
}
# Create remote directory
Write-Log -Level "INFO" -Message "Creating remote directory $RemoteDir"
$null = Invoke-SSHCommand -HostName $server.IP -User $server.User -Pass $server.Pass -Port $server.Port -Command "mkdir -p '$RemoteDir'"
# Transfer image and deployment script to remote directory
Write-Log -Level "INFO" -Message "Transferring image and deployment script to remote directory"
# Transfer image file
$scpImageResult = Send-SCPFile -LocalPath $localImagePath -RemoteHost $server.IP -RemoteUser $server.User -RemotePass $server.Pass -RemotePort $server.Port -RemotePath "$RemoteDir/"
if ($scpImageResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "Image file transfer failed: $($scpImageResult.Output)"
exit 11
}
Write-Log -Level "INFO" -Message "[OK] Image file transfer completed"
# Transfer deployment script
$scpScriptResult = Send-SCPFile -LocalPath $deployScriptPath -RemoteHost $server.IP -RemoteUser $server.User -RemotePass $server.Pass -RemotePort $server.Port -RemotePath "$RemoteDir/"
if ($scpScriptResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "Deployment script transfer failed: $($scpScriptResult.Output)"
exit 12
}
Write-Log -Level "INFO" -Message "[OK] Deployment script transfer completed"
# Find and stop remote old container
$currentContainer = Stop-RemoteContainer -Server $server -ContainerPrefix $containerPrefix
# Sync container-specific files
switch ($containerPrefix) {
"uemqx" {
if ($platform.Type -eq "new") {
$remoteEmqxDir = "/data/middleware/emqx"
} else {
$remoteEmqxDir = "/var/www/emqx"
}
$null = Sync-EmqxAssets -Server $server -RemoteTargetDir $remoteEmqxDir -PlatformLabel $platform.Type
}
"upython" {
Write-Log -Level "INFO" -Message "Python container does not sync files, only deploying container"
}
"unginx" {
if ($platform.Type -eq "new") {
$remoteNginxDir = "/data/middleware/nginx"
$null = Sync-NginxAssets -Server $server -RemoteTargetDir $remoteNginxDir -PlatformLabel $platform.Type
}
else {
Write-Log -Level "ERROR" -Message "Nginx container only supports new unified platform, please re-select"
exit 20
}
}
}
# Determine new container name
$newContainer = Get-NewContainerName -CurrentContainer $currentContainer -ContainerPrefix $containerPrefix
# Determine remote image path
$remoteImagePath = "$RemoteDir/$imageName"
# Build remote execution command
if (-not [string]::IsNullOrEmpty($platform.Flag)) {
$platformTail = " '$($platform.Flag)'"
} else {
$platformTail = ""
}
$remoteCmd = "set -euo pipefail; cd '$RemoteDir'; sed -i 's/\r$//' '$DEPLOY_SCRIPT' 2>/dev/null || true; chmod +x '$DEPLOY_SCRIPT'; ./'$DEPLOY_SCRIPT' '$newContainer' '$remoteImagePath'$platformTail"
# Execute remote deployment script
Write-Log -Level "INFO" -Message "Starting remote deployment script execution"
# Use plink to execute remote command (with TTY)
$plinkArgs = @(
"-ssh",
"-P", $server.Port,
"-l", $server.User,
"-pw", $server.Pass,
"-t",
$server.IP,
$remoteCmd
)
& plink @plinkArgs
if ($LASTEXITCODE -ne 0) {
Write-Log -Level "ERROR" -Message "Remote deployment execution failed"
exit 13
}
Write-Log -Level "INFO" -Message "Remote deployment execution completed"
# Clean remote image files and deployment script
Clear-RemoteFiles -Server $server -RemoteImagePath $remoteImagePath -RemoteTargetDir $RemoteDir -DeployName $DEPLOY_SCRIPT
Write-Log -Level "INFO" -Message "Remote update process ended"
Write-Host ""
Write-Host "=================================================================="
Write-Host " [OK] Remote container update completed!"
Write-Host "=================================================================="
Write-Host ""
}
# Execute main function
Main
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论