SLI/SLO/SLA

概述

参考:

概念英文全称核心含义简单比喻(以餐厅为例)
SLIService Level Indicator服务级别指标:实际测量出的定量数据。厨房测量出的“实际平均上菜时间是25分钟”。
SLOService Level Objective服务级别目标:团队内部期望达到的技术线。店长给厨房定的目标:“95% 的菜品必须在30分钟内上桌”。
SLAService Level Agreement服务级别协议:对客户的法律/商业承诺,没达到要赔钱。菜单上的承诺:“上菜超过40分钟,本单免费”。

SLI 是 SLO 的基础,SLO 是 SLA 的基础。先定 Indicator(指标),再定该指标的 Objective(目标),根据目标做出 Agreement(协议/承诺)。

CPU 利用率单机阈值属于"病因指标"(cause metric),不是"症状指标"(symptom metric)。SRE 的建议是:告警应该建立在症状上——比如请求延迟升高、cgroup throttling、队列积压 —— 而不是直接对 CPU 数值本身设阈值。CPU 高不一定有实际影响(可能只是在跑批处理但服务正常),CPU 不高也不代表没问题(可能是被限流限制住了)。

告警治理:如何把每天 500+ 条告警降到 50 条

原文:公众号 - 运维开发故事,告警治理:如何把每天 500+ 条告警降到 50 条

“告警邮件比我还积极,每天早上准时问候,但 99% 都是噪音!”

这句话来自一位 SRE 团队负责人的吐槽,但它是整个行业的缩影。Gartner 2025 年的报告指出: 85% 的运维告警不需要任何人工干预 。PagerDuty 的统计更残酷——一个中等规模的 Kubernetes 集群,每周产生 500+ 条告警是常态。

更讽刺的是:团队搭建了完善的告警系统来"捕获每一个问题",结果噪音淹没了一切,真正的故障反而被漏掉了。

这篇文章不讲理论,只讲实战。我会用两个真实案例(一个从 400 条/夜降到 8 条,一个从 1200 条/天降到 80 条),加上完整的 Prometheus + Alertmanager 配置,给你一套可以直接落地的告警治理方案。

前置阅读:本文是 SLO 系列的第四篇。前三篇讲了 SLO 体系全貌、Sloth 工具和 Pyrra 工具。这篇解决一个更基础的问题——在引入 SLO 之前或之后,怎么把每天 500+ 条告警的噪音降下来。


1 告警疲劳:不是抱怨,是系统性故障

先认清现实:告警疲劳不是一个"体验问题",它是 系统性故障

三个让人坐不住的数字

  • 85% — 告警无需人工干预(Gartner 2025)
  • 44% — 告警从未被查看(BigPanda 2025)
  • #1 — 告警疲劳是 SRE 离职首因(DevOps Pulse 2025)
  • 4h+ — 告警疲劳下的平均 MTTR(对比正常 11 分钟)

告警疲劳的杀伤链是这样的:

告警太多 → 选择性忽略 → 真正的故障被淹没 → MTTR 暴增 → 团队失去信任 → 值班人员离职 → 剩余人员更累 → 更多告警被忽略

这是一个正反馈恶性循环。不打破它,再好的 SLO 体系、再先进的工具都没用。

为什么 Kubernetes 让告警疲劳更严重

Kubernetes 的动态特性放大了告警噪音,五个典型场景:

场景问题一个故障产生多少条告警
Pod 重启风暴一个 CrashLoopBackOff 配置错误,3 副本每 30 秒重启一次360 条/小时
HPA 扩缩容把正常弹性伸缩当成故障告警每次扩缩容 5-10 条
节点故障一个节点挂了,上面所有 Pod、所有依赖服务的告警全触发10-50 条
重复来源Prometheus + K8s Events + APM 同时告警同一个问题3-4 倍重复
发布窗口滚动更新期间 Pod 重启、健康检查失败、连接超时20-100 条/次发布

一个 3 副本的 CrashLoopBackOff 就能在一小时内产生 360 条告警——这不是 360 个故障,这是 一个故障在噪音中喊了 360 次


2 诊断:你的告警系统病在哪里

治理之前先诊断。你需要回答三个问题:告警从哪来?谁在看?有没有用?

第一步:告警审计——导出全量告警规则

把 Prometheus 里所有的告警规则导出来,按以下四个维度分类:

分类示例处置策略
可行动数据库连接池耗尽保留,优化阈值
信息性Pod 重启了一次转为 Dashboard 指标
重复Prometheus 和 K8s Events 对同一个问题各告一次去重,保留权威来源
过期已下线服务的告警规则直接删除

用这个 PromQL 查询过去 30 天触发次数最多的告警:

# 查询过去 30 天触发最多的告警 Top 20
topk(20,
  sum by (alertname) (
    rate(ALERTS{alertstate="firing"}[30d])
  )
)
# 按触发次数排序,优先处理 Top 20

大多数团队做完审计后发现: 30-40% 的告警规则可以立即删除或转为 Dashboard 指标

第二步:告警分类统计

抽样分析一周的告警数据,按来源和性质分类。如下:

告警类型占比典型示例是否需要人工干预
重复告警45%同一实例同一规则反复触发否(去重即可)
短暂抖动25%CPU 突刺,几分钟自动恢复否(加 for 持续时间)
发布/维护窗口15%滚动更新期间的健康检查失败否(静默窗口)
无主告警10%没有负责人的旧规则否(删除或归属)
真正需要处理5%服务不可用、错误率飙升

看到了吗? 95% 的告警是噪音,只有 5% 是信号 。这就是为什么 500+ 条告警和 50 条告警在信息量上没有区别——因为真正有价值的只有那 25 条。

第三步:告警健康度评估

用以下五个指标评估你的告警系统健康度:

指标计算方式健康值危险值
告警确认率被人工确认的告警 / 总告警数>80%<50%
误报率确认后无需处理的告警 / 总告警数<10%>40%
重复率同告警名 24h 内触发 >3 次的占比<5%>30%
平均响应时间告警触发到人工确认的中位数<5 分钟>15 分钟
沉默率告警群内 30 天无任何回复的占比<10%>50%

铁律

:如果告警确认率低于 50%,说明你的告警系统已经失去了团队的信任。这不是需要"调优阈值"的问题,是需要推倒重建的问题。


3 第一刀:砍掉 60% 的垃圾告警

诊断完了,开始动刀。这一步不需要任何技术含量,只需要 狠心

告警无情日(Alert Ruthlessness Day)

来自一个英国金融服务团队的真实操作——他们在治理的第一天,运行了一条 SQL 查询:

-- 查找过去 30 天触发超过 100 次但从未导致客户影响的告警
SELECT alertname, COUNT(*) AS fires
FROM prometheus_alerts
WHERE fired_at > now() - INTERVAL '30 days'
GROUP BY alertname
HAVING COUNT(*) > 100
ORDER BY fires DESC;

结果:312 条告警在过去 30 天触发超过 100 次, 没有一条 导致过客户影响的事件。

他们的操作: 第一天就删了 287 条告警规则

判断标准

:如果一条告警在过去 12 个月内从未导致过客户故障,它就不是告警——它是 Dashboard 指标。

五类必须砍掉的告警

1. CPU/内存使用率阈值告警

# ❌ 删掉这种告警
- alert: HighCPU
  expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
  for: 1m
  labels:
    severity: warning

# ✅ CPU 使用率是指标,不是告警
# 如果 CPU 高导致请求延迟增加,告警延迟而不是 CPU
# 如果 CPU 高导致 OOM,告警 OOM 事件而不是 CPU

CPU 80% 在某些服务上是正常状态(比如计算密集型任务),在另一些服务上是危险信号。直接告 CPU 使用率,无法区分这两种情况。 告用户感知的症状,不告内部资源指标

2. Pod 重启告警

# ❌ 删掉——Pod 重启是 K8s 自愈机制,不是故障
- alert: PodRestarted
  expr: increase(kube_pod_container_status_restarts_total[1h]) > 0

# ✅ 只告 CrashLoopBackOff——持续重启才是问题
- alert: PodCrashLooping
  expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "Pod {{ $labels.pod }} 在过去1小时内重启超过5次"
    runbook_url: "https://runbook.example.com/crashloop"

3. HPA 扩缩容告警

# ❌ 删掉——HPA 扩缩容是设计行为,不是故障
- alert: HPA scaled
  expr: changes(kube_hpa_status_current_replicas[1h]) > 0

# ✅ 只告 HPA 无法扩容——真正的问题
- alert: HPAScalingLimited
  expr: kube_hpa_status_condition{condition="ScalingLimited", status="true"}
  for: 15m
  labels:
    severity: warning

4. 磁盘空间低告警(但阈值不合理)

# ❌ 删掉——阈值太低,频繁误报
- alert: DiskSpaceLow
  expr: 100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100) > 70
  for: 1m

# ✅ 用预测告警——给运维预留处理时间
- alert: DiskWillFillIn24h
  expr: predict_linear(node_filesystem_avail_bytes[1h], 24 * 3600) < 0
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "磁盘 {{ $labels.device }} 将在24小时内写满"

predict_linear 是 Prometheus 的线性预测函数,基于历史趋势预测未来值。比固定阈值告警好在:它不会在磁盘使用率 71% 时告警(可能一周都不会写满),只在 真正即将写满时 才告警。

5. 无 Runbook 的告警

如果一条告警没有对应的 Runbook(处理手册),值班人员看到后不知道该做什么——这种告警等于没有告警。规则很简单:

没有 Runbook 的告警 = 没有告警

。要么补 Runbook,要么删掉。


4 从阈值告警到 SLO 燃烧率告警

砍完垃圾告警,下一步是把剩余的阈值告警 升级为 SLO 燃烧率告警 。这是告警治理的核心转型。

阈值告警 vs SLO 告警的本质区别

维度阈值告警(噪音源)SLO 燃烧率告警(信号源)
判定逻辑指标 > 阈值SLO 违规 → 用户影响
响应心理“又是 CPU 告警,忽略吧”“错误预算在烧,用户受影响”
结果导向关注组件状态关注错误预算消耗速度
信噪比极低(False Positive 频发)极高(每条都必须处理)
维护成本每个服务手动调阈值一套规则适用于所有服务

实战:用燃烧率告警替换 47 条旧告警

一个真实案例:某团队的支付服务 payment-api 有 47 条阈值告警(CPU 高、内存高、5xx 多、延迟高、连接数多……),全部替换为 2 条 SLO 燃烧率告警:

# payment-api 的 SLO 定义:可用性 99.9%
# 错误预算 = 1 - 99.9% = 0.1%
# 月预算 = 43200 秒 × 0.1% = 43.2 秒(允许宕机时间)

# Page 级告警:2% 预算在 1 小时内烧完(快燃烧)
- alert: PaymentApiSLOBurnRateFast
  expr: |
    (
      sum(rate(http_requests_total{job="payment-api", status=~"5.."}[1h]))
      /
      sum(rate(http_requests_total{job="payment-api"}[1h]))
    ) > (14.4 * 0.001)
    and
    (
      sum(rate(http_requests_total{job="payment-api", status=~"5.."}[5m]))
      /
      sum(rate(http_requests_total{job="payment-api"}[5m]))
    ) > (14.4 * 0.001)
  for: 2m
  labels:
    severity: page
    slo: payment-api-availability
  annotations:
    summary: "payment-api 错误预算快速燃烧"
    description: "2% 的月错误预算在1小时内消耗完毕,按当前速度3小时内预算耗尽"
    runbook_url: "https://runbook.example.com/payment-slo"

# Ticket 级告警:10% 预算在 3 天内烧完(慢燃烧)
- alert: PaymentApiSLOBurnRateSlow
  expr: |
    (
      sum(rate(http_requests_total{job="payment-api", status=~"5.."}[3d]))
      /
      sum(rate(http_requests_total{job="payment-api"}[3d]))
    ) > (1 * 0.001)
    and
    (
      sum(rate(http_requests_total{job="payment-api", status=~"5.."}[6h]))
      /
      sum(rate(http_requests_total{job="payment-api"}[6h]))
    ) > (1 * 0.001)
  for: 15m
  labels:
    severity: ticket
    slo: payment-api-availability
  annotations:
    summary: "payment-api 错误预算慢性消耗"
    description: "10% 的月错误预算在3天内消耗完毕,需要排查慢性问题"
    runbook_url: "https://runbook.example.com/payment-slo"

双窗口 AND 逻辑的含义(详见 SLO 系列第一篇):

  • 长窗口(1h/3d)判断"错误预算会不会烧光"——过滤瞬时抖动
  • 短窗口(5m/6h)判断"是否正在烧"——过滤慢性病漏报
  • 两个条件同时满足才告警——既不误报也不漏报

47 条阈值告警 → 2 条 SLO 告警。这就是为什么 SLO 系列的前三篇文章如此重要——它们给了你 替代阈值告警的完整方法论和工具链

💡 不想手写燃烧率规则? 用 Sloth 或 Pyrra 自动生成。上一篇 Pyrra 文章里展示了完整的 CRD 配置,一条 SLO 定义自动生成 19 条 Prometheus 规则,包括 4 级燃烧率告警。


5 Alertmanager 降噪三板斧

SLO 告警解决了"告什么"的问题,Alertmanager 解决"怎么告"的问题。三板斧: 分组、抑制、静默

第一板斧:分组(Grouping)——把 N 条告警合并成 1 条

一个数据库挂了,依赖它的 50 个服务全部告警。没有分组,值班人员会收到 50 条通知。有分组,只收 1 条。

# alertmanager.yml
route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s        # 首次告警等待 30s,攒齐同一组的告警
  group_interval: 5m     # 同组新告警的合并间隔
  repeat_interval: 4h    # 重复提醒间隔
  receiver: 'oncall-slack'

  routes:
    # Critical 级别:立即通知,短间隔重发
    - matchers:
        - severity="critical"
      group_wait: 10s
      group_interval: 1m
      repeat_interval: 1h
      receiver: 'oncall-pagerduty'

    # Warning 级别:发 Slack,工作时间通知
    - matchers:
        - severity="warning"
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 4h
      receiver: 'oncall-slack'

    # Info 级别:只发邮件日报
    - matchers:
        - severity="info"
      group_wait: 5m
      group_interval: 1h
      repeat_interval: 24h
      receiver: 'daily-email'

group_by 的选择很关键:

  • ['alertname'] — 最粗粒度,同名的所有告警合为一条
  • ['alertname', 'cluster', 'service'] — 推荐,按服务维度分组
  • ['alertname', 'instance'] — 最细粒度,几乎不分组(不推荐)

第二板斧:抑制(Inhibition)——根因告警抑制症状告警

数据库挂了,不该再收到"服务 A 连接超时"、“服务 B 查询失败”、“服务 C 响应慢"这些症状告警——它们的根因都是数据库挂了。

# alertmanager.yml — 抑制规则
inhibit_rules:
  # 规则1:数据库挂了,抑制所有依赖该数据库的服务告警
  - source_matchers:
      - alertname="DatabaseDown"
    target_matchers:
      - alertname="ServiceHighErrorRate"
    equal: ['cluster']

  # 规则2:节点挂了,抑制该节点上所有 Pod 的告警
  - source_matchers:
      - alertname="NodeDown"
    target_matchers:
      - alertname=~"PodCrashLooping|PodNotReady|ContainerOOMKilled"
    equal: ['node']

  # 规则3:Critical 告警抑制同服务的 Warning 告警
  - source_matchers:
      - severity="critical"
    target_matchers:
      - severity="warning"
    equal: ['alertname', 'cluster', 'service']

  # 规则4:集群不可达,抑制所有该集群的告警
  - source_matchers:
      - alertname="ClusterUnreachable"
    target_matchers:
      - cluster="{{ $labels.cluster }}"
    equal: ['cluster']

抑制规则的设计原则:

  1. 根因在前:source<em>matchers 是根因告警,target</em>matchers 是症状告警
  2. 维度对齐:equal 字段确保只抑制同一维度的告警(不会把 A 集群的数据库告警抑制 B 集群的服务告警)
  3. 宁少勿多:抑制规则太激进会导致漏报,从最明确的根因开始配置

第三板斧:静默(Silence)——维护窗口自动静默

发布、巡检、扩缩容期间的告警,90% 是噪音。但手动静默容易忘记恢复——更危险的是,静默了真实故障。

正确做法: 和发布系统联动,自动创建和回收静默规则

# 发布系统在滚动更新前调用 Alertmanager API 创建静默
curl -XPOST http://alertmanager:9093/api/v2/silences \
  -H 'Content-Type: application/json' \
  -d '{
    "matchers": [
      {
        "name": "service",
        "value": "payment-api",
        "isRegex": false
      },
      {
        "name": "alertname",
        "value": "PodNotReady|ContainerOOMKilled|ProbeFailed",
        "isRegex": true
      }
    ],
    "startsAt": "2026-07-27T10:00:00Z",
    "endsAt": "2026-07-27T10:15:00Z",
    "createdBy": "deploy-bot",
    "comment": "payment-api 滚动发布,预计15分钟"
  }'

# 发布完成后,静默规则自动过期(endsAt 到期)
# 如果发布提前完成,主动删除静默规则
curl -XDELETE http://alertmanager:9093/api/v2/silence/{silence_id}

⚠️ 静默的陷阱不要静默 SLO 燃烧率告警! 发布期间如果错误率真的飙升,燃烧率告警必须能触达值班人员。静默只适用于基础设施层面的噪音告警(Pod 重启、健康检查失败等),SLO 层面的告警永远不能被静默。


6 告警分级与路由:不是所有告警都要叫醒人

四级告警体系

级别通知方式响应时间示例
CriticalPagerDuty / 电话立即(<5 分钟)服务完全不可用、SLO 快燃烧
WarningSlack / 钉钉工作时间内(<1 小时)SLO 慢燃烧、磁盘将满
Info邮件日报下一个工作日接近阈值、趋势异常
Silent仅记录到 Dashboard不通知Pod 重启、HPA 扩缩容

关键原则: 能发 Slack 解决的不打电话,能发邮件解决的不发 Slack,能看 Dashboard 的不发邮件

按团队路由

# alertmanager.yml — 按团队路由
route:
  receiver: 'default-slack'
  routes:
    # 支付团队
    - matchers:
        - team="payment"
      receiver: 'payment-slack'
      routes:
        - matchers:
            - severity="critical"
          receiver: 'payment-pagerduty'

    # 基础设施团队
    - matchers:
        - team="infra"
      receiver: 'infra-slack'
      routes:
        - matchers:
            - severity="critical"
          receiver: 'infra-pagerduty'

    # 业务团队
    - matchers:
        - team="business"
      receiver: 'business-slack'

每个告警规则必须带 team 标签。没有 team 标签的告警规则,CI 流水线直接拒绝合入。


7 Runbook 质量门禁:没有手册的告警等于没有告警

告警治理中最容易被忽视的一环:值班人员收到告警后 不知道该做什么

Runbook 必须回答的四个问题

  1. 客户现在受影响了吗?(Yes/No,附查询命令)
  2. 一键打开 Dashboard(直接链接)
  3. 如何验证和修复?(精确的命令,不是"检查一下日志”)
  4. 升级路径(找不到原因时该找谁)

告警规则模板(强制 Runbook)

# 每条告警必须包含以下 annotations
- alert: PaymentApiHighErrorRate
  expr: |
    sum(rate(http_requests_total{job="payment-api", status=~"5.."}[5m]))
    / sum(rate(http_requests_total{job="payment-api"}[5m])) > 0.01
  for: 5m
  labels:
    severity: page
    team: payment
    service: payment-api
  annotations:
    summary: "payment-api 5xx 错误率超过 1%"
    description: "当前错误率 {{ $value | humanizePercentage }},持续 5 分钟"
    runbook_url: "https://runbook.example.com/payment-5xx"
    dashboard_url: "https://grafana.example.com/d/payment-api"
    quick_check: "kubectl logs -l app=payment-api --tail=50 | grep -i error"
    escalate_to: "#payment-oncall"

CI 流水线门禁检查

# scripts/validate-alerts.py — CI 检查脚本
import yaml, sys

def validate_alerts(rules_file):
with open(rules_file) as f:
        rules = yaml.safe_load(f)

    errors = []
    required_labels = ['severity', 'team', 'service']
    required_annotations = ['summary', 'runbook_url', 'dashboard_url']

for group in rules.get('groups', []):
for rule in group.get('rules', []):
if'alert'notin rule:
continue

            name = rule['alert']

# 检查必需 labels
for label in required_labels:
if label notin rule.get('labels', {}):
                    errors.append(f"Alert '{name}' missing label: {label}")

# 检查必需 annotations
for ann in required_annotations:
if ann notin rule.get('annotations', {}):
                    errors.append(f"Alert '{name}' missing annotation: {ann}")

# 检查 for 持续时间
if'for'notin rule:
                errors.append(f"Alert '{name}' missing 'for' duration")

if errors:
for e in errors:
            print(f"  X {e}")
        sys.exit(1)
else:
        print("  All alerts validated")

validate_alerts(sys.argv[1])
# .gitlab-ci.yml 或 GitHub Actions
validate-alerts:
  stage: test
  script:
    - python scripts/validate-alerts.py alerts/rules.yaml
  # 不通过则阻止合入

💡 Runbook 自动过期机制 :Runbook 超过 90 天未更新 → 告警自动降级为 Silent(只记录 Dashboard,不通知)。这迫使团队保持 Runbook 的时效性。

8 30 天告警治理冲刺计划

理论讲完了,给你一个可以直接执行的 30 天计划。分四个阶段,每个阶段有明确的交付物和验收标准。

Week 1:诊断与清剿

  • Day 1-2:导出全量告警规则,按四分类法审计(可行动/信息性/重复/过期)
  • Day 3:运行高频告警查询(30 天触发 >100 次的),标记为待删除
  • Day 4-5:删除过期和无主告警,将信息性告警转为 Dashboard 指标
  • Day 6-7:为剩余告警补齐 team、severity、runbook_url 标签
  • 验收标准:告警规则数量减少 40%+,所有保留的告警都有 team 标签

Week 2:Alertmanager 降噪

  • Day 8-9:设计 group_by 策略,配置 group_wait / group_interval / repeat_interval
  • Day 10-11:梳理服务依赖关系,配置抑制规则(根因→症状)
  • Day 12:和发布系统对接,实现维护窗口自动静默
  • Day 13-14:配置按 severity 和 team 的路由规则
  • 验收标准:同一根因的告警合并为 1 条通知,维护窗口期间噪音告警为 0

Week 3:SLO 告警转型

  • Day 15-16:为核心服务定义 SLI(可用性 + 延迟),参考 SLO 系列第一篇
  • Day 17-18:用 Sloth 或 Pyrra 生成燃烧率告警规则
  • Day 19-20:新旧告警并行运行 48 小时,对比效果
  • Day 21:确认无漏报后,删除旧的阈值告警规则
  • 验收标准:核心服务的告警全部为 SLO 燃烧率告警,旧阈值告警归档

Week 4:度量与闭环

  • Day 22-23:搭建告警健康度 Grafana Dashboard(五组 PromQL)
  • Day 24:配置 CI 门禁检查(Runbook、labels、annotations 强制校验)
  • Day 25-26:建立每周告警复盘机制(Top 20 高频告警评审)
  • Day 27-28:配置告警反馈闭环(每条 Page 必须回答:是否可行动 + 如何永久消除)
  • Day 29-30:撰写治理报告,对比 30 天前后数据
  • 验收标准:告警总量下降 80%+,误报率 <10%,MTTR 下降 50%+

9 避坑指南:六个真实踩坑场景

坑 1:一刀切删除所有阈值告警

  • 症状:听说 SLO 好就全删阈值告警,结果某些没有 SLI 的基础设施组件(如证书过期、DNS 解析)漏报了
  • 解法:SLO 告警覆盖请求类服务,基础设施告警用预测性阈值(predict_linear),不要一刀切。分类处理:请求类 → SLO 告警;资源类 → 预测告警;事件类 → 日志告警

坑 2:抑制规则太激进导致漏报

  • 症状:配了"节点挂了抑制所有该节点的告警",结果节点上某个独立服务的 OOM 被静默了 2 小时
  • 解法:抑制规则只抑制确定性的因果关系(数据库挂→连接超时),不确定的关系不要配抑制。宁可多收几条告警,也不要漏报

坑 3: for 持续时间设太短

  • 症状:for: 1m 的告警频繁触发又自动恢复,值班手机震个不停
  • 解法:通用建议——性能类告警 for: 5m-15m,可用性告警 for: 2m-5m,只有"服务完全不可用"这种 Critical 才用 for: 0-1m。配合 rate() 的窗口大小一起调(窗口通常取 for 的 4-5 倍)

坑 4:静默规则忘记回收

  • 症状:手动创建的静默规则,维护结束后忘记删除,导致后续真实故障被静默
  • 解法:静默规则必须设置 endsAt,不允许创建无过期时间的静默。和发布系统联动,自动创建+自动过期。定期检查过期但未删除的静默规则

坑 5:告警标签不一致导致分组失效

  • 症状:同一条告警因为 service 标签的值不一致(payment-api vs payment_api vs PaymentAPI),无法被正确分组和抑制
  • 解法:在 CI 流水线里做标签规范化检查。所有 service、team、cluster 标签的值必须是预定义枚举列表中的值,拒绝不在列表中的值合入

坑 6:只减量不提质

  • 症状:告警从 500 条降到 50 条了,但值班人员还是觉得告警没用——因为剩下的 50 条里有 30 条不知道该怎么处理
  • 解法:减量只是手段,提质才是目的。每条保留的告警必须满足三个条件:可检测(Detectable)、可行动(Actionable)、有影响(Impactful)。不满足的要么补 Runbook,要么降级为 Dashboard 指标

10 工具推荐:告警治理工具栈

工具用途推荐理由
Prometheus告警规则引擎原生支持 PromQL、recording rules、for 持续时间
Alertmanager告警路由与降噪分组、抑制、静默三板斧,K8s 生态标配
KarmaAlertmanager Web UI比原生 UI 强 10 倍,支持多 Alertmanager 聚合
Sloth / PyrraSLO 告警自动生成声明式定义 SLO,自动生成燃烧率告警规则
Grafana告警健康度 Dashboard可视化告警趋势、Top 高频告警、MTTR 等
PagerDuty / Opsgenie值班调度与升级排班管理、升级策略、SLA 跟踪
amtoolAlertmanager CLI命令行管理静默规则、测试路由配置

amtool 实用命令

# 查看当前所有 firing 告警
amtool --alertmanager.url=http://alertmanager:9093 alert query

# 测试告警路由(不实际发送)
amtool --alertmanager.url=http://alertmanager:9093 config routes test \
  --severity=critical --team=payment --service=payment-api

# 创建静默规则
amtool --alertmanager.url=http://alertmanager:9093 silence add \
  alertname=PodCrashLooping service=payment-api \
  --duration=15m \
  --comment="发布期间静默" \
  --author="deploy-bot"

# 查看所有静默规则
amtool --alertmanager.url=http://alertmanager:9093 silence query

# 删除静默规则
amtool --alertmanager.url=http://alertmanager:9093 silence expire <silence_id>

11 结语:告警治理是组织工程,不是技术工程

写到最后,必须说一个真相: 告警治理的难点从来不在技术,在组织和人

Alertmanager 的分组、抑制、静默配置,半天就能学会。Sloth 和 Pyrra 的 SLO 规则生成,一天就能上手。真正难的是:

  1. 让团队承认现状有问题——很多人已经把"每天 500 条告警"当成了常态,觉得"告警多说明系统在工作"。不,告警多说明监控系统在制造噪音。
  2. 让开发参与告警治理——告警不是 SRE 一个人的事。每条告警必须有 Runbook,Runbook 必须由最了解服务的开发来写。
  3. 建立持续治理的机制——不是治理一次就结束了。每周复盘 Top 20 高频告警,每月评估告警健康度,每季度审视告警策略。
  4. 把“减少告警”写进 KPI——如果团队的目标是"告警响应率 99%",那大家会拼命加告警。如果目标是"每周 Page 不超过 2 次",大家才会认真治理噪音。

告警治理的终极目标

:让每一条发出来的告警都值得被看见、值得被处理、能够被闭环。当值班人员重新信任告警系统,当凌晨 3 点的电话响起时第一反应是"有真正的故障"而不是"又来烦我"——你就成功了。

2026 年,告警疲劳不是"正常现象",是一种选择。500 条告警/天不是"系统复杂度的必然结果",是组织债务的复利。该还债了。


最后修改 July 30, 2026: add observe sli/slo/sla (bbe9078f)