25-指标栈
指标(Metrics)是可观测性三支柱中成本最低、查询最快、最适合告警的信号。本章覆盖 Prometheus 的抓取模型与服务发现、PromQL 常用查询、Grafana 看板设计、Alertmanager 告警路由与降噪,以及从可观测性架构师培养教材提取的能力地图。
能力地图(M1-M6)#
可观测性架构师的能力链分六层,指标栈对应其中的核心环节:
| 模块 | 主题 | 一句话 |
|---|---|---|
| M1 | 遥测信号 | 原材料:指标 / 日志 / 链路追踪 |
| M2 | 采集与管道 | 信号怎么从代码流到存储 |
| M3 | 存储与查询 | 时序库原理、查询语言 |
| M4 | 告警与 SRE | SLO、症状告警、去重路由 |
| M5 | 可视化与排障 | 仪表盘设计、黄金信号、USE/RED |
| M6 | 架构与治理 | 高可用、联邦、成本、配置即代码 |
Monitoring vs Observability:监控回答"你提前想到的问题"(known unknowns),可观测性让你回答"没提前想到的问题"(unknown unknowns)。指标告诉你"发烧了",不告诉你"为什么发烧"——那是日志和链路的活。
Metrics 的关键性质#
成本与请求量无关:不管接口被调 10 次还是 1 亿次,一条序列每个抓取周期(如 15 秒)只存一个数。所以指标极廉价,可全量长期保留。
基数 cardinality(头号杀手):基数 = 不同 label 组合的数量 = 时间序列的条数。
status(5种) ×method(4种) ×service(50个) = 1000 条序列,没问题。- 把
user_id(百万级)或request_id(每次都不同)塞进 label → 序列爆炸到千万级 → Prometheus 内存 OOM。
架构师铁律:label 只放低基数维度(环境、服务、接口、状态码);高基数的东西(用户、订单号、trace_id)交给日志和链路。
Prometheus 抓取模型#
Prometheus 是 pull 模型——由 Prometheus 主动去拉取 target 的 /metrics 端点,而不是 target 主动推送。
Pull 模型的优势:监控端掌控频率;抓不到 = 天然知道 target 死了;target 无状态。
注:Prometheus 3.0(2024-11)起内置原生 OTLP 接收端点,可直接接收 OpenTelemetry 推送来的指标(短生命周期任务、Serverless 等 pull 不到的场景);但抓取(pull)仍是主模型。
核心配置:
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# 只抓取有 prometheus.io/scrape: "true" annotation 的 Pod
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
# 自定义 metrics path
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# 自定义端口
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: '([^:]+)(?::\d+)?;(\d+)'
replacement: '$1:$2'
target_label: __address__
# Pod labels 转 Prometheus 标签
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: podPod 只需加 annotation 即可被自动发现:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"ServiceMonitor(Prometheus Operator)#
如果使用 kube-prometheus-stack,可以用 ServiceMonitor CRD 提供更高层抽象:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-service
labels:
release: prometheus
spec:
namespaceSelector:
any: true
selector:
matchLabels:
app: my-service
endpoints:
- port: http-metrics
path: /metrics
interval: 30s不需要改 Prometheus 配置文件,Operator 自动处理服务发现。
PromQL 常用查询#
rate(计数器增长率)#
# 过去 5 分钟每秒请求量
rate(http_requests_total{service="api"}[5m])
# 按状态码分组
sum by (status) (rate(http_requests_total[5m]))histogram_quantile(分位数)#
# P99 延迟
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
# 按服务分组 P99
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)错误率#
# 5xx 错误率
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))常用聚合#
# 每个服务的 QPS
sum by (service) (rate(http_requests_total[5m]))
# Top 10 慢接口
topk(10,
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, path)
)
)Grafana 看板设计#
Grafana 只是展示层——本身不存数据,连接各 data source 查询并画图。"Grafana 挂了"不等于"数据丢了"。
设计原则#
- 别堆图:一屏 50 个图 = 没有重点 = 出事时找不到关键。
- 分层:概览盘(系统健康,给值班第一眼)→ 服务盘(单服务详情)→ 实例盘(单机排障)。从上往下下钻。
- 每个图要能回答一个具体问题,不是"因为有这个指标所以画上"。
三套黄金框架#
| 框架 | 面向 | 指标 |
|---|---|---|
| Four Golden Signals | 服务 | Latency、Traffic、Errors、Saturation |
| RED | 请求驱动服务 | Rate、Errors、Duration |
| USE | 资源(CPU/磁盘/队列) | Utilization、Saturation、Errors |
实践:给新服务设计监控,先套 RED(服务层)+ USE(资源层),黄金信号兜底。
Alertmanager:告警分拣中心#
Prometheus 只负责判定"该告警了",怎么处理告警是 Alertmanager 的活。
四个核心能力#
- 分组 grouping:同一类告警(如一次发布挂了 50 个 pod)合并成一条通知。
- 去重 dedup:多个 Prometheus 副本发的同一告警合并成一条。
- 抑制 inhibition:高级告警触发时压住低级的——整个集群挂了,别再报每个 pod 挂了。
- 路由 routing:按 label(团队/严重级)把告警发到不同 on-call 群。
路由树配置#
route:
receiver: 'default-ops'
group_by: ['cluster', 'alertname', 'namespace']
group_wait: 30s # 第一批告警等待 30s 聚合
group_interval: 5m # 有新告警时等 5 分钟再发
repeat_interval: 4h # 未恢复每 4h 重复通知
routes:
# Critical → PagerDuty + 钉钉
- receiver: 'pagerduty-critical'
matchers: [severity = "critical"]
group_wait: 10s
repeat_interval: 30m
continue: true # 继续匹配后续路由
- receiver: 'dingtalk-critical'
matchers: [severity = "critical"]
# Warning → 钉钉普通群
- receiver: 'dingtalk-warning'
matchers: [severity = "warning"]
repeat_interval: 8h抑制规则#
inhibit_rules:
# Critical 触发时,抑制同组的 warning
- source_matchers: [severity = "critical"]
target_matchers: [severity = "warning"]
equal: ['alertname', 'cluster', 'namespace']
# 节点 NotReady 时,抑制该节点上的 Pod 告警
- source_matchers: [alertname = "KubeNodeNotReady"]
target_matchers: [alertname =~ "KubePodCrashLooping|KubePodNotReady"]
equal: ['node', 'cluster']死人开关(Dead Man's Switch)#
一条永远在触发的告警,反向用——如果没收到它,说明整条告警链路(Prometheus→Alertmanager→通知)自己挂了。每套监控都必须有。
SLO 与错误预算#
- SLI(服务等级指标):用户体验的量化值,如请求成功率、P99 延迟。
- SLO(服务等级目标):给 SLI 定的目标,如"成功率 ≥ 99.9%/月"。
- Error Budget(错误预算) = 1 − SLO = 0.1%。允许失败的额度。
有了 SLO,才有客观标准回答"什么时候该告警、什么值得半夜爬起来"。没有 SLO,所有阈值都是拍脑袋。
多窗口多燃烧率告警#
基于错误预算的高级告警法:
- 快速烧预算(如 1 小时烧掉一个月额度的 2%)→ 紧急 page
- 慢速烧→ 工单提醒
兼顾"快响应重大故障"和"不为小波动半夜叫人"。
高可用与长期存储#
Prometheus 单机没有原生 HA。标准做法:跑两个相同配置的副本同时抓,Alertmanager 去重。
多集群全局视图 + 长期存储方案:
| 方案 | 特点 | 适用 |
|---|---|---|
| Thanos | sidecar + 对象存储 + 全局 Query | 生态成熟 |
| Mimir(Grafana) | 水平扩展多租户后端 | 大规模 |
| VictoriaMetrics | 资源省、部署简单、性能高 | 中小规模 |
本地 15 天不够看季度趋势 → 通过 remote write 把数据吐到长期存储,老数据降采样(15 秒一个点聚合成 5 分钟一个点)省 90% 空间。
成本治理#
可观测性成本能轻松吃掉基础设施账单的两位数百分比。四个抓手(按性价比排序):
- 控基数 cardinality:定期扫高基数 label,性价比最高的优化。
- 采样:链路和高频日志该采就采。
- 分层保留 retention:热(贵、快、短)/ 温 / 冷(对象存储、便宜、长)。
- 降采样:老指标降精度长留。
配置即代码#
所有告警规则、记录规则、仪表盘、Collector 配置全进 Git,经 CI/CD 部署。好处:版本化、可 review、可回滚、可审计。"核心告警手动 apply 不在 Git"是典型的治理债。
小结#
指标栈是可观测性中 ROI 最高的部分——采集成本低、查询快、告警成熟。Prometheus 的 pull 模型 + kubernetes_sd_configs 是 K8s 监控的标准答案。PromQL 的 rate 和 histogram_quantile 是最常用的两个算子。Grafana 看板设计要分层、要能回答具体问题,RED + USE 是起步框架。Alertmanager 的分组/去重/抑制/路由是治"告警疲劳"的四件套,配合 SLO 错误预算让告警有客观锚。核心治理底线:配置进 Git + 统一告警分拣 + 死人开关 + 控基数。