概述
去年有个朋友跟我吐槽:他们公司上了 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、Nagios | Prometheus+Loki+Tempo+Grafana |
| 适用阶段 | 基础运维阶段 | 微服务/云原生阶段 |
别被概念绕晕了。记住一点:监控告诉你"病了",可观测性告诉你"病在哪、为什么病"。如果你的系统是单体应用、部署在 3 台服务器上,监控够了。但如果你的系统是 50 个微服务跑在 K8s 上,没有可观测性,出了故障你就是盲人摸象。
决策1:指标先行还是三支柱齐上?——分层数据采集策略
踩坑实录
见过不少团队一上来就搞"全套可观测性":Prometheus + Loki + Jaeger + OpenTelemetry + Grafana 一把梭。结果呢?运维团队精力分散在三套系统的部署、调优、维护上,每套都半吊子。Prometheus 的 Recording Rules 没写、Loki 的日志格式没统一、Jaeger 的采样率配成 100% 直接把存储撑爆。
分层策略
正确的做法是分层、分阶段上:
第一层:Metrics(指标)—— 告警和趋势分析的基础
指标是成本最低、价值密度最高的可观测性数据。一个 Counter 或 Gauge 占用几十字节,但能支撑告警和长期趋势分析。
先上 Metrics 的理由很简单:
- 成本低:1 万个指标序列的存储成本约 500MB/月(Prometheus 默认 15 天保留)
- 告警友好:PromQL 阈值判断,延迟在秒级
- 覆盖面广:Node Exporter + cAdvisor + 应用自定义指标,覆盖 80% 的日常监控需求
- 上手快: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,000 | 2-3 台 8C16G 云主机 |
| 存储 | ¥1,000-3,000 | SSD 1TB + 对象存储 5TB |
| 运维人力 | ¥15,000-25,000 | 0.5-1 个 FTE |
| 网络带宽 | ¥500-1,000 | Remote Write + 日志采集 |
| 合计 | ¥20,000-35,000/月 |
托管方案(以 Datadog 为例)
| 项目 | 月成本(估算) | 说明 |
|---|---|---|
| 主机监控 | ¥7,000-10,000 | 50 台 × ¥140-200/台 |
| 日志管理 | ¥3,000-5,000 | 50GB/天摄入 + 7 天保留 |
| APM | ¥5,000-8,000 | 50 个服务 × ¥100-160/服务 |
| 自定义指标 | ¥2,000-4,000 | 10 万自定义指标 |
| 合计 | ¥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_id | 10,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 万 | 28GB | 15-30s | 2GB/天 |
| 治理后 | 3.5 万 | 3.2GB | 0.5-2s | 180MB/天 |
内存降 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%,如果继续按这个错误率,下周就会超预算,需要冻结发布”。
仪表盘治理经验
- 每季度做一次 Dashboard 清理:删除 3 个月没人看的面板。我见过一个团队从 56 个 Dashboard 精简到 12 个,排障效率反而提升了——因为找面板的时间少了
- 统一变量命名:所有 Dashboard 的
$service、$env、$time_range变量统一命名和默认值,减少认知负担 - Dashboard as Code:用 Grafana Terraform Provider 或 jsonnet 管理 Dashboard,纳入 Git 版本控制。别用 UI 手点——不可复现、不可审计、不可回滚
实施路线图:从碎片到统一的 8 周计划
如果你现在的情况是"工具都上了但不成体系",以下是验证过的 8 周改造计划:
| 周次 | 任务 | 产出 |
|---|---|---|
| W1 | 盘点现有工具和数据流 | 现状文档:哪些工具、采集什么、谁在看 |
| W2 | 统一 Label 体系 | 确定 service_name、env、trace_id 等统一标签 |
| W3 | Metrics 基数治理 | relabel_config + Recording Rules,序列数降 80% |
| W4 | 日志分层存储 | Hot/Warm/Cold 三层,存储成本降 60% |
| W5 | Trace 尾部采样 | Collector 部署,错误 Trace 100% 保留 |
| W6 | 三层仪表盘重建 | L1/L2/L3 面板,删掉 70% 无用面板 |
| W7 | 数据关联打通 | Metrics↔Logs↔Traces 互跳 |
| W8 | SLO 驱动告警 | 错误预算告警替代静态阈值告警 |
8 周后,你应该能看到:告警数量降 70%+,P0 根因定位时间从 30+ 分钟降到 10 分钟以内,存储成本降 50%+。
总结
可观测性不是买三套工具拼在一起就有的。它是从数据采集到数据消费的完整工程体系,每个环节都需要架构决策。
6 个核心决策回顾:
- 分层采集:Metrics → Logs → Traces 分阶段上,先告警后排障
- 自建 vs 托管:看团队规模和数据敏感度,混合方案最划算
- 基数治理:高基数标签是 Prometheus 内存炸弹,relabel + Recording Rules 是解药
- 日志分层:Hot/Warm/Cold 三层 + 采样,存储成本降 90%
- Trace 采样:尾部采样比头部采样智能,错误和慢请求 100% 保留
- 仪表盘分层:L1 告警、L2 排障、L3 决策,别给 SRE 看 SLO 面板也别给管理层看 CPU 曲线
一条核心经验:可观测性的终极目标不是数据全,而是出事时能快速定位根因。5000 条告警不如 50 条精准告警,120GB 日志不如 45GB 结构化日志,100% 采样不如尾部 5% 采样。少即是多,精准胜过全面。
最后说一句实在话:上面每个决策的参数和阈值都需要根据你的实际环境调整。文章里的数字是我生产环境的实测值,你的流量模式、服务规模、预算约束都不同。但决策框架是通用的——先想清楚"谁消费数据、解决什么问题",再决定"采什么、存多久、怎么查"。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Architecture strategies for designing a monitoring system — Microsoft Azure Well-Architected Framework,可观测性架构设计策略的官方参考
- Prometheus Agent Mode: Efficient and Cloud-Native Way for Metric Forwarding — Bartek Płotka(Prometheus Maintainer、CNCF TAG Observability 技术负责人),Prometheus Agent 模式与可观测性范式演进
- Remote Write Tuning — Prometheus 官方文档,Remote Write 调优与内存管理实践
- OpenMetrics 2.0 Specification — Prometheus 社区/CNCF,指标格式标准与 OpenTelemetry 兼容性设计
- 可观测性三大支柱:Metrics、Logs、Traces 全面解析 — CSDN,三支柱关系与协同工作的最佳实践总结
- Prometheus + Loki + Tempo 打造可观测性平台 — 腾讯云开发者社区,企业级可观测性平台搭建实战
- Observability: Logs, Metrics, and Traces — Springer,可观测性三支柱学术综述与 OpenTelemetry 实践