2026.05.31 | youres | 24次围观
服务器监控告警只靠邮件通知?很多时候运维人员根本看不到邮件,等到用户投诉才发现服务挂了。钉钉机器人 + 邮件双通道告警才是真正靠谱的方案——钉钉即时提醒 + 邮件留存记录,两条路同时走,告警再也不漏。
本文从实战出发,手把手教你用 PowerShell 写一个双通道告警脚本,覆盖钉钉机器人和邮件两种通知方式,附带防重复告警、告警恢复通知、多服务器批量监控等实用功能。
一、先搞清楚钉钉机器人Webhook怎么配
1.1 创建钉钉自定义机器人
打开钉钉群聊,进入群设置,找到智能群助手,添加机器人,选择自定义机器人。关键步骤:
- 安全设置选加签:比IP白名单更灵活,适合服务器IP可能变化的场景
- 记录 Webhook地址 和 加签密钥(Secret),后面脚本要用
- 消息类型选Markdown,方便格式化展示
1.2 加签算法原理
钉钉的加签机制很简单:用当前时间戳加上密钥生成 HMAC-SHA256 签名,拼到请求参数里。PowerShell 里用 [System.Security.Cryptography.HMACSHA256] 就能实现,不需要任何第三方模块。
二、PowerShell 钉钉发送函数
核心代码如下,直接可用:
function Send-DingTalk {
param(
[string]$Webhook,
[string]$Secret,
[string]$Title,
[string]$Content
)
$timestamp = [Math]::Floor(
(Get-Date).ToUniversalTime().Subtract(
(Get-Date '1970-01-01')
).TotalMilliseconds
)
$stringToSign = "$timestamp`n$Secret"
$hmac = [System.Security.Cryptography.HMACSHA256]::new(
[System.Text.Encoding]::UTF8.GetBytes($Secret)
)
$signature = [Convert]::ToBase64String(
$hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($stringToSign))
)
$url = "$Webhook×tamp=$timestamp&sign="
$url += [System.Web.HttpUtility]::UrlEncode($signature)
$body = @{
msgtype = 'markdown'
markdown = @{
title = $Title
text = $Content
}
} | ConvertTo-Json -Depth 5
try {
Invoke-RestMethod -Uri $url -Method POST `
-ContentType 'application/json' `
-Body ([System.Text.Encoding]::UTF8.GetBytes($body))
$true
} catch {
Write-Error "$_"
$false
}
}
关键点:签名生成后必须URL编码,否则请求会被钉钉拒绝。PowerShell 5.1 内置的 [System.Web.HttpUtility]::UrlEncode() 直接可用。
三、邮件发送函数(双通道第二条路)
邮件通知用 System.Net.Mail 实现,比 Send-MailMessage 更稳定可控:
function Send-AlertEmail {
param(
[string]$SmtpServer,
[int]$SmtpPort = 25,
[string]$From,
[string[]]$To,
[string]$Subject,
[string]$Body
)
try {
$mail = New-Object System.Net.Mail.MailMessage(
$From, ($To -join ',')
)
$mail.Subject = $Subject
$mail.Body = $Body
$mail.IsBodyHtml = $true
$mail.Priority = [System.Net.Mail.MailPriority]::High
$smtp = New-Object System.Net.Mail.SmtpClient(
$SmtpServer, $SmtpPort
)
$smtp.Send($mail)
$true
} catch {
Write-Error "$_"
$false
}
}
四、双通道告警主函数(含防重复机制)
把两个函数组合起来,加入防重复告警逻辑:
function Invoke-DualAlert {
param(
[string]$AlertName,
[string]$Message,
[string]$AlertType = 'Warning',
[switch]$Recovery
)
# 60秒内同名告警不重复发送
$cacheFile = "$env:TEMP\alert_cache_$AlertName.txt"
if (-not $Recovery -and (Test-Path $cacheFile)) {
$lastAlert = [datetime](Get-Content $cacheFile)
if (((Get-Date) - $lastAlert).TotalSeconds -lt 60) { return }
}
# 钉钉Markdown消息
$dingContent = "### $AlertName`n"
$dingContent += "**Type**: $AlertType`n"
$dingContent += "**Time**: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n"
$dingContent += "**Detail**:`n$Message"
# HTML邮件
$mailBody = "<h3>$AlertName</h3>"
$mailBody += "<p>Type: $AlertType<br>"
$mailBody += "Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</p>"
$mailBody += "<p>$Message</p>"
# 双通道发送
Send-DingTalk -Webhook $webhookUrl -Secret $secret `
-Title "$AlertName $AlertType" -Content $dingContent
Send-AlertEmail -SmtpServer "smtp.example.com" `
-From "alert@example.com" -To "ops@example.com" `
-Subject "[$AlertType] $AlertName" -Body $mailBody
# 更新缓存
if (-not $Recovery) { Get-Date | Out-File $cacheFile -Encoding utf8 }
else { Remove-Item $cacheFile -ErrorAction SilentlyContinue }
}
五、实战:CPU/内存/磁盘双通道告警
$webhookUrl = "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
$secret = "SECxxxxxxxx"
# CPU检查
$cpu = (Get-CimInstance Win32_Processor).LoadPercentage
if ($cpu -gt 90) {
Invoke-DualAlert -AlertName "CPU High" `
-Message "CPU: $cpu%" -AlertType "Critical"
} elseif ($cpu -gt 80) {
Invoke-DualAlert -AlertName "CPU Warning" `
-Message "CPU: $cpu%" -AlertType "Warning"
}
# 内存检查
$os = Get-CimInstance Win32_OperatingSystem
$memPct = [math]::Round(($os.TotalVisibleMemorySize
- $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize * 100, 1)
if ($memPct -gt 90) {
Invoke-DualAlert -AlertName "Memory High" `
-Message "Memory: $memPct%" -AlertType "Critical"
}
# 磁盘检查
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
$freePct = [math]::Round($_.FreeSpace / $_.Size * 100, 1)
if ($freePct -lt 10) {
Invoke-DualAlert -AlertName "Disk $($_.DeviceId) Low" `
-Message "Free: $freePct%, Avail: $([math]::Round($_.FreeSpace/1GB,1))GB" `
-AlertType "Critical"
}
}
六、多服务器批量告警
用 PowerShell Remoting 批量采集多台服务器数据:
$servers = @("192.168.1.10","192.168.1.11","192.168.1.12")
foreach ($srv in $servers) {
try {
$session = New-PSSession -ComputerName $srv -ErrorAction Stop
$cpu = Invoke-Command -Session $session -ScriptBlock {
(Get-CimInstance Win32_Processor).LoadPercentage
}
if ($cpu -gt 90) {
Invoke-DualAlert -AlertName "${srv}_CPU" `
-Message "CPU: ${cpu}%" -AlertType "Critical"
}
Remove-PSSession $session
} catch {
Invoke-DualAlert -AlertName "${srv}_Offline" `
-Message "Cannot connect" -AlertType "Critical"
}
}
七、配置Windows定时任务
$action = New-ScheduledTaskAction -Execute "pwsh.exe" `
-Argument "-File C:\Scripts\DualAlert.ps1"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes 5)
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
-LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "ServerDualAlert" `
-Action $action -Trigger $trigger `
-Principal $principal
八、5个踩坑经验总结
- 签名时效5分钟:确保服务器NTP同步,时间偏差会导致签名校验失败
- HTTPS和TLS:钉钉Webhook用HTTPS,SMTP用TLS时端口改587,注意证书问题
- 告警风暴:防重复间隔至少60秒,异常持续时避免每5分钟炸群
- WinRM防火墙:多服务器场景需开放5985/5986端口
- 邮件优先级:Critical告警设High优先级,配合邮件客户端过滤规则
总结
双通道告警的核心思路:即时通知靠钉钉,留存记录靠邮件。两者互补,单点故障时另一条路还能送达。上面的脚本可以直接复制使用,根据实际环境修改SMTP配置和钉钉Webhook地址即可。
相关文章推荐
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论