痛点
每周五下午,运维群总有人问"这个版本能不能发"。SRE 看了看最近的告警数量,拍脑袋说"先别发吧"——但研发说 deadline 到了,最终强行上线,周末果然出事。
问题出在哪?发布决策缺乏量化依据。 没有一个客观标准告诉你:系统当前的可靠性余量还够不够"冒一次险"。
Google SRE 提出的 Error Budget 模型把这个问题量化了:SLO 是 99.9%,一个月允许 43.2 分钟不可用。本月已消耗 40 分钟,剩余预算只有 3.2 分钟——这时候发版就是在赌命。反过来,如果预算还剩 80%,说明系统很稳,正是加速迭代的好时机。
Error Budget 的核心价值:把可靠性和迭代速度的博弈从主观争论变成客观数据。
方案
Error Budget = 1 - SLO。用它驱动三件事:
- 发布卡控:预算充足允许高风险变更,预算耗尽冻结发布
- 团队对齐:可靠性 vs 迭代速度不再靠嘴吵,用数据说话
- 故障复盘聚焦:哪个事件消耗了多少预算,优先级一目了然
实操步骤
第 1 步:定义 SLI(服务级别指标)
SLI 是整个体系的地基,选错了后面全白干。核心原则:SLI 必须反映用户体验,不是系统内部指标。
# Prometheus recording rules — 两个最通用的 SLI
groups:
- name: sli_recording_rules
interval: 30s
rules:
# 可用性 SLI:非 5xx 请求占比
- record: sli:http_availability:ratio_rate5m
expr: |
sum(rate(http_requests_total{code!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 延迟 SLI:300ms 内响应的请求占比
- record: sli:http_latency_good:ratio_rate5m
expr: |
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m]))
/
sum(rate(http_request_duration_seconds_count[5m]))
# 30 天滚动窗口(用于 Error Budget 计算)
- record: sli:http_availability:ratio_rate30d
expr: |
sum(increase(http_requests_total{code!~"5.."}[30d]))
/
sum(increase(http_requests_total[30d]))
SLI 选型速查:
| 服务类型 | 推荐 SLI | 延迟阈值 |
|---|---|---|
| REST API | 可用性 + P99 延迟 | 300ms |
| Web 页面 | 可用性 + 首屏加载 | 1s |
| 批处理任务 | 成功率 + 完成时间 | 按 SLA 定 |
| 消息队列消费 | 处理成功率 + 消费延迟 | 视业务定 |
第 2 步:设定 SLO 并计算 Error Budget
SLO 怎么定?不要拍脑袋说 99.99%,先看历史数据。
# 查询过去 90 天的实际可用性
curl -s "http://prometheus:9090/api/v1/query" \
--data-urlencode 'query=
sum(increase(http_requests_total{code!~"5.."}[90d]))
/
sum(increase(http_requests_total[90d]))
' | jq -r '.data.result[0].value[1]'
# 输出示例: 0.99957 → 实际可用性 99.957%
如果历史实际可用性是 99.95%,SLO 设 99.99% 等于一开始就在欠债。建议从历史 P50 水位起步,逐步收紧。
Error Budget 计算:
SLO = 99.9%
Error Budget = 1 - 0.999 = 0.1%
30 天总分钟 = 43200
允许不可用 = 43200 × 0.001 = 43.2 分钟
本月已消耗 38.5 分钟 → 剩余 4.7 分钟 → 剩余比例 10.9%
关键指标 — Burn Rate(燃烧速率):
| Burn Rate | 含义 | 动作 |
|---|---|---|
| < 1x | 预算消耗正常 | 正常迭代发布 |
| 1x - 2x | 消耗偏快 | 评估风险后发布 |
| 2x - 10x | 消耗过快 | 暂停非关键发布 |
| > 10x | 正在发生严重事故 | 立即响应 + 全面冻结 |
Burn Rate 计算公式:当前错误率 / Error Budget 速率。Burn Rate = 1x 意味着照此速度,刚好在窗口结束时耗尽预算。
第 3 步:自动化告警 + CI/CD 发布门禁
Prometheus 多窗口 Burn Rate 告警(Google SRE Workbook 推荐方案):
groups:
- name: error_budget_burn_rate
rules:
# 快速燃烧:1h 窗口 14.4x burn rate(1h 耗 2% 预算)
- alert: ErrorBudgetFastBurn
expr: |
(1 - sli:http_availability:ratio_rate1h) > (14.4 * 0.001)
and
(1 - sli:http_availability:ratio_rate5m) > (14.4 * 0.001)
for: 2m
labels:
severity: critical
annotations:
summary: "Error Budget 快速燃烧中"
action: "立即排查,冻结所有发布"
# 慢速燃烧:6h 窗口 6x burn rate
- alert: ErrorBudgetSlowBurn
expr: |
(1 - sli:http_availability:ratio_rate6h) > (6 * 0.001)
and
(1 - sli:http_availability:ratio_rate30m) > (6 * 0.001)
for: 5m
labels:
severity: warning
annotations:
summary: "Error Budget 持续消耗中"
action: "限制高风险发布,排查近期变更"
# 月度预算剩余 < 20%
- alert: ErrorBudgetLow
expr: |
(
1 - (1 - sli:http_availability:ratio_rate30d) / 0.001
) < 0.2
for: 10m
labels:
severity: warning
annotations:
summary: "本月 Error Budget 剩余不足 20%"
action: "进入发布冻结期,仅允许修复类变更"
CI/CD 发布门禁脚本(集成到 GitLab CI / GitHub Actions):
#!/bin/bash
# check_error_budget.sh — 发布前自动检查 Error Budget
set -euo pipefail
PROM_URL="${PROMETHEUS_URL:-http://prometheus:9090}"
SLO=0.999
# 查询 30 天可用性
availability=$(curl -sf "$PROM_URL/api/v1/query" \
--data-urlencode "query=sli:http_availability:ratio_rate30d" \
| jq -r '.data.result[0].value[1]')
# 计算预算剩余比例
budget_remaining=$(python3 -c "
slo=$SLO; avail=$availability
consumed = (1 - avail) / (1 - slo)
remaining = max(0, 1 - consumed)
print(f'{remaining:.4f}')
")
pct=$(python3 -c "print(f'{$budget_remaining * 100:.1f}')")
echo "当前 30d 可用性: $availability"
echo "Error Budget 剩余: ${pct}%"
if (( $(echo "$budget_remaining < 0.20" | bc -l) )); then
echo "❌ 预算不足 20%,禁止发布。请联系 SRE 审批修复类变更。"
exit 1
elif (( $(echo "$budget_remaining < 0.50" | bc -l) )); then
echo "⚠️ 预算不足 50%,需 SRE 审批后方可发布。"
exit 2
else
echo "✅ Error Budget 充足(${pct}%),允许发布。"
exit 0
fi
在 GitLab CI 中集成:
# .gitlab-ci.yml
release_gate:
stage: pre-deploy
script:
- bash scripts/check_error_budget.sh
allow_failure: false
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
避坑
1. SLO 定太高,一上来就欠债
最常见的错误。老板说"我们要 99.99%",但过去 3 个月实际只有 99.95%。SLO 定为 99.99% 意味着一个月只有 4.3 分钟预算,系统咳嗽一下就超了。建议先设 99.9%,稳定 3 个月后再考虑收紧到 99.95%。
2. 只看可用性 SLI,忽略延迟
接口返回 200 但响应 8 秒,用户体验跟 503 没区别。必须同时跟踪延迟 SLI,而且阈值要按业务场景分——支付接口 200ms,信息流 API 500ms,后台报表 5s,不能一刀切。
3. 预算耗尽后没有执行力
最大的坑不是技术,是组织。预算耗尽了但研发说"这个功能 CEO 要的,必须上"——如果管理层不支持冻结发布,Error Budget 就是摆设。建议提前在团队 OKR/章程中写明:Error Budget 耗尽 = 自动进入稳定性冲刺,只做可靠性修复,至少维持一个迭代周期。 让规则替代人情。
总结
Error Budget 落地三步走:
- 定义 SLI:可用性 + 延迟,必须基于用户体验,不是 CPU/内存
- 设定 SLO + 计算预算:从历史数据出发,30 天滚动窗口,宁松勿紧
- 自动化卡控:多窗口 Burn Rate 告警 + CI/CD 发布门禁脚本
当发布决策从"我觉得没问题"变成"Error Budget 剩余 67%,放行",SRE 才算真正落了地。可靠性不是目标,在可靠性和迭代速度之间找到可量化的平衡点,才是 Error Budget 的真正价值。