概述

去年有个朋友跟我吐槽:他们公司上了 Prometheus、ELK、Jaeger 三套工具,运维团队 7 个人,每天光处理告警就耗掉 4 小时。某天凌晨支付服务 P0 故障,从告警触发到定位根因花了 42 分钟——根因是一个数据库连接池配置变更导致慢查询堆积,Jaeger 里有完整的 Trace,但没人看;ELK 里有慢查询日志,但没人搜;Prometheus 里有连接池指标的异常波动,但告警被淹没在 5000 条日常告警里。

问题出在哪?工具都上了,数据都采了,但三套系统各自为战,没有形成"可观测性"。这就像你给一辆车装了速度表、油表、温度计,但三个表盘分别放在后备箱、手套箱和引擎盖下面——仪表都在,但你开不了车。

可观测性(Observability)这个词来自控制理论,核心定义很简单:通过系统的外部输出推断系统内部状态的能力。说白了就是——不用进到系统里面去翻代码,光看数据就能回答"系统现在健不健康?出了问题在哪?为什么出问题?"

这篇文章不讲三支柱是什么(Metrics/Logs/Traces 满大街都有文章讲),讲的是从碎片化监控到统一可观测性体系,你需要做哪些架构决策,以及每个决策的踩坑实录。每一条都是我在生产环境中验证过的,不是 PPT 上的理论。

监控≠可观测性:先搞清楚你在解决什么问题

很多人把监控和可观测性混为一谈。这俩不是一回事。

监控(Monitoring) 回答的是"出了什么问题"——CPU 炸了、内存满了、接口超时了。它是预设的、基于已知故障模式的。你提前定义好"CPU > 80% 就告警",系统照着执行。

可观测性(Observability) 回答的是"为什么出问题"——为什么这个接口突然变慢了?为什么这个 Pod 一直在重启?为什么这个用户的请求失败了?它是探索性的、面向未知故障模式的。你没法提前定义所有可能的故障,但通过 Metrics + Logs + Traces 的关联,你能在出事后快速定位。

举个例子:用户反馈下单失败。

  • 只有监控:你看到订单服务的错误率从 0.1% 跳到 5%。你盯着 Grafana 看了 10 分钟,猜可能是数据库问题,跑去查 DB 日志。发现有个慢查询。但为什么有慢查询?不知道。继续猜。
  • 有可观测性:你看到错误率跳到 5%,点开 Trace 链路,看到请求在库存服务的 DB 查询 Span 耗时 8 秒。点开这个 Span 关联的日志,看到是连接池等待超时。再看 Metrics,发现连接池活跃连接数在 15 分钟前从 5 飙到 50(配置变更后上限改大了,但数据库端 max_connections 没改)。3 分钟定位完毕。
维度监控可观测性
核心问题出了什么问题?为什么出问题?
数据预设预定义指标和阈值探索性分析,支持临时查询
故障模式已知故障模式未知故障模式也能排查
数据关联各系统独立查看Metrics↔Logs↔Traces 互相关联
典型工具Zabbix、NagiosPrometheus+Loki+Tempo+Grafana
适用阶段基础运维阶段微服务/云原生阶段

别被概念绕晕了。记住一点:监控告诉你"病了",可观测性告诉你"病在哪、为什么病"。如果你的系统是单体应用、部署在 3 台服务器上,监控够了。但如果你的系统是 50 个微服务跑在 K8s 上,没有可观测性,出了故障你就是盲人摸象。

决策1:指标先行还是三支柱齐上?——分层数据采集策略

踩坑实录

见过不少团队一上来就搞"全套可观测性":Prometheus + Loki + Jaeger + OpenTelemetry + Grafana 一把梭。结果呢?运维团队精力分散在三套系统的部署、调优、维护上,每套都半吊子。Prometheus 的 Recording Rules 没写、Loki 的日志格式没统一、Jaeger 的采样率配成 100% 直接把存储撑爆。

分层策略

正确的做法是分层、分阶段上:

第一层:Metrics(指标)—— 告警和趋势分析的基础

指标是成本最低、价值密度最高的可观测性数据。一个 Counter 或 Gauge 占用几十字节,但能支撑告警和长期趋势分析。

先上 Metrics 的理由很简单:

  1. 成本低:1 万个指标序列的存储成本约 500MB/月(Prometheus 默认 15 天保留)
  2. 告警友好:PromQL 阈值判断,延迟在秒级
  3. 覆盖面广:Node Exporter + cAdvisor + 应用自定义指标,覆盖 80% 的日常监控需求
  4. 上手快:Prometheus + Grafana 半天能跑起来

第二层:Logs(日志)—— 根因定位的关键

Metrics 告诉你"哪个服务慢了",但"为什么慢"得看日志。日志的问题是量大——一个中等规模微服务集群每天产生 50-200GB 日志。

上日志采集前的关键决策:

  • 日志格式是否统一?结构化 JSON 日志比非结构化文本好 10 倍
  • 日志级别是否合理?生产环境 Debug 日志应该关掉或采样
  • 存储方案选 Loki 还是 ELK?Loki 轻量但查询能力弱,ELK 强大但吃资源

第三层:Traces(链路追踪)—— 微服务排障的杀手锏

Trace 的价值在微服务环境下才能体现。如果你的请求要经过 5 个以上的服务,Trace 是唯一能让你看清完整调用链的手段。

但 Trace 也是成本最高的:

  • 一个请求产生 5-15 个 Span,每个 Span 约 500 字节
  • 1000 QPS 的服务,1 分钟产生 300 万个 Span
  • 全量采集,存储成本是 Metrics 的 100 倍

推荐的落地节奏

Phase 1 (第1-2周):Prometheus + Grafana + Node Exporter + cAdvisor
                   → 覆盖基础资源监控和应用指标

Phase 2 (第3-4周):Loki + Promtail + 结构化日志改造
                   → 覆盖日志搜索和根因定位

Phase 3 (第5-6周):Tempo/Jaeger + OpenTelemetry SDK
                   → 覆盖分布式链路追踪

Phase 4 (第7-8周):Grafana 统一面板 + 关联配置
                   → Metrics↔Logs↔Traces 互跳

这个节奏的好处是:每个阶段都能独立产出价值,不会出现"建了 3 个月还没用上"的尴尬。Phase 1 完成后告警就能跑起来,Phase 2 完成后日志能搜了,Phase 3 完成后链路能看了。

关键经验

别一上来就追求数据关联。Metrics↔Logs↔Traces 的互跳需要统一的 Label/Tag 体系(比如都用 service_name + trace_id),但这个统一不是第一天就能设计好的。先各自跑起来,在实践中发现需要关联的场景,再逐步打通。

我在一个项目里见过团队花 2 周设计"完美的统一标签体系",结果上线后发现实际业务场景需要的标签和他们设计的完全不一样,又花 1 周重构。先跑起来再优化,别过度设计。

决策2:自建还是托管?——可观测性平台的 TCO 拆解

踩坑实录

一个 15 人研发团队的公司,自建 Prometheus + Loki + Tempo + Grafana 全套。头 3 个月一切正常。6 个月后问题开始冒:Prometheus 单机扛不住(每秒采集 15 万个样本),Loki 查询慢得像蜗牛(日志量到 5TB 后查询要 30 秒),Tempo 存储成本飙升(每月 2TB Trace 数据)。运维团队 2 个人,一半时间花在维护这三套系统上。

最后算了一笔账:自建成本(服务器 + 存储 + 2 个运维人力)比 Datadog 还贵 30%,而且功能远不如商业方案。

成本拆解

以一个中等规模集群(50 个微服务、500 个 Pod、1000 QPS)为例:

自建方案(PLTG Stack:Prometheus + Loki + Tempo + Grafana)

项目月成本(估算)说明
服务器¥3,000-6,0002-3 台 8C16G 云主机
存储¥1,000-3,000SSD 1TB + 对象存储 5TB
运维人力¥15,000-25,0000.5-1 个 FTE
网络带宽¥500-1,000Remote Write + 日志采集
合计¥20,000-35,000/月

托管方案(以 Datadog 为例)

项目月成本(估算)说明
主机监控¥7,000-10,00050 台 × ¥140-200/台
日志管理¥3,000-5,00050GB/天摄入 + 7 天保留
APM¥5,000-8,00050 个服务 × ¥100-160/服务
自定义指标¥2,000-4,00010 万自定义指标
合计¥17,000-27,000/月

看起来托管更便宜?别急,还有隐性成本。

决策框架

选自建还是托管,看三个维度:

1. 团队规模

  • < 10 人研发:直接用托管。自建的运维成本占比太高
  • 10-30 人:可以自建 Metrics(Prometheus 不难),Logs 和 Traces 考虑托管
  • 30-100 人:自建全套有规模效应,成本可控
  • 100+ 人:自建,并且可以考虑贡献回上游社区

2. 数据敏感度

  • 数据不能出机房(金融、政务、医疗):必须自建
  • 数据可以上云但不想给第三方:自建 or 开源托管方案
  • 数据不敏感:托管方案更省心

3. 定制化需求

  • 需要深度定制告警逻辑、自定义 PromQL 函数:自建
  • 需要多集群联邦查询、跨地域聚合:自建(商业方案支持但贵)
  • 标准监控需求:托管方案足够

混合方案(推荐)

实测最划算的方案是混合:

# Metrics:自建 Prometheus(成本低、控制力强)
# - 基础设施指标:Node Exporter + cAdvisor
# - 应用指标:OpenTelemetry SDK → Prometheus
# - 长期存储:Thanos 或 VictoriaMetrics(比 Datadog 便宜 10 倍)

# Logs:Loki 自建(轻量、够用)
# - 采集:Promtail DaemonSet
# - 存储:本地 SSD 7 天 + S3 30 天
# - 查询:Grafana LogQL

# Traces:Tempo 自建 or Grafana Cloud(量小可托管)
# - 采集:OpenTelemetry Collector
# - 采样:动态采样,生产环境 1-5%
# - 存储:S3 + 本地缓存

这套方案月成本约 ¥8,000-12,000,是纯托管的 40-50%,功能覆盖 90% 的场景。代价是需要 1 个运维花 20% 时间维护。

决策3:高基数标签是毒药——指标基数治理

踩坑实录

这是我见过最多的 Prometheus 翻车场景,没有之一。

有个团队在 HTTP 请求指标里加了 user_id 标签:

# 错误示范:每个用户一个时间序列
http_requests_total{
  method="GET",
  path="/api/order",
  user_id="12345"  # ← 这就是高基数标签
} 1

10 万用户 × 5 个接口 × 2 个方法 = 100 万条时间序列。Prometheus 内存从 2GB 飙到 32GB,查询超时,告警延迟 5 分钟。最后不得不重启 Prometheus 清空数据。

这种坑太常见了。高基数标签就是 Prometheus 的"内存炸弹"。

什么是基数,为什么重要

基数(Cardinality)指的是一个指标的时间序列数量。Prometheus 的存储模型是:每条时间序列 = 指标名 + 标签组合的笛卡尔积。

# 基数 = 标签值的组合数
http_requests_total{method="GET", path="/api/order", status="200"}
# 如果 method 有 5 种、path 有 20 种、status 有 10 种
# 基数 = 5 × 20 × 10 = 1000 条时间序列
标签类型基数是否安全
method (GET/POST/PUT/DELETE)4✅ 低基数
status (200/404/500/…)10✅ 低基数
path (/api/order, /api/user)20-50✅ 中基数
instance (host:port)50-200✅ 中基数
user_id10,000-1,000,000❌ 高基数,禁止
request_id (UUID)无限❌ 极高基数,禁止
session_id无限❌ 极高基数,禁止

经验法则:一个标签的基数超过 1000 就要警惕,超过 10000 基本是 bug。

治理手段

1. relabel_config 丢弃无用标签

在 Prometheus 采集端就干掉高基数标签,不要让它们进入存储:

scrape_configs:
  - job_name: 'app'
    static_configs:
      - targets: ['app:8080']
    metric_relabel_configs:
      # 丢弃 user_id 标签
      - action: labeldrop
        regex: 'user_id|request_id|session_id|trace_id'
      # 替换高基数 path 为低基数分组
      - source_labels: [path]
        regex: '/api/users/(\d+)'
        target_label: path
        replacement: '/api/users/:id'
      - source_labels: [path]
        regex: '/api/orders/([a-f0-9-]+)'
        target_label: path
        replacement: '/api/orders/:id'

这段配置做了两件事:一是直接丢弃 user_id 等高基数标签;二是把 /api/users/12345 这样的路径归一化为 /api/users/:id,把 10 万种路径变成 50 种。

2. Recording Rules 预聚合

对于查询频率高、计算量大的指标,用 Recording Rules 预聚合:

groups:
  - name: preaggregation
    rules:
      # 按服务聚合请求率,减少查询时的计算量
      - record: job:http_requests:rate5m
        expr: sum by (job, status) (rate(http_requests_total[5m]))
      
      # 按服务聚合 P99 延迟
      - record: job:http_request_duration_seconds:p99
        expr: histogram_quantile(0.99, sum by (job, le) (rate(http_request_duration_seconds_bucket[5m])))
      
      # 错误率
      - record: job:http_error_rate:ratio
        expr: |
          sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
          /
          sum by (job) (rate(http_requests_total[5m]))          

预聚合后,原来需要扫 100 万条序列的查询变成扫 50 条序列,查询速度提升 1000 倍。

3. 用 Cardinality API 监控基数

Prometheus 提供 /api/v1/status/tsdb 接口查看高基数指标:

# 查看基数最高的 10 个指标
curl -s http://prometheus:9090/api/v1/status/tsdb | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
series = data['data']['seriesCountByMetricName']
for name, count in sorted(series.items(), key=lambda x: -x[1])[:10]:
    print(f'{count:>10} {name}')
"

把这个加到监控面板上,基数异常增长时第一时间发现。

成本对比

基数治理前后的资源消耗对比(实测数据):

状态时间序列数内存占用查询延迟存储增长
治理前120 万28GB15-30s2GB/天
治理后3.5 万3.2GB0.5-2s180MB/天

内存降 89%,查询快 15 倍,存储增长降 91%。基数治理的 ROI 是所有可观测性优化里最高的,没有之一。

决策4:日志该存多久?——分层存储与采样策略

踩坑实录

一个电商团队,日订单量 50 万,12 个微服务,每天产生 180GB 日志。用 ELK 集群存,3 个节点 30TB SSD,以为够用了。3 个月后磁盘满了。扩容?预算不够。删旧日志?合规要求至少保留 90 天。最后搞了个热温冷三层数据层,存储成本降了 60%,查询体验还更好了。

日志分层存储架构

┌─────────────────────────────────────────────────────────────┐
│                    日志数据流                                 │
│                                                              │
│  应用日志 → Promtail/Fluent Bit → Loki/ELK                   │
│                                      │                       │
│                    ┌─────────────────┼───────────────┐       │
│                    │                 │               │       │
│                ┌───▼───┐     ┌──────▼──────┐  ┌────▼────┐  │
│                │ Hot   │     │   Warm      │  │ Cold    │  │
│                │ (热)  │     │   (温)      │  │ (冷)    │  │
│                │       │     │             │  │         │  │
│                │ 3 天  │     │   7-30 天   │  │ 90 天+  │  │
│                │ SSD  │     │  HDD/压缩   │  │ S3/OSS  │  │
│                │ 快查询│     │  慢查询     │  │ 按需恢复 │  │
│                └───────┘     └─────────────┘  └─────────┘  │
│                                                              │
│  查询优先级:Hot → Warm → Cold(逐层降级查询)                 │
└─────────────────────────────────────────────────────────────┘

Loki 的实现:

# Loki 配置:分层存储
storage_config:
  boltdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/cache
  
  # 热数据:本地 SSD
  filesystem:
    directory: /loki/data
  
  # 冷数据:S3
  aws:
    s3: s3://observability-logs/cold/
    endpoint: s3.cn-north-1.amazonaws.com

# 保留策略
limits_config:
  retention_period: 90d  # 总保留 90 天
  max_query_series: 1000  # 限制查询复杂度

compactor:
  working_directory: /loki/compactor
  shared_store: s3
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 50

日志降本手段

1. 结构化日志:一劳永逸的降本

非结构化日志(纯文本)在 Loki 里查询要用正则匹配,又慢又贵。结构化 JSON 日志可以用 LogQL 的标签直接过滤,查询效率高 10 倍:

# 差:非结构化日志
logging.info(f"User 12345 placed order 67890, amount=99.50, method=alipay")
# 查询:{app="order"} |= "order" |= "12345"  → 全文扫描

# 好:结构化日志
logging.info({
    "event": "order_placed",
    "user_id": "12345",  # 日志内容里的 user_id 没问题,不是 Prometheus 标签
    "order_id": "67890",
    "amount": 99.50,
    "payment_method": "alipay"
})
# 查询:{app="order"} | json | event="order_placed"  → 标签过滤

2. 日志级别采样

生产环境 Info 级日志占 80%,但 80% 的 Info 日志在出事时没用。采样策略:

# Promtail 管道配置:日志级别采样
pipeline_stages:
  - json:
      expressions:
        level: level
        message: msg
  
  # Drop DEBUG 日志
  - drop:
      source: level
      expression: "DEBUG"
  
  # 采样 INFO 日志(保留 10%)
  - sampling:
      rate: 0.1
      source: level
      expression: "INFO"
  
  # WARN 和 ERROR 全保留
  # (不在采样规则中的默认全保留)

实测效果:180GB/天 降到 45GB/天,WARN 和 ERROR 日志 0 丢失。

3. 日志字段裁剪

很多日志包含了大量冗余字段。用 Promtail 管道或 Fluent Bit Filter 裁剪:

# 只保留必要字段
pipeline_stages:
  - json:
      expressions:
        timestamp: timestamp
        level: level
        message: msg
        service: service_name
        trace_id: trace_id
        error: error
  # 其他字段全部丢弃

一条日志从 2KB 压到 200 字节,10 倍压缩。

存储成本对比

方案日存储月存储成本90 天总成本
全量热存(ELK SSD)180GB¥16,200¥48,600
分层存储(Hot 3d + Warm 27d + Cold 60d)180GB→45GB¥6,480¥19,440
采样+分层45GB¥1,620¥4,860

分层 + 采样的方案月成本只有全量热存的 10%,而且因为热数据少了,查询速度更快。降本和增效在这里是统一的。

决策5:Trace 采样率怎么定?——全量采集的成本陷阱

踩坑实录

一个团队用 OpenTelemetry SDK 采集 Trace,采样率配成 100%(全量采集),“反正先采着,以后再调”。1 周后 Tempo 存储从 0 涨到 800GB,S3 月账单多了 ¥4,000。更尴尬的是,因为数据太多,查询 Trace 列表要 20 秒,比不采集还影响排障效率。

全量采集 Trace 在生产环境几乎没必要。你不需要看到每一个请求的链路——你需要看到的是出问题的请求的链路。

两种采样策略

Head-based Sampling(头部采样)

在请求入口处就决定要不要采样。OpenTelemetry SDK 内置支持:

// OpenTelemetry SDK 配置:1% 采样率
SdkTracerProvider.builder()
    .setSampler(Sampler.traceIdRatioBased(0.01))  // 1% 采样
    .build();

优点:简单、低开销、不需要额外组件。

缺点:随机采样,出错的请求可能被采样掉了。1% 采样率下,99% 的错误请求的 Trace 会丢失。

Tail-based Sampling(尾部采样)

在请求完成后,根据请求的结果决定是否采样。通过 OpenTelemetry Collector 实现:

# OpenTelemetry Collector 配置:尾部采样
processors:
  tail_sampling:
    decision_wait: 30s  # 等待 30 秒收集完整 Trace
    num_traces: 50000   # 内存中保留的 Trace 数
    
    policies:
      # 采样所有错误请求
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      
      # 采样慢请求(>2s)
      - name: slow
        type: latency
        latency:
          threshold_ms: 2000
      
      # 正常请求采样 1%
      - name: baseline
        type: probabilistic
        probabilistic:
          sampling_percentage: 1

这套配置的效果:所有错误 Trace 100% 保留,慢请求 100% 保留,正常请求只保留 1%。存储量降 95%+,但排障关键信息一个不少。

动态采样

更进阶的做法是根据系统状态动态调整采样率。正常时 1%,错误率飙升时自动提到 100%:

processors:
  tail_sampling:
    decision_wait: 30s
    num_traces: 50000
    
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      
      - name: dynamic_rate
        type: probabilistic
        probabilistic:
          # 通过环境变量动态注入采样率
          # 正常时 1%,告警时通过 API 调整为 100%
          sampling_percentage: ${env:TRACE_SAMPLING_RATE}

配合一个简单的脚本,在告警触发时自动提高采样率:

import os
import requests
from prometheus_client import CollectorRegistry

def adjust_sampling_rate():
    """根据错误率动态调整 Trace 采样率"""
    # 查询当前 5 分钟错误率
    query = 'sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))'
    resp = requests.get(
        f'http://prometheus:9090/api/v1/query',
        params={'query': query}
    ).json()
    
    error_rate = float(resp['data']['result'][0]['value'][1])
    
    if error_rate > 0.05:  # 错误率 > 5%
        rate = 100
    elif error_rate > 0.01:  # 错误率 > 1%
        rate = 50
    else:
        rate = 1  # 正常 1%
    
    # 更新 Collector 配置(通过 API 或环境变量)
    os.environ['TRACE_SAMPLING_RATE'] = str(rate)
    print(f"Error rate: {error_rate:.2%}, sampling rate: {rate}%")

if __name__ == '__main__':
    adjust_sampling_rate()

成本对比

采样策略日 Trace 数据量月存储成本排障能力
全量采集 (100%)120GB/天¥10,800全覆盖但查询慢
头部采样 (1%)1.2GB/天¥108差,99% 错误 Trace 丢失
尾部采样(错误+慢+1%正常)6GB/天¥540好,关键 Trace 全保留
动态采样2-8GB/天¥180-720最优,故障期间全覆盖

尾部采样是大多数场景的最优解。动态采样更智能但实现复杂度高,适合对可观测性有较高要求的团队。

决策6:仪表盘给谁看?——可观测性数据的消费者分层

踩坑实录

见过一个团队的 Grafana:56 个 Dashboard,每个 Dashboard 20-40 个面板。最大的一个"全局总览"Dashboard 有 87 个面板,加载一次要 40 秒。值班 SRE 看不过来,开发嫌太复杂,管理层看不懂。最后大家都不看了,Dashboard 变成了摆设。

仪表盘不是越多越好,是越对的人看到对的信息越好。

三层仪表盘架构

┌──────────────────────────────────────────────────────────┐
│                     仪表盘分层架构                         │
│                                                           │
│  ┌─────────────────┐                                      │
│  │  L1: 执行层      │  目标受众:值班 SRE                   │
│  │  (实时告警面板)   │  关注:当前有没有问题?               │
│  │  刷新:10s-30s   │  面板数:5-8 个                      │
│  │  数据:实时指标   │  布局:单屏可览全貌                   │
│  └────────┬────────┘                                      │
│           │                                               │
│  ┌────────▼────────┐                                      │
│  │  L2: 诊断层      │  目标受众:开发 + SRE                 │
│  │  (服务排障面板)   │  关注:哪个服务出了问题?为什么?     │
│  │  刷新:1m-5m     │  面板数:15-20 个                    │
│  │  数据:指标+日志  │  布局:下钻式,从全局到单服务         │
│  └────────┬────────┘                                      │
│           │                                               │
│  ┌────────▼────────┐                                      │
│  │  L3: 决策层     │  目标受众:技术管理 + 产品              │
│  │  (SLO 面板)     │  关注:服务质量达标了吗?             │
│  │  刷新:1h-1d    │  面板数:8-12 个                     │
│  │  数据:SLO 指标  │  布局:业务视角,非技术细节           │
│  └─────────────────┘                                      │
└──────────────────────────────────────────────────────────┘

L1:执行层面板(值班 SRE 用)

设计原则:一眼看清系统当前健康状态。不超过 8 个面板,单屏不滚动。

// L1 Dashboard 关键面板
// 1. 全局健康状态(绿灯/红灯)
// 2. 核心服务错误率(P99 + SLO 线)
// 3. 核心服务延迟(P50/P99)
// 4. 告警计数(按严重度分组)
// 5. K8s Pod 状态(Running/Pending/Failed)
// 6. 数据库连接池使用率
// 7. 最近 1 小时变更事件
// 8. On-Call 值班信息

核心 PromQL:

// 全局健康状态:核心服务 SLO 达标率
sum by (service) (
  rate(http_requests_total{status!~"5..", service=~"order|payment|user"}[5m])
)
/
sum by (service) (
  rate(http_requests_total{service=~"order|payment|user"}[5m])
)

// 告警计数
count by (severity) (
  ALERTS{alertstate="firing"}
)

L2:诊断层面板(开发 + SRE 用)

设计原则:从全局到单服务的下钻式排障。按服务分组,每组面板结构一致。

关键面板:

  • 服务级 Request Rate / Error Rate / Latency(RED 指标)
  • 服务级资源使用(CPU / Memory / 连接池)
  • 日志面板:最近 100 条 ERROR 日志
  • Trace 面板:最近慢请求的 Trace 链接

L2 面板的价值在于关联。Grafana 支持面板间的数据关联:

# Grafana Dashboard Links 配置
links:
  # 从指标面板跳转到日志
  - title: "查看日志"
    type: link
    url: /explore?orgId=1&left={"datasource":"Loki","queries":[{"expr":"{service=\"$service\"} |= \"ERROR\""}]}
    
  # 从指标面板跳转到 Trace
  - title: "查看链路"
    type: link
    url: /explore?orgId=1&left={"datasource":"Tempo","queries":[{"service_name":"$service","operation":"$operation"}]}

配置好后,值班 SRE 在指标面板看到某个服务错误率升高,点一下就能跳到对应日志,再点一下能看到 Trace。这就是"可观测性"的体感——数据之间互相关联,不用在多个工具间手动切换。

L3:决策层面板(技术管理 + 产品用)

设计原则:用业务语言讲技术故事。不展示 CPU、内存这些技术指标,展示 SLO 达标率、错误预算消耗、故障次数。

// SLO 达标率(30 天滑动窗口)
sum(rate(http_requests_total{status!~"5..", service="order"}[30d]))
/
sum(rate(http_requests_total{service="order"}[30d]))

// 错误预算消耗进度
1 - (
  sum(rate(http_requests_total{status!~"5..", service="order"}[30d]))
  /
  sum(rate(http_requests_total{service="order"}[30d]))
)
/
(1 - 0.999)  // SLO 目标 99.9%

错误预算面板是 L3 最有价值的面板。它把"系统可靠性"翻译成了产品和管理能理解的语言:“你这个月的错误预算还剩 30%,如果继续按这个错误率,下周就会超预算,需要冻结发布”。

仪表盘治理经验

  1. 每季度做一次 Dashboard 清理:删除 3 个月没人看的面板。我见过一个团队从 56 个 Dashboard 精简到 12 个,排障效率反而提升了——因为找面板的时间少了
  2. 统一变量命名:所有 Dashboard 的 $service、$env、$time_range 变量统一命名和默认值,减少认知负担
  3. Dashboard as Code:用 Grafana Terraform Provider 或 jsonnet 管理 Dashboard,纳入 Git 版本控制。别用 UI 手点——不可复现、不可审计、不可回滚

实施路线图:从碎片到统一的 8 周计划

如果你现在的情况是"工具都上了但不成体系",以下是验证过的 8 周改造计划:

周次任务产出
W1盘点现有工具和数据流现状文档:哪些工具、采集什么、谁在看
W2统一 Label 体系确定 service_name、env、trace_id 等统一标签
W3Metrics 基数治理relabel_config + Recording Rules,序列数降 80%
W4日志分层存储Hot/Warm/Cold 三层,存储成本降 60%
W5Trace 尾部采样Collector 部署,错误 Trace 100% 保留
W6三层仪表盘重建L1/L2/L3 面板,删掉 70% 无用面板
W7数据关联打通Metrics↔Logs↔Traces 互跳
W8SLO 驱动告警错误预算告警替代静态阈值告警

8 周后,你应该能看到:告警数量降 70%+,P0 根因定位时间从 30+ 分钟降到 10 分钟以内,存储成本降 50%+。

总结

可观测性不是买三套工具拼在一起就有的。它是从数据采集到数据消费的完整工程体系,每个环节都需要架构决策。

6 个核心决策回顾:

  1. 分层采集:Metrics → Logs → Traces 分阶段上,先告警后排障
  2. 自建 vs 托管:看团队规模和数据敏感度,混合方案最划算
  3. 基数治理:高基数标签是 Prometheus 内存炸弹,relabel + Recording Rules 是解药
  4. 日志分层:Hot/Warm/Cold 三层 + 采样,存储成本降 90%
  5. Trace 采样:尾部采样比头部采样智能,错误和慢请求 100% 保留
  6. 仪表盘分层:L1 告警、L2 排障、L3 决策,别给 SRE 看 SLO 面板也别给管理层看 CPU 曲线

一条核心经验:可观测性的终极目标不是数据全,而是出事时能快速定位根因。5000 条告警不如 50 条精准告警,120GB 日志不如 45GB 结构化日志,100% 采样不如尾部 5% 采样。少即是多,精准胜过全面。

最后说一句实在话:上面每个决策的参数和阈值都需要根据你的实际环境调整。文章里的数字是我生产环境的实测值,你的流量模式、服务规模、预算约束都不同。但决策框架是通用的——先想清楚"谁消费数据、解决什么问题",再决定"采什么、存多久、怎么查"。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Architecture strategies for designing a monitoring system — Microsoft Azure Well-Architected Framework,可观测性架构设计策略的官方参考
  2. Prometheus Agent Mode: Efficient and Cloud-Native Way for Metric Forwarding — Bartek Płotka(Prometheus Maintainer、CNCF TAG Observability 技术负责人),Prometheus Agent 模式与可观测性范式演进
  3. Remote Write Tuning — Prometheus 官方文档,Remote Write 调优与内存管理实践
  4. OpenMetrics 2.0 Specification — Prometheus 社区/CNCF,指标格式标准与 OpenTelemetry 兼容性设计
  5. 可观测性三大支柱:Metrics、Logs、Traces 全面解析 — CSDN,三支柱关系与协同工作的最佳实践总结
  6. Prometheus + Loki + Tempo 打造可观测性平台 — 腾讯云开发者社区,企业级可观测性平台搭建实战
  7. Observability: Logs, Metrics, and Traces — Springer,可观测性三支柱学术综述与 OpenTelemetry 实践