面向 ITOps Agent Platform 系列第 2 篇。先学"什么叫系统健康",再学 AI 如何判断系统健康。
1. 为什么这个问题重要
如果 AIOps 不知道"什么是健康",它的一切判断都是空中楼阁。SLO/SLI/Error Budget 提供了可度量的可靠性目标,是 AI 判断系统健康与否的唯一尺度。
2. 前沿技术趋势
Google SRE 将 error budget 用作可靠性与变更速度之间的控制机制。AIOps 的演进方向,是让告警、优先级、自动化策略都围绕 error budget 展开,而非围绕孤立的资源阈值(如 CPU > 80%)。
3. 核心概念
SLI(Service Level Indicator)
用户真正感知的指标:availability、latency、correctness、freshness、durability。
SLO(Service Level Objective)
99.95% availability
P99 latency < 500ms
99.9% successful checkout
Error Budget
error_budget = 1 – SLO
一个 99.9% SLO 的服务,在对应窗口内 error budget 就是 0.1%。AIOps 不应只问"CPU 是否超过 80%“,而应问"当前异常是否正在消耗业务 error budget”。
4. ITOps Agent Platform 中的对应模块
SLO Agent → Budget Calculator → Incident Priority → Automation Policy
5. 架构设计
把可靠性目标下沉到自动化决策:
SLO 定义
↓
Error Budget 实时计算
↓
Incident 优先级映射
↓
自动化权限分级(预算充足→可自动,预算耗尽→强制人工)
下面是一个基于 error budget 剩余量自动调整自动化权限的示例配置:
automation_policy:
service: payment–api
# 以 30 天窗口的 error budget 剩余比例为决策依据
error_budget_levels:
– name: healthy
remaining_budget: ">= 50%"
automation:
enabled: true
allowed_actions:
– restart_pod
– scale_out
– rollback_release
approval: none
– name: degraded
remaining_budget: ">= 20%"
automation:
enabled: true
allowed_actions:
– restart_pod
– scale_out
approval: auto_ticket
– name: critical
remaining_budget: "< 20%"
automation:
enabled: false
allowed_actions: []
approval: manual_approval
# 预算耗尽时,直接切换到强制人工审批
budget_exhausted:
action: force_manual
approval_flow: p1_oncall
note: "error budget 耗尽时禁止自动变更,需人工审批后执行"
策略规则说明:
- healthy:预算充足(≥ 50%),允许自动重启、扩容、回滚,无需人工审批。
- degraded:预算部分消耗(≥ 20%),仍允许自动重启/扩容,但需自动创建变更单留痕。
- critical:预算紧张(< 20%),关闭自动化变更,所有动作转人工审批。
- budget_exhausted:预算耗尽,强制走 P1 值班人工审批流程,避免在系统不稳定时叠加自动变更风险。
6. 实战实验
定义一个服务的 SLO:
service: payment–api
slos:
– name: availability
target: 99.95%
window: 30d
– name: latency
target: 99.9%
threshold_ms: 500
window: 30d
7. 代码 / API / 配置
Burn rate 是比"当前错误率"更关键的信号(它衡量消耗预算的速度):
def burn_rate(error_ratio, slo_ratio):
# error_ratio: 实际错误比例; slo_ratio: 允许的错误预算比例
return error_ratio / slo_ratio
# 例:错误率 2%,SLO 允许 0.1%,burn rate = 20 倍
完整 Python 实战示例
下面给出可直接运行的示例,覆盖 SLO 配置、滑动窗口 error budget 计算、多窗口 burn rate 监控与告警级别输出:
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from collections import deque
from typing import Optional
# ———- 1. SLO 配置数据结构 ———-
@dataclass
class SLOConfig:
service: str
slo_name: str
target: float # SLO 目标,如 0.999 表示 99.9%
window_days: int = 30 # 统计窗口
thresholds: dict = field(default_factory=lambda: {
"1h": 14.4,
"6h": 6.0,
"30d": 1.0,
})
@property
def error_budget_ratio(self) –> float:
"""SLO 允许的错误比例 = 1 – target"""
return 1 – self.target
@dataclass
class Event:
ts: datetime
ok: bool
# ———- 2. 滑动窗口 error budget 计算 ———-
class ErrorBudgetTracker:
def __init__(self, slo: SLOConfig):
self.slo = slo
self.events: deque[Event] = deque()
def record(self, ts: datetime, ok: bool):
"""记录一次请求结果"""
self.events.append(Event(ts, ok))
def error_ratio(self, window: timedelta, now: datetime) –> float:
"""计算滑动窗口内的错误率"""
cutoff = now – window
window_events = [e for e in self.events if e.ts >= cutoff]
if not window_events:
return 0.0
failed = sum(1 for e in window_events if not e.ok)
return failed / len(window_events)
def remaining_error_budget(self, now: datetime, window_days: Optional[int] = None) –> float:
"""剩余 error budget 比例;小于等于 0 表示预算耗尽"""
days = window_days or self.slo.window_days
error_ratio = self.error_ratio(timedelta(days=days), now)
return max(0.0, self.slo.error_budget_ratio – error_ratio)
# ———- 3. 多窗口 burn rate 监控与告警级别 ———-
def burn_rate(error_ratio: float, slo_ratio: float) –> float:
"""burn rate = 实际错误率 / SLO 允许错误率"""
return error_ratio / slo_ratio if slo_ratio > 0 else float("inf")
def get_severity(window: str, burn_rate_value: float, threshold: float) –> str:
if burn_rate_value < threshold:
return "OK"
return {"1h": "P1/critical", "6h": "P2/warning", "30d": "P3/info"}.get(window, "OK")
def multi_window_monitor(tracker: ErrorBudgetTracker, now: datetime) –> list[dict]:
"""同时监控 1h / 6h / 30d 三个窗口"""
windows = {
"1h": timedelta(hours=1),
"6h": timedelta(hours=6),
"30d": timedelta(days=30),
}
results = []
for name, window in windows.items():
error_ratio = tracker.error_ratio(window, now)
br = burn_rate(error_ratio, tracker.slo.error_budget_ratio)
threshold = tracker.slo.thresholds[name]
results.append({
"window": name,
"error_ratio": round(error_ratio, 6),
"burn_rate": round(br, 2),
"threshold": threshold,
"severity": get_severity(name, br, threshold),
"budget_remaining": round(tracker.remaining_error_budget(now), 6),
})
return results
if __name__ == "__main__":
slo = SLOConfig(service="payment-api", slo_name="availability", target=0.999)
now = datetime(2026, 9, 3, 13, 0, 0)
# === 场景 1:正常流量,所有窗口 burn rate 均为 0 ===
tracker = ErrorBudgetTracker(slo)
for i in range(100):
tracker.record(now – timedelta(minutes=i % 60), ok=True)
for i in range(1000):
tracker.record(now – timedelta(minutes=60 + i % 300), ok=True)
print("场景 1:正常流量")
for r in multi_window_monitor(tracker, now):
print(f" window={r['window']}, burn_rate={r['burn_rate']}, "
f"threshold={r['threshold']}, severity={r['severity']}")
# 输出示例:
# window=1h, burn_rate=0.0, threshold=14.4, severity=OK
# window=6h, burn_rate=0.0, threshold=6.0, severity=OK
# window=30d, burn_rate=0.0, threshold=1.0, severity=OK
# === 场景 2:短期故障,1h/6h 超过阈值,30d 仍可控 ===
tracker = ErrorBudgetTracker(slo)
for i in range(100):
tracker.record(now – timedelta(minutes=i % 60), ok=(i % 50 != 0)) # 1h 约 2% 失败
for i in range(1000):
tracker.record(now – timedelta(minutes=60 + i % 300), ok=(i % 100 != 0)) # 6h 约 1% 失败
for i in range(50000):
tracker.record(now – timedelta(hours=10 + i % 700), ok=True) # 30d 历史成功样本稀释
print("场景 2:短期故障")
for r in multi_window_monitor(tracker, now):
print(f" window={r['window']}, burn_rate={r['burn_rate']}, "
f"threshold={r['threshold']}, severity={r['severity']}")
# 输出示例:
# window=1h, burn_rate≈20.0, threshold=14.4, severity=P1/critical
# window=6h, burn_rate≈10.0, threshold=6.0, severity=P2/warning
# window=30d, burn_rate≈0.2, threshold=1.0, severity=OK
# === 场景 3:严重故障,预算耗尽 ===
tracker = ErrorBudgetTracker(slo)
for i in range(100):
tracker.record(now – timedelta(minutes=i % 60), ok=(i % 10 < 7)) # 1h 约 30% 失败
for i in range(1000):
tracker.record(now – timedelta(minutes=60 + i % 300), ok=(i % 10 < 8)) # 6h 约 20% 失败
print("场景 3:严重故障,预算耗尽")
for r in multi_window_monitor(tracker, now):
print(f" window={r['window']}, burn_rate={r['burn_rate']}, "
f"threshold={r['threshold']}, severity={r['severity']}, "
f"budget_remaining={r['budget_remaining']}")
# 输出示例:
# window=1h, burn_rate≈300.0, threshold=14.4, severity=P1/critical, budget_remaining≈0.0
# window=6h, burn_rate≈200.0, threshold=6.0, severity=P2/warning, budget_remaining≈0.0
# window=30d, burn_rate≈209.0, threshold=1.0, severity=P3/info, budget_remaining=0.0
边界条件与注意事项
在实际落地时,上述示例的简化逻辑会遇到几个边界条件,需要显式处理,否则会产生误报或漏报:
1. 窗口内样本量不足时 error_ratio 的计算偏差
当窗口内样本量很少时,error_ratio 的统计意义会显著下降。例如 1h 窗口内只有 10 个请求,其中 1 个失败,error_ratio = 10%,burn rate 高达 100 倍,但这很可能只是噪声而非真实故障。
def error_ratio(self, window: timedelta, now: datetime, min_samples: int = 100) –> float:
"""计算滑动窗口内的错误率;样本不足时返回 None 表示不可信"""
cutoff = now – window
window_events = [e for e in self.events if e.ts >= cutoff]
if len(window_events) < min_samples:
return None # 样本不足,不参与 burn rate 判定
failed = sum(1 for e in window_events if not e.ok)
return failed / len(window_events)
处理建议:为每个窗口设置最小样本量阈值(如 1h 窗口至少 100 个样本),样本不足时跳过该窗口的 burn rate 判定,或降级为 observe 而非直接告警。
2. 多个 SLO 同时被击穿时告警优先级如何合并
一个服务通常同时维护多个 SLO(availability、latency、correctness 等)。当多个 SLO 同时被击穿时,不能简单取最高级别,而应合并为一个聚合告警,避免告警风暴:
def merge_slo_alerts(alerts: list[dict]) –> dict:
"""合并同一服务的多个 SLO 告警,取最高优先级并聚合上下文"""
if not alerts:
return {}
severity_rank = {"P1": 3, "P2": 2, "P3": 1}
top = max(alerts, key=lambda a: severity_rank.get(a["severity"], 0))
return {
"service": top["service"],
"severity": top["severity"],
"slo_hits": [a["slo_name"] for a in alerts],
"max_burn_rate": max(a["burn_rate"] for a in alerts),
"windows": sorted({a["window"] for a in alerts}),
"action": top["action"],
}
处理建议:按「最高严重级别 + 命中的 SLO 列表 + 最大 burn rate」合并为一条告警;若多个 SLO 指向同一根因(如上游依赖超时同时击穿 availability 与 latency),应合并为一次排查,而非重复触发。
3. burn_rate 为无穷大(slo_ratio=0)时的处理
当 SLO 目标为 100%(slo_ratio = 0)时,burn_rate 会除以 0 得到无穷大。此时任何一次失败都意味着「预算瞬间耗尽」,需要特殊处理:
def burn_rate(error_ratio: float, slo_ratio: float) –> float:
"""burn rate = 实际错误率 / SLO 允许错误率;slo_ratio=0 时返回 inf"""
if slo_ratio <= 0:
return float("inf") if error_ratio > 0 else 0.0
return error_ratio / slo_ratio
处理建议:slo_ratio = 0 意味着「零容忍」策略,任何失败都应直接触发最高级别告警并升级人工,不应进入常规的 burn rate 阈值比较逻辑。同时应在 SLO 定义阶段就避免 target = 1.0 的写法,改为 target = 0.9999 等可度量的目标。
4. 滑动窗口与固定窗口在 error budget 计算上的差异
滑动窗口(rolling window)与固定窗口(calendar window)在 error budget 计算上有本质差异:
| 窗口边界 | 随当前时间滚动,如「最近 30 天」 | 按日历对齐,如「本月 1 日 0 点至月末」 |
| 预算重置 | 永不重置,旧数据持续滑出 | 每个周期(月/周)重置 |
| 对突发故障的敏感度 | 高,故障会立即反映在窗口内 | 低,月初的故障会被整月数据稀释 |
| 适合场景 | 实时监控、burn rate 告警 | 月度/季度复盘、容量规划 |
def remaining_error_budget_fixed_window(
slo: SLOConfig, month_start: datetime, now: datetime
) –> float:
"""固定窗口(自然月)error budget 剩余量计算"""
elapsed_ratio = (now – month_start).total_seconds() / (
(month_start.replace(month=month_start.month % 12 + 1, day=1) – month_start).total_seconds()
)
# 已消耗的预算 = 已流逝时间比例 × 总预算
consumed_budget = slo.error_budget_ratio * elapsed_ratio
# 实际错误率
error_ratio = compute_error_ratio(month_start, now)
return max(0.0, consumed_budget – error_ratio)
处理建议:生产环境应同时维护两种口径——滑动窗口用于实时 burn rate 告警(快速发现),固定窗口用于周期性复盘与容量规划(宏观视角)。两者结论不一致时(如滑动窗口已告警但固定窗口仍健康),以滑动窗口为准触发响应,固定窗口仅作趋势参考。
当 burn rate 超过阈值时的排查决策流程 burn rate 阈值与时间窗口对照表
以允许错误率为 0.1%(SLO 99.9%,窗口 30d)的服务为例,可按「预算消耗速度」设置多档阈值:
| 1h | 14.4 | 约 2.08 天(1 小时消耗约 2% 预算) | P1 / critical | 故障正在快速消耗预算,需立即响应并拉通排查 |
| 6h | 6 | 约 5 天(6 小时消耗约 5% 预算) | P2 / warning | 持续错误正在累积预算,应进入排查流程 |
| 30d | 1 | 约 30 天 | P3 / info | 整窗预算刚好按计划消耗,用于趋势观察与复盘 |
说明:同一时刻可以同时维护多个窗口的 burn rate 告警。短期窗口(1h)负责「快速发现」,长期窗口(30d)负责「慢泄漏」发现;当 1h 与 6h 同时超阈值时,通常意味着故障仍在持续,告警优先级应相应上调。
当 SLO Agent 检测到某个 SLI 的 burn rate 超过预设阈值(例如短期窗口 burn rate > 14.4 倍时,意味着 2 小时内消耗完 30 天错误预算),应触发以下决策链路:
SLO Agent 检测高 burn rate
- 输入:实时 SLI 指标(如 availability 当前低于 SLO、latency P99 超标事件流)
- 输出:一个告警事件,包含 service、slo_name、burn_rate、time_window、severity 示例:
alert: payment-api / availability / burn_rate=20.0 / window=1h / severity=critical
关联 SLI 指标与维度
- 输入:告警事件中的 service 标签与 SLO 定义
- 输出:命中该 SLO 的全部 SLI 指标集合及其当前值 示例:
service=payment-api
availability=99.2%(低于 99.95%)
latency_p99=780ms(超过 500ms)
error_rate=2.0%
定位异常服务与贡献资源
- 输入:关联后的 SLI 指标,结合运行时拓扑和 span/trace 数据
- 输出:疑似异常的服务实例、依赖或基础设施组件 示例:
suspect_service=payment-api-pod-7f8c9
root_cause_hint=upstream dependency: inventory-service timeout
判断触发自动修复或升级人工
- 输入:error budget 剩余量、burn rate 等级、事件可信度、自动化策略
- 输出:动作决策(自动修复 / 人工升级 / 继续观察) 示例:
error_budget_remaining=0.03%(即将耗尽)
burn_rate_level=critical
confidence=high
action=escalate_to_human(P1)
下面是上述 4 步决策链路的完整流程图:
#mermaid-svg-yqE4rw5SmzxSee9V{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yqE4rw5SmzxSee9V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yqE4rw5SmzxSee9V .error-icon{fill:#552222;}#mermaid-svg-yqE4rw5SmzxSee9V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yqE4rw5SmzxSee9V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yqE4rw5SmzxSee9V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yqE4rw5SmzxSee9V .marker.cross{stroke:#333333;}#mermaid-svg-yqE4rw5SmzxSee9V svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yqE4rw5SmzxSee9V p{margin:0;}#mermaid-svg-yqE4rw5SmzxSee9V .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-yqE4rw5SmzxSee9V .cluster-label text{fill:#333;}#mermaid-svg-yqE4rw5SmzxSee9V .cluster-label span{color:#333;}#mermaid-svg-yqE4rw5SmzxSee9V .cluster-label span p{background-color:transparent;}#mermaid-svg-yqE4rw5SmzxSee9V .label text,#mermaid-svg-yqE4rw5SmzxSee9V span{fill:#333;color:#333;}#mermaid-svg-yqE4rw5SmzxSee9V .node rect,#mermaid-svg-yqE4rw5SmzxSee9V .node circle,#mermaid-svg-yqE4rw5SmzxSee9V .node ellipse,#mermaid-svg-yqE4rw5SmzxSee9V .node polygon,#mermaid-svg-yqE4rw5SmzxSee9V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yqE4rw5SmzxSee9V .rough-node .label text,#mermaid-svg-yqE4rw5SmzxSee9V .node .label text,#mermaid-svg-yqE4rw5SmzxSee9V .image-shape .label,#mermaid-svg-yqE4rw5SmzxSee9V .icon-shape .label{text-anchor:middle;}#mermaid-svg-yqE4rw5SmzxSee9V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yqE4rw5SmzxSee9V .rough-node .label,#mermaid-svg-yqE4rw5SmzxSee9V .node .label,#mermaid-svg-yqE4rw5SmzxSee9V .image-shape .label,#mermaid-svg-yqE4rw5SmzxSee9V .icon-shape .label{text-align:center;}#mermaid-svg-yqE4rw5SmzxSee9V .node.clickable{cursor:pointer;}#mermaid-svg-yqE4rw5SmzxSee9V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yqE4rw5SmzxSee9V .arrowheadPath{fill:#333333;}#mermaid-svg-yqE4rw5SmzxSee9V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yqE4rw5SmzxSee9V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yqE4rw5SmzxSee9V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yqE4rw5SmzxSee9V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yqE4rw5SmzxSee9V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yqE4rw5SmzxSee9V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yqE4rw5SmzxSee9V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yqE4rw5SmzxSee9V .cluster text{fill:#333;}#mermaid-svg-yqE4rw5SmzxSee9V .cluster span{color:#333;}#mermaid-svg-yqE4rw5SmzxSee9V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yqE4rw5SmzxSee9V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yqE4rw5SmzxSee9V rect.text{fill:none;stroke-width:0;}#mermaid-svg-yqE4rw5SmzxSee9V .icon-shape,#mermaid-svg-yqE4rw5SmzxSee9V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yqE4rw5SmzxSee9V .icon-shape p,#mermaid-svg-yqE4rw5SmzxSee9V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yqE4rw5SmzxSee9V .icon-shape .label rect,#mermaid-svg-yqE4rw5SmzxSee9V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yqE4rw5SmzxSee9V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yqE4rw5SmzxSee9V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yqE4rw5SmzxSee9V :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
剩余 >= 20%(预算充足)
高(>= HIGH_TRUST)
低
剩余 < 20%(预算紧张)
critical
warning
剩余 <= 0(预算耗尽)
SLO Agent 检测高 burn rate
burn_rate >= 阈值?
继续观察 observe
关联 SLI 指标与维度
定位异常服务与贡献资源
error budget 剩余量?
置信度 confidence?
自动修复 auto_remediate
继续观察 observe
burn rate 等级?
升级人工 escalate_to_human(P1)
自动创建变更单 + 人工审批
强制人工审批 force_manual(P1 值班)
伪代码描述完整链路:
def troubleshoot_high_burn_rate(alert):
# Step 1: SLO Agent 输出高 burn rate 告警
service = alert["service"]
slo_name = alert["slo_name"]
burn_rate = alert["burn_rate"]
if burn_rate < BURN_RATE_THRESHOLD:
return {"action": "observe", "reason": "burn rate below threshold"}
# Step 2: 关联 SLI 指标
sli_metrics = get_sli_metrics(service, slo_name)
# 返回如:{"availability": 0.992, "latency_p99_ms": 780, "error_rate": 0.02}
# Step 3: 定位异常服务
topology = get_service_topology(service)
suspects = locate_anomaly(topology, sli_metrics)
# 返回如:["payment-api-pod-7f8c9"],可能通过 trace/日志关联
# Step 4: 评估预算和置信度,决定动作
error_budget = get_error_budget(service, slo_name, window="30d")
confidence = calc_confidence(sli_metrics, suspects)
if error_budget.burned > BUDGET_CRITICAL_RATIO and confidence >= HIGH_TRUST:
return {"action": "auto_remediate", "targets": suspects,
"allowed_actions": ["restart_pod", "scale_out"]}
elif error_budget.remaining < BUDGET_EXHAUSTED_THRESHOLD:
return {"action": "escalate_to_human", "priority": "P1",
"reason": "error budget nearly exhausted, manual control required"}
else:
return {"action": "observe", "reason": "collect more evidence"}
8. 生产环境最佳实践
不要让 CPU 直接决定"自动修复"。应组合多个信号:
CPU 95% + request error rate ↑ + latency ↑ + SLO burn rate ↑ = 高可信事件
9. 常见错误
- 用资源指标(CPU/内存)代替 SLI 来定义健康。
- 只设 SLO 不计算 burn rate,无法感知"预算消耗速度"。
- 把 error budget 当摆设,自动化策略与其脱钩。
10. 安全注意事项
Error budget 也是"自动化风险预算":预算耗尽时,应收紧自动化权限,回退到人工,避免在系统不稳定时叠加更多变更风险。
11. 可观测性
为每个 SLI 暴露实时采集与 burn rate 指标,并打上 service/environment 标签,供 SLO Agent 消费。
12. Evaluation
验证"异常是否真的消耗 error budget",用一个 SLO → Error Budget → Alert Priority 的 Demo 做端到端验收。
13. 验收标准
完成一个 SLO → Error Budget → Alert Priority 的可运行 Demo。
14. 思考题
- 你团队 Top 3 服务的 SLO 是什么?是否可度量?
- 一个 CPU 100% 但业务无损的服务,该不该触发 P1 告警?
15. 延伸阅读
- Google SRE Workbook
- Error Budget Policy
- Eliminating Toil
网硕互联帮助中心




评论前必须登录!
注册