饮墨

子安饮墨馀三斗,留与卿儿作赋来

SRE Error Budget 实战:3 步落地错误预算驱动发布决策,告别拍脑袋上线

7 views

痛点

每周五下午,运维群总有人问"这个版本能不能发"。SRE 看了看最近的告警数量,拍脑袋说"先别发吧"——但研发说 deadline 到了,最终强行上线,周末果然出事。

问题出在哪?发布决策缺乏量化依据。 没有一个客观标准告诉你:系统当前的可靠性余量还够不够"冒一次险"。

Google SRE 提出的 Error Budget 模型把这个问题量化了:SLO 是 99.9%,一个月允许 43.2 分钟不可用。本月已消耗 40 分钟,剩余预算只有 3.2 分钟——这时候发版就是在赌命。反过来,如果预算还剩 80%,说明系统很稳,正是加速迭代的好时机。

Error Budget 的核心价值:把可靠性和迭代速度的博弈从主观争论变成客观数据。

方案

Error Budget = 1 - SLO。用它驱动三件事:

  1. 发布卡控:预算充足允许高风险变更,预算耗尽冻结发布
  2. 团队对齐:可靠性 vs 迭代速度不再靠嘴吵,用数据说话
  3. 故障复盘聚焦:哪个事件消耗了多少预算,优先级一目了然

实操步骤

第 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 落地三步走:

  1. 定义 SLI:可用性 + 延迟,必须基于用户体验,不是 CPU/内存
  2. 设定 SLO + 计算预算:从历史数据出发,30 天滚动窗口,宁松勿紧
  3. 自动化卡控:多窗口 Burn Rate 告警 + CI/CD 发布门禁脚本

当发布决策从"我觉得没问题"变成"Error Budget 剩余 67%,放行",SRE 才算真正落了地。可靠性不是目标,在可靠性和迭代速度之间找到可量化的平衡点,才是 Error Budget 的真正价值。

相关文章