0

PowerShell监控CPU内存磁盘自动发邮件:5个实战脚本让服务器告警不再漏报

2026.05.31 | youres | 33次围观

为什么要用PowerShell做服务器监控告警

服务器出问题的时候,最怕的不是问题本身,而是发现了太晚。CPU飙到100%半天没人管,磁盘满了业务直接挂掉,内存泄漏导致服务反复崩溃——这些故障如果能第一时间收到告警邮件,处理起来完全不是事。

市面上监控工具不少,Zabbix、Prometheus、PRTG都很强大,但对于小团队或者个人维护的几台服务器来说,PowerShell脚本+任务计划+邮件告警这套方案最实在:零成本、部署快、不依赖第三方服务。

一、监控CPU使用率并发邮件告警

CPU是最核心的监控指标。下面这个脚本检查CPU使用率,超过阈值就发邮件:

# CPU监控告警脚本 Monitor-CPU.ps1
$Threshold = 85  # CPU使用率阈值(%)
$SmtpServer = "smtp.example.com"
$From = "alert@example.com"
$To = "admin@example.com"

# 获取CPU使用率
$CpuUsage = (Get-WmiObject Win32_Processor | 
    Measure-Object -Property LoadPercentage -Average).Average

if ($CpuUsage -ge $Threshold) {
    $Subject = "⚠ CPU告警:当前使用率 $CpuUsage%"
    $Body = @"
服务器CPU使用率超过阈值!

主机名:$env:COMPUTERNAME
CPU使用率:$CpuUsage%
阈值:$Threshold%
时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

请尽快检查服务器状态。
"@
    Send-MailMessage -From $From -To $To -Subject $Subject `
        -Body $Body -SmtpServer $SmtpServer -Encoding UTF8
    Write-Host "已发送CPU告警邮件,使用率: $CpuUsage%"
} else {
    Write-Host "CPU正常,使用率: $CpuUsage%"
}

要点说明:

  • Win32_ProcessorLoadPercentage取的是查询瞬间的CPU占用,多核取平均值
  • 阈值建议设85%以上再告警,避免频繁误报
  • 如果有多颗CPU,Measure-Object -Average会算整体均值

二、监控内存使用率并发邮件告警

内存不足会导致应用崩溃、系统卡死。内存监控脚本如下:

# 内存监控告警脚本 Monitor-Memory.ps1
$Threshold = 90  # 内存使用率阈值(%)
$SmtpServer = "smtp.example.com"
$From = "alert@example.com"
$To = "admin@example.com"

# 获取内存信息
$OS = Get-WmiObject Win32_OperatingSystem
$TotalMB = [math]::Round($OS.TotalVisibleMemorySize / 1024, 0)
$FreeMB = [math]::Round($OS.FreePhysicalMemory / 1024, 0)
$UsedMB = $TotalMB - $FreeMB
$UsagePercent = [math]::Round(($UsedMB / $TotalMB) * 100, 1)

if ($UsagePercent -ge $Threshold) {
    $Subject = "⚠ 内存告警:当前使用率 $UsagePercent%"
    $Body = @"
服务器内存使用率超过阈值!

主机名:$env:COMPUTERNAME
总内存:${TotalMB}MB
已使用:${UsedMB}MB
剩余:${FreeMB}MB
使用率:$UsagePercent%
阈值:$Threshold%
时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
"@
    Send-MailMessage -From $From -To $To -Subject $Subject `
        -Body $Body -SmtpServer $SmtpServer -Encoding UTF8
    Write-Host "已发送内存告警邮件,使用率: $UsagePercent%"
} else {
    Write-Host "内存正常,使用率: $UsagePercent%"
}

关键点:

  • TotalVisibleMemorySize是操作系统可见的物理内存,不含硬件保留部分
  • 内存阈值建议90%,因为Windows本身会利用空闲内存做缓存
  • 如果需要更精确,可用Win32_PhysicalMemory获取实际安装内存总量

三、监控磁盘空间并发邮件告警

磁盘满了是最常见的故障之一,尤其是日志盘和数据库盘:

# 磁盘监控告警脚本 Monitor-Disk.ps1
$ThresholdPercent = 90  # 磁盘使用率阈值(%)
$ThresholdGB = 10       # 剩余空间阈值(GB)
$SmtpServer = "smtp.example.com"
$From = "alert@example.com"
$To = "admin@example.com"

$Disks = Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3"
$Alerts = @()

foreach ($Disk in $Disks) {
    $TotalGB = [math]::Round($Disk.Size / 1GB, 1)
    $FreeGB = [math]::Round($Disk.FreeSpace / 1GB, 1)
    $UsedGB = [math]::Round(($Disk.Size - $Disk.FreeSpace) / 1GB, 1)
    $UsagePercent = [math]::Round(($Disk.Size - $Disk.FreeSpace) / $Disk.Size * 100, 1)

    if ($UsagePercent -ge $ThresholdPercent -or $FreeGB -le $ThresholdGB) {
        $Alerts += "盘符:$($Disk.DeviceID) | 总量:${TotalGB}GB | 已用:${UsedGB}GB | 剩余:${FreeGB}GB | 使用率:${UsagePercent}%"
    }
}

if ($Alerts.Count -gt 0) {
    $Subject = "⚠ 磁盘告警:$($Alerts.Count)个磁盘空间不足"
    $Body = @"
服务器磁盘空间不足!

主机名:$env:COMPUTERNAME
时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

$($Alerts -join "`n")

请及时清理磁盘空间。
"@
    Send-MailMessage -From $From -To $To -Subject $Subject `
        -Body $Body -SmtpServer $SmtpServer -Encoding UTF8
    Write-Host "已发送磁盘告警邮件"
} else {
    Write-Host "所有磁盘空间正常"
}

双重阈值设计:使用率阈值和剩余空间阈值同时判断。对于小容量磁盘,使用率可能不高但剩余空间已经很少;对于大容量磁盘,剩余空间看着还多但使用率已经很高。两个条件取"或"关系,更安全。

四、三合一综合监控脚本

把CPU、内存、磁盘监控合到一个脚本里,一次运行全面检查:

# 综合监控脚本 Monitor-All.ps1
param(
    [int]$CpuThreshold = 85,
    [int]$MemThreshold = 90,
    [int]$DiskThreshold = 90,
    [int]$DiskFreeGB = 10
)

$SmtpServer = "smtp.example.com"
$From = "alert@example.com"
$To = "admin@example.com"
$Alerts = @()

# CPU检查
$CpuUsage = (Get-WmiObject Win32_Processor | 
    Measure-Object -Property LoadPercentage -Average).Average
if ($CpuUsage -ge $CpuThreshold) {
    $Alerts += "[CPU] 使用率 $CpuUsage%(阈值 ${CpuThreshold}%)"
}

# 内存检查
$OS = Get-WmiObject Win32_OperatingSystem
$TotalMB = [math]::Round($OS.TotalVisibleMemorySize / 1024)
$FreeMB = [math]::Round($OS.FreePhysicalMemory / 1024)
$MemPercent = [math]::Round(($TotalMB - $FreeMB) / $TotalMB * 100, 1)
if ($MemPercent -ge $MemThreshold) {
    $Alerts += "[内存] 使用率 ${MemPercent}%,剩余 ${FreeMB}MB(阈值 ${MemThreshold}%)"
}

# 磁盘检查
$Disks = Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3"
foreach ($Disk in $Disks) {
    $FreeGB = [math]::Round($Disk.FreeSpace / 1GB, 1)
    $DiskPercent = [math]::Round(($Disk.Size - $Disk.FreeSpace) / $Disk.Size * 100, 1)
    if ($DiskPercent -ge $DiskThreshold -or $FreeGB -le $DiskFreeGB) {
        $Alerts += "[磁盘] $($Disk.DeviceID) 使用率 ${DiskPercent}%,剩余 ${FreeGB}GB"
    }
}

# 发送告警
if ($Alerts.Count -gt 0) {
    $Subject = "⚠ 服务器告警:$($Alerts.Count)项异常 - $env:COMPUTERNAME"
    $Body = @"
服务器监控报告

主机名:$env:COMPUTERNAME
时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')

异常项:
$($Alerts | ForEach-Object { "  - $_" } | Out-String)

正常项:
  - CPU使用率:$CpuUsage%
  - 内存使用率:${MemPercent}%
"@
    Send-MailMessage -From $From -To $To -Subject $Subject `
        -Body $Body -SmtpServer $SmtpServer -Encoding UTF8
}
Write-Host "检查完成,异常项: $($Alerts.Count)"

这个脚本支持参数化配置阈值,方便不同服务器设置不同标准。部署到任务计划程序后,每5分钟或15分钟执行一次就行。

五、配置任务计划程序自动运行

脚本写好了,还得让它自动跑。用PowerShell一条命令创建定时任务:

# 创建每15分钟执行一次的监控任务
$Action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Monitor-All.ps1 -CpuThreshold 85 -MemThreshold 90"

$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 15)

$Settings = New-ScheduledTaskSettingsSet `
    -StartWhenAvailable `
    -DontStopOnIdleEnd `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries

Register-ScheduledTask `
    -TaskName "ServerMonitor" `
    -Action $Action `
    -Trigger $Trigger `
    -Settings $Settings `
    -Description "服务器CPU/内存/磁盘监控告警" `
    -RunLevel Highest

几个关键参数:

  • -NoProfile:不加载用户配置文件,加快启动速度
  • -ExecutionPolicy Bypass:绕过执行策略限制
  • -RunLevel Highest:以最高权限运行,确保能读取系统信息
  • -StartWhenAvailable:错过了执行时间,系统空闲后补执行

常见问题

Send-MailMessage发邮件失败怎么办

最常见的原因是SMTP认证问题。Send-MailMessage需要加-Credential参数。如果用的是匿名SMTP,确认SMTP服务器允许匿名中继。也可以考虑用MailKit替代Send-MailMessage,支持OAuth2认证,更安全也更灵活。

WMI查询很慢怎么办

在Windows Server上,Get-WmiObject偶尔会卡。可以换成Get-CimInstance,性能更好,语法基本一样:

# WMI写法
Get-WmiObject Win32_Processor
# CIM写法(推荐)
Get-CimInstance Win32_Processor

如何避免告警风暴

阈值触发后连续发邮件会刷屏。加一个简单的防重复机制:用文件记录上次告警时间,5分钟内不重复发:

$LockFile = "$env:TEMP\monitor-alert.lock"
if (Test-Path $LockFile) {
    $LastAlert = (Get-Item $LockFile).LastWriteTime
    if ((Get-Date) - $LastAlert -lt [TimeSpan]::FromMinutes(5)) {
        Write-Host "5分钟内已发过告警,跳过"
        return
    }
}
# 发送告警后写入锁文件
New-Item -Path $LockFile -Force | Out-Null

相关文章推荐

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论