SLO 工程实践

学习 SLO 工程实践 相关知识

SLO 工程实践

学习目标

掌握从 SLI 定义 → SLO 设定 → 错误预算策略 → 告警规则 → Dashboard 设计的完整 SLO 工程链路。


1. SLI 定义方法论

1.1 好的 SLI 的三个标准

标准说明反例
用户可见用户直接感受到CPU使用率(用户不直接感知)
可测量Prometheus/日志能采集”用户体验好”(无法量化)
可行动变差时能触发行动”代码行数”(无关可靠性)

1.2 SLI 四类黄金信号

可用性 (Availability)  →  "服务能正常响应吗?"
延迟   (Latency)       →  "服务响应快吗?"
流量   (Traffic)       →  "服务被用得多吗?"
错误   (Errors)        →  "多少请求失败了?"
饱和   (Saturation)    →  "服务快到极限了吗?"

1.3 按服务类型选 SLI

服务类型核心 SLIPromQL 示例
HTTP API可用性、P99延迟、错误率见下方
gRPC可用性、P99延迟grpc_server_handled_total
消息队列消费延迟、积压量kafka_consumer_lag
数据库查询延迟、连接池利用率pg_stat_activity
定时任务成功率、执行时长自建指标

1.4 PromQL 实现常用 SLI

# === 可用性 (Availability) ===
# HTTP API 成功率(排除 5xx)
sum(rate(http_requests_total{status!~"5.."}[5m]))
  /
sum(rate(http_requests_total[5m]))

# === 延迟 (Latency) ===
# P99 响应时间
histogram_quantile(0.99,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

# P50/P95/P99 三线合一
histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

# === 错误率 (Error Rate) ===
# 5xx 占比(按 endpoint 分组)
sum(rate(http_requests_total{status=~"5.."}[5m])) by (path)
  /
sum(rate(http_requests_total[5m])) by (path)

# === 饱和度 (Saturation) ===
# Goroutine 数量(Go 服务)
go_goroutines{job="my-service"}

# 线程池利用率
thread_pool_active_threads / thread_pool_max_threads

2. SLO 设定

2.1 SLO 黄金公式

SLO = SLI ≥ 目标值,在 测量窗口 内

例:99.9% 的请求在 30 天内 P99 < 300ms

2.2 可用性 SLO 对照表

SLO月度不可用时间年度不可用时间适用场景
99%7h 18m3d 15h内部工具
99.9%43m8h 45m业务后台
99.95%21m4h 22m支付/交易
99.99%4m52m核心基础设施

2.3 按重要性设定 SLO

# 例:一个电商系统的 SLO 矩阵
services:
  - name: 用户登录
    tier: critical
    slo:
      availability: 99.95%    # 月度
      latency_p99: 500ms
      
  - name: 商品列表
    tier: important  
    slo:
      availability: 99.9%
      latency_p99: 800ms
      
  - name: 推荐算法
    tier: best_effort
    slo:
      availability: 99%
      latency_p99: 2000ms

2.4 SLO 设定的常见陷阱

陷阱说明正确做法
所有服务 99.99%过度承诺,不可持续按 tier 分级
只看平均值P50 掩盖长尾必须用 P95/P99
窗口太短5分钟窗口波动大至少 28 天滚动
忽略部分失败只看整体成功率按 endpoint 拆分
无降级目标全有或全无定义 degraded 状态

3. 错误预算

3.1 核心公式

错误预算 = 1 - SLO

如果 SLO 是 99.9% 可用性:
错误预算 = 0.1% = 每月允许 43 分钟不可用

3.2 错误预算消耗计算

# 错误预算消耗率(当前烧钱速度 vs 预算)
# 假设 SLO = 99.9%(30天窗口)

# 1. 当前错误率
error_rate = sum(rate(http_requests_total{status=~"5.."}[5m]))
              /
             sum(rate(http_requests_total[5m]))

# 2. 错误预算剩余(百分比)
budget_remaining = (1 - error_rate) - 0.999

# 3. 错误预算消耗速率(按当前速率多久烧完预算)
# burn rate < 1: 正常
# burn rate > 1: 在消耗预算(当月会有短缺)
# burn rate > 10: 紧急!几小时内烧完
burn_rate = (error_rate / (1 - 0.999))

3.3 错误预算策略

错误预算 > 50% 剩余:
  → 正常节奏发布,功能上线不受限

错误预算 20%-50%:
  → 减少发布频率,增加变更评审

错误预算 < 20%:
  → 冻结所有非紧急发布
  → 全员投入可靠性改进

错误预算耗尽(≤ 0):
  → 发布完全冻结
  → 启动可靠性专项
  → 需要 VP 级别审批才能破例

4. Multi-Window Multi-Burn-Rate 告警

4.1 核心原理

Google SRE 提出的告警策略:用两个窗口 + 两种燃烧速率检测不同程度的问题。

短窗口 + 高燃速 → 紧急告警(page)
长窗口 + 低燃速 → 警告(ticket)

目的:避免单一窗口的误报,同时不错过真实故障

4.2 标准配置

告警级别短窗口短窗口燃速长窗口长窗口燃速通知
Critical1h14.4x5m14.4xPagerDuty/电话
Warning6h6x30m6xSlack/Ticket
Info3d1xDashboard

4.3 Prometheus 告警规则

# prometheus-rules/slo-alerts.yaml
groups:
  - name: slo_alerts
    interval: 30s
    rules:
    
      # === Critical: 1h 内燃烧了 2% 的错误预算 ===
      - alert: SLOBurnRateCritical
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[1h]))
            /
            sum(rate(http_requests_total[1h]))
          ) > 14.4 * (1 - 0.999)
        for: 5m
        labels:
          severity: critical
          slo: "99.9%"
        annotations:
          summary: "错误预算高速燃烧!1h burn rate > 14.4x"
          description: "当前 1h 错误率 {{ $value | humanizePercentage }},
                       预算燃烧速率远超正常值,2h 内可能耗尽月度预算"
          
      # === Warning: 6h 低燃速预热 ===
      - alert: SLOBurnRateWarning
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[6h]))
            /
            sum(rate(http_requests_total[6h]))
          ) > 6 * (1 - 0.999)
        for: 30m
        labels:
          severity: warning
          slo: "99.9%"
        annotations:
          summary: "错误预算在持续消耗,6h burn rate > 6x"
          description: "建议减少发布并排查根因"
          
      # === 错误预算耗尽 ===
      - alert: SLOBurnRateExhausted
        expr: |
          (
            1 - (
              sum(rate(http_requests_total{status!~"5.."}[30d]))
              /
              sum(rate(http_requests_total[30d]))
            )
          ) > (1 - 0.999)
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "30天错误预算已耗尽!"
          description: "当前 30d 可用性低于 SLO=99.9%,立即冻结发布"

5. SLO Dashboard 设计

5.1 Grafana Dashboard 布局

┌─────────────────────────────────────────────────────┐
│  SLO Overview — Service: api-gateway                │
├──────────────┬──────────────┬──────────────────────┤
│  可用性 SLO   │  延迟 SLO     │  错误预算            │
│  99.95% ✅    │  P99<300ms ✅ │  剩余 76% 🟡         │
│  (本月 99.97%)│  (本月 287ms) │                      │
├──────────────┴──────────────┴──────────────────────┤
│                                                     │
│  📈 可用性趋势(30天)                                │
│  99.98% ┤                                    ╭─     │
│  99.95% ┤──────────────── SLO ─────────────────     │
│  99.90% ┤      ╲        ╱                           │
│         └────────────────────────────               │
│                                                     │
│  📊 错误预算燃烧仪表盘                                 │
│  [████████████░░░░] 76% 剩余 (8.7h / 36h)           │
│                                                     │
│  🔥 Burn Rate(当前)                                 │
│  1h: 0.3x 🟢  |  6h: 0.5x 🟢  |  24h: 1.2x 🟡      │
│                                                     │
│  📋 按 Endpoint 的可用性                              │
│  /api/users    ████████████ 99.98% ✅               │
│  /api/orders   ██████████░░ 99.91% ⚠️ (低于 SLO)    │
│  /api/search   ████████████ 99.99% ✅               │
│                                                     │
│  📜 最近 SLO 事件                                   │
│  05-21 14:30  Error budget < 20% → 冻结非紧急发布    │
│  05-20 09:15  SLO burn rate warning (6h: 8.2x)      │
│  05-18 22:00  P99 latency spike → 自动回滚           │
└─────────────────────────────────────────────────────┘

5.2 Grafana 面板 PromQL

# === 错误预算剩余(Gauge 面板)===
1 - (
  (1 - (
    sum(rate(http_requests_total{status!~"5.."}[30d]))
    /
    sum(rate(http_requests_total[30d]))
  ))
  /
  (1 - 0.999)
)

# === 月度可用性趋势(Time Series)===
# 按天聚合,看趋势
sum(rate(http_requests_total{status!~"5.."}[1d]))
  /
sum(rate(http_requests_total[1d]))

# === 按 endpoint 可用性(Table)===
sum(rate(http_requests_total{status!~"5.."}[7d])) by (path)
  /
sum(rate(http_requests_total[7d])) by (path)

# === Burn Rate Multi-window(Stat)===
# 1h burn rate
(
  sum(rate(http_requests_total{status=~"5.."}[1h]))
  /
  sum(rate(http_requests_total[1h]))
) / (1 - 0.999)

# 6h burn rate
(
  sum(rate(http_requests_total{status=~"5.."}[6h]))
  /
  sum(rate(http_requests_total[6h]))
) / (1 - 0.999)

6. 实战:为已有服务建立 SLO 体系

步骤

# 第1步:选服务(从最重要的开始)
# → 先做核心 API,再做内部服务

# 第2步:收集 30 天历史数据
# 用 Prometheus recording rules 预计算 SLI
# recording-rules/slo-slis.yaml
groups:
  - name: slo_recording
    interval: 30s
    rules:
      - record: sli:http_availability:ratio_rate5m
        expr: |
          sum(rate(http_requests_total{status!~"5.."}[5m]))
            /
          sum(rate(http_requests_total[5m]))
          
      - record: sli:http_latency_p99:quantile5m
        expr: |
          histogram_quantile(0.99,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# 第3步:计算 SLO 基线
# 看 30 天 P50/P95/P99 → 设定合理目标(通常 P99 的 1.5x-2x)

# 第4步:配置告警
# 先用 warning 级别跑 2 周,调阈值,再升级到 critical

# 第5步:建立 Dashboard
# SLO 总览 + endpoint 明细 + 错误预算仪表盘

# 第6步:建立流程
# 月度 SLO 评审会 → 分析预算消耗原因 → 驱动可靠性项目

实用脚本:快速 SLO 健康检查

#!/bin/bash
# slo-health-check.sh — 快速检查所有 SLO 状态

PROM_URL="${PROMETHEUS_URL:-http://localhost:9090}"
SLO_TARGET="${SLO_TARGET:-0.999}"

echo "=== SLO Health Check ==="
echo "Target: $(echo "$SLO_TARGET * 100" | bc)%"
echo ""

# 30天可用性
availability=$(curl -s "$PROM_URL/api/v1/query" \
  --data-urlencode 'query=sum(rate(http_requests_total{status!~"5.."}[30d])) / sum(rate(http_requests_total[30d]))' \
  | jq -r '.data.result[0].value[1]')

echo "30d Availability: $(echo "$availability * 100" | bc -l | xargs printf '%.3f')%%"

# 错误预算剩余
budget=$(echo "1 - (1 - $availability) / (1 - $SLO_TARGET)" | bc -l)
budget_pct=$(echo "$budget * 100" | bc -l | xargs printf '%.1f')
echo "Error Budget: ${budget_pct}%"

# 判定
if (( $(echo "$budget < 0" | bc -l) )); then
    echo "⚠️  CRITICAL: 错误预算已耗尽!"
elif (( $(echo "$budget < 0.2" | bc -l) )); then
    echo "⚠️  WARNING: 错误预算 < 20%"
elif (( $(echo "$budget < 0.5" | bc -l) )); then
    echo "🟡  CAUTION: 错误预算 < 50%"
else
    echo "🟢  HEALTHY: 错误预算充足"
fi

7. 常见故障模式与 SLO 响应

故障场景SLO 表现响应
单机故障短暂下跌,预算轻微消耗自愈/自动替换,无需人工介入
发布引入回归错误率陡增,6h burn rate > 10x立即回滚,事后复盘
流量突发延迟上涨但可用性正常扩容,评估是否需要调整延迟 SLO
依赖服务故障可用性大幅下降熔断+降级,更新 SLO(区分整体 vs 部分故障)
数据库慢查询P99 延迟恶化优化查询,评估是否需要拆分延迟 SLO