概述
凌晨三点,手机震动。你从被窝爬起来,打开电脑,SSH 上去,发现某个服务 CPU 飙升。Kill 进程,重启服务,12 分钟搞定——但你彻底清醒了。四点半又来一条告警:磁盘使用率超 85%。又爬起来,du -sh 定位,删掉过期日志,15 分钟。
这是无数运维工程师的日常。监控做了,告警配了,脚本也写了——但最后一步还是人在跑。而且偏偏在凌晨。
根据 Google SRE Book 的数据,一个典型的 SRE 团队每天接收 50-100 条告警,其中 80% 是噪音,超过 60% 的告警是重复处理过的已知问题。
告警自动化的目标不是消灭告警,而是把人的判断和操作转化为系统的自动响应。我将从告警降噪、分级路由、Runbook 自动化、自愈平台架构、AI 辅助治理五个维度,详细梳理如何构建告警自动化处理体系。
告警现状:为什么需要自动化
告警风暴的根源
告警风暴通常不是监控配置不足,而是配置泛滥的产物。以下是生产环境中最常见的告警问题模式:
| 问题模式 | 典型表现 | 根因 |
|---|---|---|
| 告警泛滥 | 每天 100+ 条告警,80% 无需人工介入 | 静态阈值过敏感,缺少聚合和去重 |
| 告警疲劳 | 工程师忽略告警通知,真正故障被淹没 | 信号噪声比太低,缺少优先级分级 |
| 重复告警 | 同一问题触发多条告警,不同监控视角 | 缺少告警关联和聚合机制 |
| 响应延迟 | 从告警到人工处理平均 15-30 分钟 | 缺少自动化响应,依赖人工介入 |
| 重复劳动 | 超过 60% 的告警处理流程完全相同 | 没有将已知操作沉淀为自动化 Runbook |
告警生命周期的五个阶段
一个成熟的告警自动化系统应该覆盖告警的完整生命周期:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. 产生 │────▶│ 2. 降噪 │────▶│ 3. 路由 │────▶│ 4. 处理 │────▶│ 5. 复盘 │
│ Detection │ │ Dedup │ │ Route │ │ Remediate │ │ Review │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │ │
监控系统 去重/聚合/抑制 分级/分发/升级 自动Runbook/人工 归档/改进/度量
- 产生:监控系统(Prometheus、Zabbix、Datadog 等)检测到异常,产生原始告警
- 降噪:去重、聚合、抑制,将告警风暴压缩为可管理的事件
- 路由:按严重程度、服务归属、值班表,将告警分发到正确的处理通道
- 处理:自动执行 Runbook 或人工介入修复问题
- 复盘:记录处理过程、度量效果、持续改进告警规则和自动化脚本
告警降噪:从噪声到信号
告警去重与聚合
Alertmanager 是 Prometheus 生态中最常用的告警管理组件,它提供了强大的告警降噪能力。
分组(Grouping)
将相关告警合并为一个通知,避免告警风暴:
# alertmanager.yml — 分组配置
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s # 首次告警后等待 30s,收集同组告警
group_interval: 5m # 同组告警的发送间隔
repeat_interval: 4h # 未解决的告警重复通知间隔
receiver: 'default'
receivers:
- name: 'default'
webhook_configs:
- url: 'http://alert-router:8080/alert'
分组策略的关键在于选择正确的 group_by 维度。以下是不同场景的分组建议:
| 场景 | group_by 配置 | 效果 |
|---|---|---|
| 多实例服务故障 | ['alertname', 'cluster', 'service'] | 同一服务的多实例告警合并 |
| 基础设施级故障 | ['alertname', 'cluster'] | 同一集群的基础设施告警合并 |
| 单节点故障 | ['alertname', 'instance'] | 同一节点的多维度告警合并 |
| 全局性故障 | ['alertname'] | 所有同名告警合并为一条 |
抑制(Inhibition)
当高级别告警触发时,自动抑制低级别告警:
# alertmanager.yml — 抑制规则
inhibit_rules:
# 当节点宕机时,抑制该节点上的所有服务告警
- source_match:
alertname: 'NodeDown'
target_match_re:
alertname: '.*(Service|Pod|Container).*Down|CrashLoopBackOff'
equal: ['instance']
# 当集群不可用时,抑制该集群的所有告警
- source_match:
severity: 'critical'
alertname: 'ClusterUnavailable'
target_match_re:
severity: 'warning|info'
equal: ['cluster']
# 当数据库主节点切换时,抑制从库同步延迟告警
- source_match:
alertname: 'MySQLMasterSwitch'
target_match:
alertname: 'MySQLReplicationLag'
equal: ['cluster', 'service']
告警指纹去重
对于跨多个监控系统的告警,需要在告警路由层实现基于指纹的去重:
#!/usr/bin/env python3
"""告警指纹去重引擎 — 基于告警特征生成唯一指纹,实现跨系统去重"""
import hashlib
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Alert:
"""告警数据结构"""
alertname: str
severity: str
service: str
instance: str
cluster: str
message: str
labels: dict = field(default_factory=dict)
fingerprint: str = ""
first_seen: float = 0
last_seen: float = 0
count: int = 0
def compute_fingerprint(self) -> str:
"""计算告警指纹(基于关键字段,忽略时间戳和动态值)"""
# 只用结构性字段生成指纹,忽略 message 中的动态数值
key_fields = f"{self.alertname}:{self.service}:{self.instance}:{self.cluster}:{self.severity}"
return hashlib.md5(key_fields.encode()).hexdigest()[:16]
class AlertDeduplicator:
"""告警去重器"""
def __init__(self, dedup_window: int = 300):
"""
Args:
dedup_window: 去重窗口(秒),同一指纹在此窗口内只保留一条
"""
self.dedup_window = dedup_window
self.alert_store: dict[str, Alert] = {} # fingerprint -> Alert
def process(self, alert: Alert) -> Optional[Alert]:
"""
处理告警,返回需要发送的告警(去重后)
返回 None 表示该告警是重复的,已被抑制
"""
alert.fingerprint = alert.compute_fingerprint()
now = time.time()
if alert.fingerprint in self.alert_store:
existing = self.alert_store[alert.fingerprint]
existing.last_seen = now
existing.count += 1
# 在去重窗口内,抑制重复告警
if now - existing.first_seen < self.dedup_window:
return None # 抑制
else:
# 超过去重窗口,作为新告警处理
existing.first_seen = now
existing.count = 1
return existing
else:
alert.first_seen = now
alert.last_seen = now
alert.count = 1
self.alert_store[alert.fingerprint] = alert
return alert
def get_active_alerts(self) -> list[Alert]:
"""获取当前活跃的告警列表"""
now = time.time()
return [
a for a in self.alert_store.values()
if now - a.last_seen < self.dedup_window * 2
]
def cleanup(self, max_age: int = 3600):
"""清理过期的告警记录"""
now = time.time()
expired = [
fp for fp, alert in self.alert_store.items()
if now - alert.last_seen > max_age
]
for fp in expired:
del self.alert_store[fp]
if __name__ == "__main__":
dedup = AlertDeduplicator(dedup_window=300)
# 模拟告警风暴
alerts = [
Alert("HighCPU", "warning", "api-gw", "10.0.1.1", "prod", "CPU 92%"),
Alert("HighCPU", "warning", "api-gw", "10.0.1.1", "prod", "CPU 95%"), # 重复
Alert("HighCPU", "warning", "api-gw", "10.0.1.1", "prod", "CPU 98%"), # 重复
Alert("HighMemory", "critical", "api-gw", "10.0.1.1", "prod", "内存 95%"),
Alert("DiskFull", "critical", "db", "10.0.2.1", "prod", "磁盘 85%"),
]
for alert in alerts:
result = dedup.process(alert)
if result:
print(f"[发送] {alert.alertname} | {alert.service} | {alert.instance} | 指纹={alert.fingerprint}")
else:
print(f"[抑制] {alert.alertname} | {alert.service} | {alert.instance} | 重复告警")
print(f"\n活跃告警数: {len(dedup.get_active_alerts())}")
静默管理
在维护窗口期间,临时静默特定告警:
# alertmanager.yml — 静默规则(也可通过 API 动态创建)
# 通过 API 创建静默
# curl -X POST http://alertmanager:9093/api/v2/silences \
# -H "Content-Type: application/json" \
# -d '{
# "matchers": [
# {"name": "service", "value": "payment-service", "isRegex": false}
# ],
# "startsAt": "2026-07-11T02:00:00+08:00",
# "endsAt": "2026-07-11T04:00:00+08:00",
# "createdBy": "ops-team",
# "comment": "数据库维护窗口"
# }'
自动化的静默管理脚本:
#!/bin/bash
# schedule-silence.sh — 自动创建告警静默(用于维护窗口)
ALERTMANAGER="http://alertmanager:9093"
SERVICE="${1:-payment-service}"
DURATION="${2:-120}" # 分钟
START_TIME=$(date -u -d "+1 minute" '+%Y-%m-%dT%H:%M:%S.000Z')
END_TIME=$(date -u -d "+${DURATION} minutes" '+%Y-%m-%dT%H:%M:%S.000Z')
curl -s -X POST "${ALERTMANAGER}/api/v2/silences" \
-H "Content-Type: application/json" \
-d "{
\"matchers\": [
{\"name\": \"service\", \"value\": \"${SERVICE}\", \"isRegex\": false}
],
\"startsAt\": \"${START_TIME}\",
\"endsAt\": \"${END_TIME}\",
\"createdBy\": \"automation\",
\"comment\": \"自动静默: ${SERVICE} 维护 ${DURATION}分钟\"
}"
echo "已为 ${SERVICE} 创建 ${DURATION} 分钟静默窗口"
多维度告警关联
当多个监控系统(Prometheus、ELK、APM)同时产生告警时,需要将它们关联为同一个事件:
#!/usr/bin/env python3
"""多维度告警关联引擎 — 将不同来源的告警关联为同一事件"""
import time
from dataclasses import dataclass, field
from typing import Optional
from collections import defaultdict
@dataclass
class AlertEvent:
"""关联后的告警事件"""
event_id: str
service: str
cluster: str
severity: str # 取最高级别
alerts: list = field(default_factory=list)
first_seen: float = 0
last_seen: float = 0
status: str = "firing" # firing / resolved
def add_alert(self, alert: dict):
self.alerts.append(alert)
self.last_seen = time.time()
severity_order = {"info": 0, "warning": 1, "critical": 2, "fatal": 3}
if severity_order.get(alert.get("severity", "info"), 0) > \
severity_order.get(self.severity, 0):
self.severity = alert["severity"]
class AlertCorrelator:
"""告警关联器 — 基于服务、集群、时间窗口关联告警"""
CORRELATION_WINDOW = 300 # 5分钟窗口
def __init__(self):
self.events: dict[str, AlertEvent] = {} # correlation_key -> AlertEvent
def _correlation_key(self, alert: dict) -> str:
"""生成关联键"""
service = alert.get("labels", {}).get("service", "unknown")
cluster = alert.get("labels", {}).get("cluster", "unknown")
return f"{service}:{cluster}"
def process(self, alert: dict) -> Optional[AlertEvent]:
"""处理告警,返回关联后的事件"""
key = self._correlation_key(alert)
now = time.time()
if key in self.events:
event = self.events[key]
# 检查是否在关联窗口内
if now - event.last_seen <= self.CORRELATION_WINDOW:
event.add_alert(alert)
return event
else:
# 窗口过期,创建新事件
event.status = "resolved"
# 创建新事件
event_id = f"EVT-{int(now)}-{key.replace(':', '-')}"
event = AlertEvent(
event_id=event_id,
service=alert.get("labels", {}).get("service", "unknown"),
cluster=alert.get("labels", {}).get("cluster", "unknown"),
severity=alert.get("severity", "info"),
first_seen=now,
last_seen=now,
)
event.add_alert(alert)
self.events[key] = event
return event
if __name__ == "__main__":
correlator = AlertCorrelator()
# 模拟多源告警
raw_alerts = [
{"alertname": "HighCPU", "severity": "warning",
"labels": {"service": "api-gw", "cluster": "prod"}},
{"alertname": "HighLatency", "severity": "critical",
"labels": {"service": "api-gw", "cluster": "prod"}}, # 关联
{"alertname": "ErrorRateHigh", "severity": "critical",
"labels": {"service": "api-gw", "cluster": "prod"}}, # 关联
{"alertname": "DiskFull", "severity": "critical",
"labels": {"service": "db", "cluster": "prod"}}, # 不同服务
]
for alert in raw_alerts:
event = correlator.process(alert)
print(f"告警: {alert['alertname']:20s} → 事件: {event.event_id} | "
f"严重度: {event.severity:8s} | 关联告警数: {len(event.alerts)}")
告警分级与路由
告警分级标准
建立统一的告警分级标准,是自动化路由的前提:
| 级别 | 定义 | 响应时间 | 示例 | 处理方式 |
|---|---|---|---|---|
| P0 - Critical | 核心服务不可用 | 立即(< 1分钟) | 生产数据库宕机、API 全不可用 | 电话+短信+IM,全员响应 |
| P1 - High | 核心服务降级 | 5 分钟内 | API 错误率 > 5%、P99 延迟翻倍 | 短信+IM,值班工程师处理 |
| P2 - Medium | 非核心服务异常 | 30 分钟内 | 测试环境服务异常、磁盘 > 80% | IM 通知,工作时间处理 |
| P3 - Low | 预警信息 | 工作时间 | 磁盘 > 70%、证书 30 天过期 | 邮件/IM,创建工单跟踪 |
告警路由规则设计
#!/usr/bin/env python3
"""告警路由引擎 — 基于规则的告警分级与分发"""
import re
import json
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
class Severity(Enum):
P0 = "critical"
P1 = "high"
P2 = "medium"
P3 = "low"
@dataclass
class RouteRule:
"""路由规则"""
name: str
match_labels: dict # 匹配的标签
match_severity: list[str] # 匹配的严重度
receivers: list[str] # 接收者列表
escalate_after: int = 0 # 未响应后升级时间(秒)
escalate_to: list[str] = field(default_factory=list) # 升级接收者
auto_remediation: str = "" # 关联的自动修复 Runbook
class AlertRouter:
"""告警路由器"""
def __init__(self):
self.rules: list[RouteRule] = []
self.default_receivers = ["on-call-team"]
def add_rule(self, rule: RouteRule):
self.rules.append(rule)
def route(self, alert: dict) -> dict:
"""路由告警到正确的接收者和处理流程"""
severity = alert.get("severity", "info")
labels = alert.get("labels", {})
for rule in self.rules:
if self._match(alert, rule):
return {
"alert": alert,
"severity": severity,
"receivers": rule.receivers,
"escalate_after": rule.escalate_after,
"escalate_to": rule.escalate_to,
"auto_remediation": rule.auto_remediation,
"routed_at": alert.get("startsAt", ""),
}
return {
"alert": alert,
"severity": severity,
"receivers": self.default_receivers,
"escalate_after": 0,
"escalate_to": [],
"auto_remediation": "",
"routed_at": alert.get("startsAt", ""),
}
def _match(self, alert: dict, rule: RouteRule) -> bool:
"""检查告警是否匹配路由规则"""
severity = alert.get("severity", "info")
labels = alert.get("labels", {})
if rule.match_severity and severity not in rule.match_severity:
return False
for key, value in rule.match_labels.items():
if key not in labels:
return False
if isinstance(value, str) and value.startswith("~"):
pattern = value[1:]
if not re.match(pattern, str(labels.get(key, ""))):
return False
elif str(labels.get(key, "")) != str(value):
return False
return True
if __name__ == "__main__":
router = AlertRouter()
# 定义路由规则
router.add_rule(RouteRule(
name="数据库 P0",
match_labels={"service": "mysql", "cluster": "prod"},
match_severity=[Severity.P0.value],
receivers=["on-call-dba", "on-call-sre", "tech-lead"],
escalate_after=300, # 5 分钟未响应升级
escalate_to=["cto", "vp-engineering"],
auto_remediation="runbook:mysql-failover",
))
router.add_rule(RouteRule(
name="API 服务 P1",
match_labels={"service": "~api-.*", "cluster": "prod"},
match_severity=[Severity.P1.value, Severity.P0.value],
receivers=["on-call-sre"],
escalate_after=600,
escalate_to=["tech-lead"],
auto_remediation="runbook:api-restart",
))
router.add_rule(RouteRule(
name="磁盘空间预警",
match_labels={"alertname": "DiskSpaceWarning"},
match_severity=[Severity.P2.value, Severity.P3.value],
receivers=["ops-team-im"],
escalate_after=0,
auto_remediation="runbook:disk-cleanup",
))
# 测试路由
test_alerts = [
{"alertname": "MySQLDown", "severity": "critical",
"labels": {"service": "mysql", "cluster": "prod"},
"startsAt": "2026-07-11T02:25:59+08:00"},
{"alertname": "HighErrorRate", "severity": "high",
"labels": {"service": "api-gateway", "cluster": "prod"},
"startsAt": "2026-07-11T02:25:59+08:00"},
{"alertname": "DiskSpaceWarning", "severity": "medium",
"labels": {"service": "web", "cluster": "prod", "instance": "10.0.1.5"},
"startsAt": "2026-07-11T02:25:59+08:00"},
]
for alert in test_alerts:
result = router.route(alert)
print(f"\n告警: {alert['alertname']} ({alert['severity']})")
print(f" 接收者: {result['receivers']}")
print(f" 自动修复: {result['auto_remediation']}")
print(f" 升级策略: {result['escalate_after']}s → {result['escalate_to']}")
升级机制
当告警在指定时间内未被响应时,自动升级到更高级别:
#!/usr/bin/env python3
"""告警升级管理器 — 跟踪告警响应状态,超时自动升级"""
import time
import threading
from dataclasses import dataclass, field
@dataclass
class EscalationPolicy:
"""升级策略"""
initial_receivers: list[str]
escalate_after: int # 秒
escalate_to: list[str] # 升级后的接收者
max_escalations: int = 3 # 最大升级次数
auto_resolve_on_action: bool = True # 有人响应后停止升级
@dataclass
class AlertEscalation:
"""告警升级跟踪"""
alert_id: str
policy: EscalationPolicy
created_at: float
acknowledged: bool = False
current_level: int = 0
escalation_history: list = field(default_factory=list)
def acknowledge(self, user: str):
self.acknowledged = True
self.escalation_history.append({
"action": "acknowledged",
"user": user,
"timestamp": time.time(),
})
def escalate(self) -> list[str]:
"""执行升级,返回新的接收者列表"""
if self.acknowledged:
return []
self.current_level += 1
if self.current_level > self.policy.max_escalations:
return ["final-escalation", "cto"]
receivers = self.policy.escalate_to[:self.current_level]
self.escalation_history.append({
"action": "escalated",
"level": self.current_level,
"receivers": receivers,
"timestamp": time.time(),
})
return receivers
class EscalationManager:
"""告警升级管理器"""
def __init__(self):
self.tracked: dict[str, AlertEscalation] = {}
self._timer: threading.Timer = None
def track(self, alert_id: str, policy: EscalationPolicy):
"""开始跟踪告警的升级"""
escalation = AlertEscalation(
alert_id=alert_id,
policy=policy,
created_at=time.time(),
)
self.tracked[alert_id] = escalation
# 设置升级定时器
timer = threading.Timer(
policy.escalate_after,
self._check_escalation,
args=[alert_id]
)
timer.daemon = True
timer.start()
def _check_escalation(self, alert_id: str):
"""检查是否需要升级"""
if alert_id not in self.tracked:
return
escalation = self.tracked[alert_id]
if escalation.acknowledged:
return
new_receivers = escalation.escalate()
if new_receivers:
print(f"[升级] 告警 {alert_id} → 级别 {escalation.current_level}")
print(f" 新接收者: {new_receivers}")
# 这里触发实际的通知发送
def acknowledge(self, alert_id: str, user: str):
"""确认告警"""
if alert_id in self.tracked:
self.tracked[alert_id].acknowledge(user)
print(f"[确认] 告警 {alert_id} 已被 {user} 确认")
if __name__ == "__main__":
manager = EscalationManager()
policy = EscalationPolicy(
initial_receivers=["on-call-sre"],
escalate_after=5, # 5秒后升级(演示用,实际应为 300-600 秒)
escalate_to=["tech-lead", "vp-engineering", "cto"],
max_escalations=3,
)
manager.track("EVT-001", policy)
print("告警已跟踪,等待响应...")
print("(未确认,5秒后自动升级)")
time.sleep(8) # 等待升级触发
# 模拟确认
# manager.acknowledge("EVT-001", "张三")
Runbook 自动化
Runbook 注册表设计
Runbook 是告警自动化的核心——将已知的故障处理流程编码为可自动执行的脚本:
#!/usr/bin/env python3
"""Runbook 注册表 — 管理和执行自动化修复脚本"""
import subprocess
import logging
import time
import json
import os
from dataclasses import dataclass, field
from typing import Optional, Callable
from enum import Enum
logger = logging.getLogger(__name__)
class RunbookStatus(Enum):
SUCCESS = "success"
FAILED = "failed"
PARTIAL = "partial"
SKIPPED = "skipped"
class RunbookRisk(Enum):
SAFE = "safe" # 安全操作,可自动执行
CAUTION = "caution" # 需谨慎,建议人工确认后执行
DANGEROUS = "dangerous" # 高风险,必须人工确认
@dataclass
class RunbookResult:
"""Runbook 执行结果"""
runbook_name: str
status: RunbookStatus
message: str
execution_time: float
output: str = ""
actions_taken: list = field(default_factory=list)
@dataclass
class Runbook:
"""Runbook 定义"""
name: str
description: str
risk_level: RunbookRisk
match_conditions: dict # 匹配的告警条件
handler: Callable # 处理函数
max_retries: int = 1 # 最大重试次数
timeout: int = 60 # 超时时间(秒)
cooldown: int = 300 # 冷却时间(秒),同一 Runbook 的执行间隔
last_executed: float = 0 # 上次执行时间
def matches(self, alert: dict) -> bool:
"""检查告警是否匹配此 Runbook"""
labels = alert.get("labels", {})
for key, value in self.match_conditions.items():
if key not in labels:
return False
if str(labels[key]) != str(value):
return False
return True
def can_execute(self) -> bool:
"""检查是否可以执行(冷却时间检查)"""
if self.last_executed == 0:
return True
return time.time() - self.last_executed >= self.cooldown
def execute(self, alert: dict) -> RunbookResult:
"""执行 Runbook"""
if not self.can_execute():
return RunbookResult(
runbook_name=self.name,
status=RunbookStatus.SKIPPED,
message=f"Runbook 处于冷却期({self.cooldown}s)",
execution_time=0,
)
self.last_executed = time.time()
start = time.time()
for attempt in range(self.max_retries + 1):
try:
result = self.handler(alert)
elapsed = time.time() - start
if result.get("success", False):
return RunbookResult(
runbook_name=self.name,
status=RunbookStatus.SUCCESS,
message=result.get("message", "执行成功"),
execution_time=elapsed,
output=result.get("output", ""),
actions_taken=result.get("actions", []),
)
else:
if attempt < self.max_retries:
logger.warning(f"Runbook {self.name} 第 {attempt+1} 次执行失败,重试中...")
time.sleep(2 ** attempt) # 指数退避
else:
return RunbookResult(
runbook_name=self.name,
status=RunbookStatus.FAILED,
message=result.get("message", "执行失败"),
execution_time=elapsed,
output=result.get("output", ""),
actions_taken=result.get("actions", []),
)
except Exception as e:
elapsed = time.time() - start
logger.error(f"Runbook {self.name} 执行异常: {e}")
if attempt >= self.max_retries:
return RunbookResult(
runbook_name=self.name,
status=RunbookStatus.FAILED,
message=f"执行异常: {str(e)}",
execution_time=elapsed,
)
time.sleep(2 ** attempt)
return RunbookResult(
runbook_name=self.name,
status=RunbookStatus.FAILED,
message="重试次数耗尽",
execution_time=time.time() - start,
)
class RunbookRegistry:
"""Runbook 注册表"""
def __init__(self):
self.runbooks: list[Runbook] = []
self.execution_history: list[dict] = []
def register(self, runbook: Runbook):
"""注册 Runbook"""
self.runbooks.append(runbook)
logger.info(f"已注册 Runbook: {runbook.name} (风险: {runbook.risk_level.value})")
def find_and_execute(self, alert: dict, auto_execute: bool = True) -> Optional[RunbookResult]:
"""查找匹配的 Runbook 并执行"""
for runbook in self.runbooks:
if runbook.matches(alert):
if not auto_execute and runbook.risk_level != RunbookRisk.SAFE:
logger.info(f"Runbook {runbook.name} 风险等级 {runbook.risk_level.value},需人工确认")
return RunbookResult(
runbook_name=runbook.name,
status=RunbookStatus.SKIPPED,
message=f"风险等级 {runbook.risk_level.value},等待人工确认",
execution_time=0,
)
logger.info(f"执行 Runbook: {runbook.name}")
result = runbook.execute(alert)
# 记录执行历史
self.execution_history.append({
"timestamp": time.time(),
"runbook": runbook.name,
"alert": alert.get("alertname", ""),
"status": result.status.value,
"message": result.message,
"execution_time": result.execution_time,
"actions": result.actions_taken,
})
return result
return None
# ====== 六大自动修复场景 ======
def handle_high_cpu(alert: dict) -> dict:
"""场景1:CPU 使用率过高 — 重启进程或扩容"""
instance = alert.get("labels", {}).get("instance", "")
service = alert.get("labels", {}).get("service", "")
actions = []
# 步骤1:采集诊断信息
actions.append(f"采集 {instance} 的 top 进程信息")
top_output = subprocess.run(
["ssh", instance, "top", "-b", "-n", "1"],
capture_output=True, text=True, timeout=10
).stdout
# 步骤2:识别高 CPU 进程
actions.append("分析高 CPU 进程")
if "runaway_worker" in top_output:
# 步骤3:重启服务
actions.append(f"重启服务 {service} on {instance}")
subprocess.run(
["ssh", instance, "systemctl", "restart", service],
timeout=30
)
return {"success": True, "message": f"已重启 {service} on {instance}", "actions": actions}
return {"success": False, "message": "未识别到异常进程,需人工排查", "actions": actions}
def handle_disk_full(alert: dict) -> dict:
"""场景2:磁盘空间不足 — 清理过期日志和临时文件"""
instance = alert.get("labels", {}).get("instance", "")
partition = alert.get("labels", {}).get("partition", "/")
actions = []
# 查找大文件
actions.append(f"扫描 {instance}:{partition} 的大文件")
du_output = subprocess.run(
["ssh", instance, "du", "-sh", f"{partition}/*"],
capture_output=True, text=True, timeout=30
).stdout
# 清理策略
cleanup_dirs = ["/var/log", "/tmp", "/var/cache/apt/archives"]
total_cleaned = 0
for dir_path in cleanup_dirs:
if dir_path.startswith(partition):
actions.append(f"清理 {dir_path} 中 7 天前的文件")
result = subprocess.run(
["ssh", instance, "find", dir_path, "-type", "f", "-mtime", "+7", "-delete"],
capture_output=True, text=True, timeout=60
)
actions.append(f"已清理 {dir_path}")
# 清理 Docker 无用镜像
actions.append("清理 Docker 无用镜像和容器")
subprocess.run(
["ssh", instance, "docker", "system", "prune", "-f"],
capture_output=True, text=True, timeout=60
)
# 验证磁盘空间
df_output = subprocess.run(
["ssh", instance, "df", "-h", partition],
capture_output=True, text=True, timeout=10
).stdout
return {
"success": True,
"message": f"磁盘清理完成\n{df_output}",
"actions": actions,
}
def handle_memory_oom(alert: dict) -> dict:
"""场景3:内存 OOM — 重启 OOM 进程并调整限制"""
instance = alert.get("labels", {}).get("instance", "")
service = alert.get("labels", {}).get("service", "")
actions = []
# 检查 OOM 记录
actions.append("检查 dmesg 中的 OOM 记录")
oom_log = subprocess.run(
["ssh", instance, "dmesg", "-T", "|", "grep", "-i", "oom"],
capture_output=True, text=True, timeout=10, shell=True
).stdout
if "Out of memory" in oom_log or "Killed process" in oom_log:
actions.append(f"检测到 OOM Kill,重启服务 {service}")
subprocess.run(["ssh", instance, "systemctl", "restart", service], timeout=30)
actions.append("增加 cgroup 内存限制")
return {"success": True, "message": f"已处理 OOM 并重启 {service}", "actions": actions}
return {"success": False, "message": "未检测到 OOM Kill 记录", "actions": actions}
def handle_cert_expiry(alert: dict) -> dict:
"""场景4:证书即将过期 — 自动续期"""
instance = alert.get("labels", {}).get("instance", "")
domain = alert.get("labels", {}).get("domain", "")
actions = [f"为 {domain} 执行证书续期"]
# 使用 certbot 续期
result = subprocess.run(
["ssh", instance, "certbot", "renew", "--quiet", "--deploy-hook",
"systemctl reload nginx"],
capture_output=True, text=True, timeout=120
)
if result.returncode == 0:
actions.append("证书续期成功,已重新加载 nginx")
return {"success": True, "message": f"证书 {domain} 已自动续期", "actions": actions}
else:
return {"success": False, "message": f"证书续期失败: {result.stderr}", "actions": actions}
def handle_service_down(alert: dict) -> dict:
"""场景5:服务不可用 — 尝试重启并检查健康"""
instance = alert.get("labels", {}).get("instance", "")
service = alert.get("labels", {}).get("service", "")
actions = []
# 尝试重启
actions.append(f"重启服务 {service} on {instance}")
subprocess.run(["ssh", instance, "systemctl", "restart", service], timeout=30)
# 等待并检查健康
time.sleep(10)
health_check = subprocess.run(
["ssh", instance, "systemctl", "is-active", service],
capture_output=True, text=True, timeout=10
)
if health_check.stdout.strip() == "active":
actions.append("服务已恢复")
return {"success": True, "message": f"{service} 已重启并恢复", "actions": actions}
else:
actions.append("服务重启后仍未恢复")
return {"success": False, "message": f"{service} 重启后仍未恢复,需人工介入",
"actions": actions}
def handle_disk_io_high(alert: dict) -> dict:
"""场景6:磁盘 IO 过高 — 识别并限制 IO"""
instance = alert.get("labels", {}).get("instance", "")
actions = []
# 识别高 IO 进程
actions.append(f"识别 {instance} 上的高 IO 进程")
iotop_output = subprocess.run(
["ssh", instance, "iotop", "-b", "-n", "1", "-o"],
capture_output=True, text=True, timeout=10
).stdout
# 使用 cgroup v2 限制 IO
actions.append("对高 IO 进程应用 cgroup 限制")
# 这里简化处理,实际场景需要解析 iotop 输出获取 PID
return {"success": True, "message": "已识别并限制高 IO 进程", "actions": actions}
if __name__ == "__main__":
registry = RunbookRegistry()
# 注册 Runbook
registry.register(Runbook(
name="auto-cpu-restart",
description="CPU 过高时自动重启服务",
risk_level=RunbookRisk.CAUTION,
match_conditions={"alertname": "HighCPU"},
handler=handle_high_cpu,
max_retries=1,
timeout=60,
cooldown=300,
))
registry.register(Runbook(
name="auto-disk-cleanup",
description="磁盘空间不足时自动清理",
risk_level=RunbookRisk.SAFE,
match_conditions={"alertname": "DiskSpaceWarning"},
handler=handle_disk_full,
max_retries=1,
timeout=120,
cooldown=600,
))
registry.register(Runbook(
name="auto-oom-restart",
description="OOM 时自动重启并调整内存限制",
risk_level=RunbookRisk.CAUTION,
match_conditions={"alertname": "OOMKilled"},
handler=handle_memory_oom,
max_retries=1,
timeout=30,
cooldown=300,
))
registry.register(Runbook(
name="auto-cert-renew",
description="证书即将过期时自动续期",
risk_level=RunbookRisk.SAFE,
match_conditions={"alertname": "CertExpiringSoon"},
handler=handle_cert_expiry,
max_retries=1,
timeout=120,
cooldown=3600,
))
registry.register(Runbook(
name="auto-service-restart",
description="服务不可用时自动重启",
risk_level=RunbookRisk.CAUTION,
match_conditions={"alertname": "ServiceDown"},
handler=handle_service_down,
max_retries=2,
timeout=30,
cooldown=300,
))
registry.register(Runbook(
name="auto-io-throttle",
description="IO 过高时自动限制",
risk_level=RunbookRisk.SAFE,
match_conditions={"alertname": "HighDiskIO"},
handler=handle_disk_io_high,
max_retries=1,
timeout=30,
cooldown=300,
))
# 模拟告警触发 Runbook
test_alert = {
"alertname": "DiskSpaceWarning",
"severity": "medium",
"labels": {
"instance": "10.0.1.5",
"service": "web-server",
"partition": "/",
},
}
print(f"收到告警: {test_alert['alertname']}")
result = registry.find_and_execute(test_alert, auto_execute=True)
if result:
print(f"Runbook: {result.runbook_name}")
print(f"状态: {result.status.value}")
print(f"消息: {result.message}")
print(f"执行时间: {result.execution_time:.2f}s")
print(f"执行动作: {result.actions_taken}")
安全机制:白名单与限流
自动修复必须是安全的。以下安全机制确保自动化不会造成更大故障:
#!/usr/bin/env python3
"""自动化安全防护层 — 白名单、限流、熔断"""
import time
from collections import defaultdict
from dataclasses import dataclass, field
from threading import Lock
@dataclass
class CircuitBreaker:
"""熔断器 — 连续失败时熔断自动修复"""
failure_threshold: int = 3 # 连续失败阈值
recovery_timeout: int = 600 # 熔断恢复时间(秒)
failure_count: int = 0
last_failure_time: float = 0
state: str = "closed" # closed / open / half-open
lock: Lock = field(default_factory=Lock)
def can_execute(self) -> bool:
with self.lock:
if self.state == "open":
# 检查是否可以进入 half-open
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "half-open"
return True
return False
return True
def record_success(self):
with self.lock:
self.failure_count = 0
self.state = "closed"
def record_failure(self):
with self.lock:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
print(f"[熔断] 连续失败 {self.failure_count} 次,熔断 {self.recovery_timeout}s")
class RateLimiter:
"""限流器 — 限制同一 Runbook 的执行频率"""
def __init__(self):
self.executions: dict[str, list[float]] = defaultdict(list)
self.lock = Lock()
def can_execute(self, runbook_name: str, max_per_hour: int = 5) -> bool:
"""检查是否允许执行(每小时最多 max_per_hour 次)"""
with self.lock:
now = time.time()
# 清理1小时前的记录
self.executions[runbook_name] = [
t for t in self.executions[runbook_name]
if now - t < 3600
]
if len(self.executions[runbook_name]) >= max_per_hour:
return False
self.executions[runbook_name].append(now)
return True
class SafeExecutor:
"""安全执行器 — 集成白名单、限流、熔断"""
def __init__(self):
self.circuit_breakers: dict[str, CircuitBreaker] = defaultdict(CircuitBreaker)
self.rate_limiter = RateLimiter()
# 白名单:只允许对特定服务执行自动修复
self.service_whitelist = {
"nginx", "redis", "web-server", "api-gateway",
"log-collector", "metric-exporter",
}
# 黑名单:永远不自动修复的服务
self.service_blacklist = {
"mysql-master", "postgresql-primary", "etcd",
"consul", "zookeeper",
}
def can_execute(self, runbook_name: str, alert: dict) -> tuple[bool, str]:
"""检查是否可以安全执行"""
labels = alert.get("labels", {})
service = labels.get("service", "")
# 检查黑名单
if service in self.service_blacklist:
return False, f"服务 {service} 在黑名单中,禁止自动修复"
# 检查白名单
if service and service not in self.service_whitelist:
return False, f"服务 {service} 不在白名单中,需人工确认"
# 检查熔断器
cb = self.circuit_breakers[runbook_name]
if not cb.can_execute():
return False, f"Runbook {runbook_name} 已熔断,等待恢复"
# 检查限流
if not self.rate_limiter.can_execute(runbook_name, max_per_hour=5):
return False, f"Runbook {runbook_name} 已达每小时执行上限"
return True, "允许执行"
def record_result(self, runbook_name: str, success: bool):
"""记录执行结果"""
cb = self.circuit_breakers[runbook_name]
if success:
cb.record_success()
else:
cb.record_failure()
if __name__ == "__main__":
executor = SafeExecutor()
# 测试白名单检查
alerts = [
{"alertname": "HighCPU", "labels": {"service": "nginx"}},
{"alertname": "HighCPU", "labels": {"service": "mysql-master"}},
{"alertname": "HighCPU", "labels": {"service": "unknown-service"}},
]
for alert in alerts:
can_run, reason = executor.can_execute("auto-cpu-restart", alert)
status = "允许" if can_run else "拒绝"
print(f"[{status}] {alert['labels']['service']:20s} | {reason}")
自愈平台架构设计
整体架构
┌──────────────────────────────────────────────────────────────────────────┐
│ 自愈平台架构 │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Prometheus │ │ ELK Stack │ │ APM (Jaeger)│ │ Zabbix │ │
│ │ (Metrics) │ │ (Logs) │ │ (Traces) │ │ (Infra) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 告警接入层 (Alert Ingestion) │ │
│ │ Webhook Receiver | API Gateway | Message Queue │ │
│ └──────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 告警处理层 (Alert Processing) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ 去重引擎 │ │ 关联引擎 │ │ 抑制引擎 │ │ 静默管理 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └──────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 路由决策层 (Routing & Dispatch) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ 分级引擎 │ │ 路由规则 │ │ 升级管理 │ │ 通知分发 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └──────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
│ │ 自动修复层 │ │ 人工通知层 │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────────────┐ │ │
│ │ │ Runbook Registry │ │ │ │ PagerDuty / 飞书 / 短信 │ │ │
│ │ │ (匹配+执行) │ │ │ └──────────────────────────┘ │ │
│ │ └────────┬─────────┘ │ └──────────────────────────────────┘ │
│ │ │ │ │
│ │ ┌────────▼─────────┐ │ │
│ │ │ Safe Executor │ │ (白名单+限流+熔断) │
│ │ └────────┬─────────┘ │ │
│ │ │ │ │
│ │ ┌────────▼─────────┐ │ │
│ │ │ Action Executor │ │ (SSH/API/K8s/Cloud) │
│ │ └────────┬─────────┘ │ │
│ └───────────┼──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 验证与反馈层 (Verify & Feedback) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ 健康检查 │ │ 效果验证 │ │ 失败回滚 │ │ 事件归档 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
核心服务实现
以下是自愈平台的核心服务骨架,用 Go 语言实现(适合运维团队部署):
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"time"
)
// Alert 告警结构
type Alert struct {
ID string `json:"id"`
AlertName string `json:"alertname"`
Severity string `json:"severity"`
Labels map[string]string `json:"labels"`
Message string `json:"message"`
StartsAt time.Time `json:"startsAt"`
}
// RemediationAction 修复动作
type RemediationAction struct {
Name string `json:"name"`
Type string `json:"type"` // ssh, api, k8s, cloud
Params map[string]interface{} `json:"params"`
Result string `json:"result"`
ExecutedAt time.Time `json:"executedAt"`
}
// RemediationResult 修复结果
type RemediationResult struct {
AlertID string `json:"alertId"`
Success bool `json:"success"`
Message string `json:"message"`
Actions []RemediationAction `json:"actions"`
Duration float64 `json:"duration"`
Timestamp time.Time `json:"timestamp"`
}
// AutoRemediationPlatform 自愈平台核心
type AutoRemediationPlatform struct {
mu sync.RWMutex
deduplicator *Deduplicator
correlator *Correlator
router *Router
runbookReg *RunbookRegistry
safeExecutor *SafeExecutor
verifier *HealthVerifier
eventStore []RemediationResult
}
// HandleAlert 处理告警的完整流程
func (p *AutoRemediationPlatform) HandleAlert(alert Alert) (*RemediationResult, error) {
start := time.Now()
// 1. 去重检查
if p.deduplicator.IsDuplicate(alert) {
return &RemediationResult{
AlertID: alert.ID,
Success: true,
Message: "告警已去重,无需处理",
Timestamp: time.Now(),
}, nil
}
// 2. 关联检查
event := p.correlator.Correlate(alert)
// 3. 路由决策
route := p.router.Route(alert)
// 4. 尝试自动修复
var result *RemediationResult
if route.AutoRemediation != "" {
canExecute, reason := p.safeExecutor.Check(route.AutoRemediation, alert)
if canExecute {
result = p.runbookReg.Execute(route.AutoRemediation, alert)
p.safeExecutor.RecordResult(route.AutoRemediation, result.Success)
// 5. 验证修复效果
if result.Success {
verified := p.verifier.Verify(alert)
if !verified {
result.Success = false
result.Message = "修复执行成功但验证未通过,需人工检查"
}
}
} else {
log.Printf("[安全拦截] Runbook %s 被拦截: %s", route.AutoRemediation, reason)
}
}
// 6. 如果自动修复失败或未配置,发送人工通知
if result == nil || !result.Success {
p.notifyHumans(route, alert)
}
// 7. 归档
result.Duration = time.Since(start).Seconds()
result.Timestamp = time.Now()
p.storeEvent(*result)
return result, nil
}
func (p *AutoRemediationPlatform) notifyHumans(route *RouteResult, alert Alert) {
log.Printf("[人工通知] 告警 %s → %v (升级: %ds → %v)",
alert.AlertName, route.Receivers, route.EscalateAfter, route.EscalateTo)
}
func (p *AutoRemediationPlatform) storeEvent(result RemediationResult) {
p.mu.Lock()
defer p.mu.Unlock()
p.eventStore = append(p.eventStore, result)
}
// WebhookHandler Alertmanager webhook 接收器
func (p *AutoRemediationPlatform) WebhookHandler(w http.ResponseWriter, r *http.Request) {
var payload struct {
Alerts []Alert `json:"alerts"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
for _, alert := range payload.Alerts {
go func(a Alert) {
result, err := p.HandleAlert(a)
if err != nil {
log.Printf("[错误] 处理告警 %s 失败: %v", a.AlertName, err)
} else {
log.Printf("[完成] 告警 %s → %s (%.2fs)",
a.AlertName, result.Message, result.Duration)
}
}(alert)
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "received"})
}
func main() {
platform := &AutoRemediationPlatform{
deduplicator: NewDeduplicator(300),
correlator: NewCorrelator(),
router: NewRouter(),
runbookReg: NewRunbookRegistry(),
safeExecutor: NewSafeExecutor(),
verifier: NewHealthVerifier(),
}
http.HandleFunc("/api/v1/alerts", platform.WebhookHandler)
// 健康检查
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "OK")
})
// 统计接口
http.HandleFunc("/api/v1/stats", func(w http.ResponseWriter, r *http.Request) {
platform.mu.RLock()
defer platform.mu.RUnlock()
total := len(platform.eventStore)
success := 0
for _, e := range platform.eventStore {
if e.Success {
success++
}
}
json.NewEncoder(w).Encode(map[string]interface{}{
"total_events": total,
"success_count": success,
"success_rate": func() float64 {
if total == 0 {
return 0
}
return float64(success) / float64(total) * 100
}(),
})
})
log.Println("自愈平台启动,监听 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
// 以下是各组件的简化实现(实际项目中应有完整实现)
type Deduplicator struct{ window int }
type Correlator struct{}
type Router struct{}
type RouteResult struct {
Receivers []string
EscalateAfter int
EscalateTo []string
AutoRemediation string
}
type RunbookRegistry struct{}
type SafeExecutor struct{}
type HealthVerifier struct{}
func NewDeduplicator(w int) *Deduplicator { return &Deduplicator{window: w} }
func (d *Deduplicator) IsDuplicate(a Alert) bool { return false }
func NewCorrelator() *Correlator { return &Correlator{} }
func (c *Correlator) Correlate(a Alert) interface{} { return nil }
func NewRouter() *Router { return &Router{} }
func (r *Router) Route(a Alert) *RouteResult {
return &RouteResult{Receivers: []string{"on-call"}, AutoRemediation: ""}
}
func NewRunbookRegistry() *RunbookRegistry { return &RunbookRegistry{} }
func (r *RunbookRegistry) Execute(name string, a Alert) *RemediationResult {
return &RemediationResult{Success: false, Message: "no runbook matched"}
}
func NewSafeExecutor() *SafeExecutor { return &SafeExecutor{} }
func (s *SafeExecutor) Check(name string, a Alert) (bool, string) { return false, "safe check" }
func (s *SafeExecutor) RecordResult(name string, success bool) {}
func NewHealthVerifier() *HealthVerifier { return &HealthVerifier{} }
func (h *HealthVerifier) Verify(a Alert) bool { return true }
Docker 一键部署
# docker-compose.yml — 自愈平台部署
version: '3.8'
services:
remediation-platform:
build: .
container_name: auto-remediation
ports:
- "8080:8080"
volumes:
- ./config:/app/config
- ./runbooks:/app/runbooks
- ./logs:/app/logs
environment:
- LOG_LEVEL=info
- ALERTMANAGER_URL=http://alertmanager:9093
- PROMETHEUS_URL=http://prometheus:9090
- GRAFANA_URL=http://grafana:3000
networks:
- monitoring
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
ports:
- "9093:9093"
volumes:
- ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
networks:
- monitoring
restart: unless-stopped
networks:
monitoring:
driver: bridge
AI 辅助告警治理
从规则到智能
传统的告警自动化基于静态规则(if-then),而 AI 辅助告警治理可以引入动态学习和自适应能力:
| 能力维度 | 规则驱动 | AI 辅助 |
|---|---|---|
| 异常检测 | 静态阈值 | 动态基线 + 异常检测算法 |
| 告警分类 | 人工定义规则 | 自动分类 + 语义理解 |
| 根因分析 | 预定义关联规则 | 因果推理 + 拓扑分析 |
| 修复建议 | 固定 Runbook | 基于上下文生成修复策略 |
| 告警优化 | 人工调参 | 自动调优 + 反馈学习 |
基于 LLM 的告警诊断
#!/usr/bin/env python3
"""AI 告警诊断引擎 — 利用 LLM 理解告警上下文并生成修复建议"""
import json
import requests
from dataclasses import dataclass
@dataclass
class DiagnosticResult:
"""诊断结果"""
root_cause: str # 根因分析
severity_assessment: str # 严重度评估
impact_analysis: str # 影响面分析
remediation_plan: str # 修复建议
confidence: float # 置信度
class AlertDiagnosticEngine:
"""告警诊断引擎"""
def __init__(self, llm_api_url: str, llm_api_key: str):
self.llm_api_url = llm_api_url
self.llm_api_key = llm_api_key
def diagnose(self, alert: dict, context: dict) -> DiagnosticResult:
"""
告警诊断
Args:
alert: 告警信息
context: 上下文信息(监控指标、日志、拓扑等)
"""
prompt = self._build_prompt(alert, context)
# 调用 LLM 进行诊断
response = self._call_llm(prompt)
return self._parse_response(response)
def _build_prompt(self, alert: dict, context: dict) -> str:
"""构建诊断 prompt"""
return f"""你是一位资深 SRE 工程师,请分析以下告警并给出诊断。
## 告警信息
- 名称: {alert.get('alertname', '')}
- 严重度: {alert.get('severity', '')}
- 服务: {alert.get('labels', {}).get('service', '')}
- 实例: {alert.get('labels', {}).get('instance', '')}
- 消息: {alert.get('message', '')}
## 上下文信息
- 相关指标:
{json.dumps(context.get('metrics', {}), indent=2, ensure_ascii=False)}
- 相关日志(最近5分钟):
{context.get('recent_logs', '无')}
- 服务拓扑:
{context.get('topology', '无')}
- 最近变更:
{context.get('recent_changes', '无')}
## 请输出
1. 根因分析:最可能的故障原因
2. 严重度评估:基于影响面重新评估
3. 影响面分析:哪些服务和用户可能受影响
4. 修复建议:具体的修复步骤(优先自动化的方案)
请以 JSON 格式输出:
{{"root_cause": "...", "severity_assessment": "...", "impact_analysis": "...", "remediation_plan": "...", "confidence": 0.0-1.0}}
"""
def _call_llm(self, prompt: str) -> str:
"""调用 LLM API"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.llm_api_key}",
}
payload = {
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3, # 低温度,确保输出稳定
"max_tokens": 2000,
}
response = requests.post(
self.llm_api_url,
headers=headers,
json=payload,
timeout=30
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
def _parse_response(self, response: str) -> DiagnosticResult:
"""解析 LLM 响应"""
try:
# 尝试提取 JSON
start = response.find("{")
end = response.rfind("}") + 1
if start >= 0 and end > start:
data = json.loads(response[start:end])
return DiagnosticResult(
root_cause=data.get("root_cause", ""),
severity_assessment=data.get("severity_assessment", ""),
impact_analysis=data.get("impact_analysis", ""),
remediation_plan=data.get("remediation_plan", ""),
confidence=data.get("confidence", 0.5),
)
except (json.JSONDecodeError, KeyError) as e:
pass
# 降级:返回原始文本
return DiagnosticResult(
root_cause=response[:500],
severity_assessment="unknown",
impact_analysis="unknown",
remediation_plan="需人工分析",
confidence=0.3,
)
if __name__ == "__main__":
engine = AlertDiagnosticEngine(
llm_api_url="https://api.example.com/v1/chat/completions",
llm_api_key="your-api-key"
)
alert = {
"alertname": "HighErrorRate",
"severity": "critical",
"labels": {"service": "payment-api", "instance": "10.0.1.5:8080"},
"message": "错误率 15.2%,持续 3 分钟",
}
context = {
"metrics": {
"error_rate": 0.152,
"p99_latency_ms": 3500,
"qps": 1200,
"cpu_usage": 0.89,
"memory_usage": 0.92,
},
"recent_logs": "[ERROR] 2026-07-11 02:20:01 connection refused: db-pool exhausted",
"topology": "payment-api → redis-cluster → mysql-primary",
"recent_changes": "2小时前部署了 payment-api v2.3.1",
}
result = engine.diagnose(alert, context)
print(f"根因: {result.root_cause}")
print(f"严重度: {result.severity_assessment}")
print(f"影响: {result.impact_analysis}")
print(f"修复建议: {result.remediation_plan}")
print(f"置信度: {result.confidence}")
告警反馈闭环
AI 诊断的准确性依赖持续学习。以下是一个完整的告警反馈闭环:
#!/usr/bin/env python3
"""告警反馈闭环 — 持续学习改进告警质量"""
import time
import json
from dataclasses import dataclass, field, asdict
from pathlib import Path
@dataclass
class AlertFeedback:
"""告警反馈记录"""
alert_id: str
alert_name: str
severity: str
was_actionable: bool # 告警是否需要人工处理
was_auto_resolved: bool # 是否被自动修复解决
false_positive: bool # 是否误报
resolution_time: float # 解决耗时(分钟)
root_cause: str # 根因
action_taken: str # 实际处理操作
operator: str # 处理人
feedback: str # 附加反馈
timestamp: float = field(default_factory=time.time)
class FeedbackLoop:
"""告警反馈闭环管理器"""
def __init__(self, storage_path: str = "/data/feedback"):
self.storage_path = Path(storage_path)
self.storage_path.mkdir(parents=True, exist_ok=True)
self.feedback_store: list[AlertFeedback] = []
self._load()
def record(self, feedback: AlertFeedback):
"""记录反馈"""
self.feedback_store.append(feedback)
self._save(feedback)
self._analyze_patterns()
def _save(self, feedback: AlertFeedback):
"""持久化反馈记录"""
filepath = self.storage_path / f"{feedback.alert_id}.json"
filepath.write_text(json.dumps(asdict(feedback), indent=2, ensure_ascii=False))
def _load(self):
"""加载历史反馈"""
for filepath in self.storage_path.glob("*.json"):
data = json.loads(filepath.read_text())
self.feedback_store.append(AlertFeedback(**data))
def _analyze_patterns(self):
"""分析反馈模式,输出改进建议"""
if len(self.feedback_store) < 10:
return
total = len(self.feedback_store)
false_positives = sum(1 for f in self.feedback_store if f.false_positive)
auto_resolved = sum(1 for f in self.feedback_store if f.was_auto_resolved)
fp_rate = false_positives / total * 100
auto_rate = auto_resolved / total * 100
print(f"\n=== 告警质量报告 ===")
print(f"总告警数: {total}")
print(f"误报率: {fp_rate:.1f}%")
print(f"自动修复率: {auto_rate:.1f}%")
print(f"平均解决时间: {sum(f.resolution_time for f in self.feedback_store)/total:.1f} 分钟")
# 按告警类型统计误报
by_name = {}
for f in self.feedback_store:
if f.alert_name not in by_name:
by_name[f.alert_name] = {"total": 0, "fp": 0}
by_name[f.alert_name]["total"] += 1
if f.false_positive:
by_name[f.alert_name]["fp"] += 1
print(f"\n=== 高误报告警 TOP 5 ===")
sorted_alerts = sorted(
by_name.items(),
key=lambda x: x[1]["fp"] / max(x[1]["total"], 1),
reverse=True
)[:5]
for name, stats in sorted_alerts:
rate = stats["fp"] / stats["total"] * 100
print(f" {name:30s} | 误报率 {rate:.0f}% ({stats['fp']}/{stats['total']})")
# 改进建议
if fp_rate > 20:
print("\n[建议] 误报率过高,建议调整告警阈值或增加前置条件")
if auto_rate < 30:
print("[建议] 自动修复率低,建议为高频告警增加 Runbook")
效果度量
关键指标
| 指标 | 定义 | 目标值 | 度量方法 |
|---|---|---|---|
| 平均告警数 | 每天产生的告警总量 | < 20 条/天 | 告警系统统计 |
| 有效告警率 | 需要人工处理的告警占比 | > 70% | 人工标注 |
| 误报率 | 不需要处理的告警占比 | < 10% | 人工标注 |
| 自动修复率 | 被自动 Runbook 解决的告警占比 | > 50% | 自动统计 |
| 平均响应时间(MTTA) | 从告警到开始处理的时间 | < 5 分钟 | 系统记录 |
| 平均修复时间(MTTR) | 从告警到问题解决的时间 | < 15 分钟 | 系统记录 |
| 告警疲劳指数 | 工程师忽略告警的比例 | < 5% | 人工调研 |
度量看板
#!/usr/bin/env python3
"""告警自动化效果度量看板"""
import time
import json
from dataclasses import dataclass, field
from collections import defaultdict
@dataclass
class AlertMetrics:
"""告警度量数据"""
total_alerts: int = 0
actionable_alerts: int = 0 # 需要人工处理的
false_positives: int = 0 # 误报
auto_resolved: int = 0 # 自动修复
escalated: int = 0 # 升级到人工
mtta_seconds: float = 0 # 平均响应时间
mttr_seconds: float = 0 # 平均修复时间
by_severity: dict = field(default_factory=lambda: defaultdict(int))
by_service: dict = field(default_factory=lambda: defaultdict(int))
by_runbook: dict = field(default_factory=lambda: {"executed": 0, "success": 0})
def to_dict(self) -> dict:
return {
"total_alerts": self.total_alerts,
"actionable_alerts": self.actionable_alerts,
"false_positive_rate": f"{self.false_positives/max(self.total_alerts,1)*100:.1f}%",
"auto_resolution_rate": f"{self.auto_resolved/max(self.total_alerts,1)*100:.1f}%",
"mtta": f"{self.mtta_seconds:.0f}s",
"mttr": f"{self.mttr_seconds:.0f}s",
"by_severity": dict(self.by_severity),
"by_service": dict(self.by_service),
"runbook_stats": dict(self.by_runbook),
}
if __name__ == "__main__":
metrics = AlertMetrics()
# 模拟数据
metrics.total_alerts = 156
metrics.actionable_alerts = 98
metrics.false_positives = 12
metrics.auto_resolved = 58
metrics.escalated = 40
metrics.mtta_seconds = 45
metrics.mttr_seconds = 480
metrics.by_severity = {"critical": 8, "high": 25, "medium": 68, "low": 55}
metrics.by_service = {"api-gw": 35, "db": 12, "web": 45, "cache": 28, "other": 36}
metrics.by_runbook = {"executed": 58, "success": 45}
print("=== 告警自动化效果看板 ===")
print(json.dumps(metrics.to_dict(), indent=2, ensure_ascii=False))
print(f"\n=== 核心指标 ===")
print(f"告警总量: {metrics.total_alerts} 条/天")
print(f"有效告警率: {metrics.actionable_alerts/metrics.total_alerts*100:.1f}%")
print(f"误报率: {metrics.false_positives/metrics.total_alerts*100:.1f}%")
print(f"自动修复率: {metrics.auto_resolved/metrics.total_alerts*100:.1f}%")
print(f"Runbook成功率: {metrics.by_runbook['success']/max(metrics.by_runbook['executed'],1)*100:.1f}%")
print(f"平均响应时间: {metrics.mtta_seconds:.0f}s")
print(f"平均修复时间: {metrics.mttr_seconds/60:.1f} 分钟")
总结
告警自动化不是一蹴而就的工程,而是一个从"噪声到信号、从信号到行动、从行动到自愈"的渐进式建设过程。
核心建设路径:
告警降噪是基础:通过 Alertmanager 的分组、抑制和静默,配合自定义的指纹去重和多维度关联,将告警风暴压缩为可管理的事件。目标是让工程师每天看到的告警从 100+ 条降到 20 条以内,有效告警率超过 70%。
分级路由是骨架:建立统一的 P0-P3 分级标准,配合路由规则和升级机制,确保每条告警在正确的时间到达正确的人。关键是在自动修复和人工介入之间找到平衡——安全操作自动执行,高风险操作人工确认。
Runbook 自动化是核心:将已知的故障处理流程编码为可自动执行的 Runbook,覆盖 CPU 过高、磁盘满、OOM、服务不可用、证书过期、IO 过高等高频场景。配合白名单、限流和熔断机制确保安全。
自愈平台是载体:构建统一的告警接入、处理、路由、修复、验证平台,将分散的自动化能力整合为完整的自愈链路。关键设计是"修复后验证"——不仅执行修复,还要验证修复效果,失败时自动回滚并升级人工。
AI 辅助是加速器:在规则驱动的基础上引入 AI 能力,利用 LLM 进行告警诊断、根因分析和修复建议生成。但 AI 不是替代规则,而是在规则无法覆盖的场景下提供智能辅助,并通过反馈闭环持续学习。
最终目标是构建一个比最优秀工程师更快响应的可靠性系统——在故障影响用户之前自动检测、分析并修复,让 SRE 从"救火队"变为"可靠性工程师"。