在单体里,出问题时你读日志、挂调试器。在一个由几十个服务组成的分布式系统里——本站每个设计都是这种——一个用户请求会扇出到很多机器,"为什么慢?"没有单一的地方可看。可观测性(observability)就是给系统插桩,让你能从外部回答关于它行为的问题,包括你没预料到的问题。它的基础是三大支柱:指标(metrics)、日志(logs)、追踪(traces)。这是我们 DDIA"分布式系统的麻烦"笔记 的运维另一面。
- 监控 vs 可观测性——监控盯着已知的故障模式(你预设的仪表盘/告警);可观测性让你事后探查未知的问题。
- 三大支柱:指标、日志、追踪。指标 = 便宜可聚合的数字;日志 = 丰富的离散事件;追踪 = 一个请求跨服务的路径。
- 指标驱动仪表盘和告警(Prometheus 拉取模型)——但当心基数(cardinality):高基数标签会让存储爆炸。
- 分布式追踪用一个透传的 trace ID 跟踪单个请求跨服务,暴露延迟去了哪。
- 用共享的 trace ID 把三支柱关联起来——这才是真正的威力,指标 → 追踪 → 日志一路跳。
- SLI/SLO/错误预算定义"够可靠";告警基于用户可见的症状而非每个内部原因,避免告警疲劳。
- OpenTelemetry 是发出这三者的厂商中立标准。
可观测性给系统插桩,让你能从它的输出理解其内部状态——在请求跨很多服务时至关重要。指标(数值时间序列)驱动仪表盘和告警;日志(离散事件)给细节;追踪用透传的 trace ID 把单个请求跨服务的旅程串起来。关联这三者才能快速排障。用 SLI/SLO 和错误预算定义可靠性,基于症状而非原因告警,并用 OpenTelemetry 统一采集。当心基数和成本。
监控 vs 可观测性
两者相关但不同。监控是盯着预定义信号找预定义问题——"CPU > 90% 或错误率飙升时告警我"。它回答已知问题(known-unknowns)。可观测性是系统的一种属性:它有足够多、足够好、可查询的遥测数据,让你能问你从没预料到的新问题——"为什么只有这一个客户、在这个 API 版本、只在欧盟慢?"(unknown-unknowns)。监控告诉你有问题;可观测性帮你发现为什么,哪怕是新型故障。
三大支柱
| 支柱 | 是什么 | 擅长 / 成本 |
|---|---|---|
| 指标(Metrics) | 随时间的数值测量(计数器、仪表、直方图) | 便宜、可聚合,适合告警和趋势;细节低 |
| 日志(Logs) | 带时间戳的离散事件记录 | 每条细节丰富;高量时昂贵 |
| 追踪(Traces) | 单个请求跨服务的端到端路径 | 显示延迟/错误在哪;常采样 |
指标
指标是随时间测量的数字——请求速率、错误数、延迟分位、队列深度、CPU。它们存储便宜、查询快,因为是聚合的,所以非常适合仪表盘和告警。常见模型是 Prometheus,它从每个服务的 /metrics 端点定时拉取(scrape)指标,存为带维度标签的时间序列;Grafana 做可视化。
http_requests_total{service="api", route="/checkout", status="500"} 4271
http_request_duration_seconds{service="api", quantile="0.99"} 0.842
告警: rate(http_requests_total{status=~"5.."}[5m]) / rate(all) > 0.01
每个唯一的标签值组合都是一条单独的时间序列。给指标加一个高基数标签(如 user_id 或 request_id)可能产生数百万条序列、撑爆存储和查询成本。让指标标签保持低基数(service、route、status)——把高基数标识放进日志/追踪,而不是指标。
日志
日志是离散、带时间戳的事件记录——最丰富、最详细的信号。关键实践是结构化日志:把日志发成机器可解析的 key-value/JSON(带 trace_id、user_id、latency_ms 等字段),而非自由文本,这样才能搜索和聚合。日志通常被送往像 Elasticsearch 这样的搜索后端(ELK/Elastic 技术栈)或日志服务。它们的弱点是大规模下成本高——高量服务产生海量日志数据——所以团队会采样、设保留期、在热路径上克制地打日志。
追踪
在分布式系统里,单个请求在很多服务间跳转,指标和日志都看不出那段旅程。分布式追踪能:每个请求拿到一个唯一的 trace ID,它被透传到请求触及的每个服务,每个工作单元记一个 span(带时延、父 span、元数据)。拼起来,这些 span 组成一棵树,精确显示请求在哪花了时间、在哪失败。
trace 7f3c… │ POST /checkout 320ms
├─ api-gateway ▇▇ 20ms
├─ order-service ▇▇▇▇▇▇ 90ms
│ └─ db: INSERT ▇▇▇ 55ms ◀ 慢!
└─ payment-service ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 210ms ◀ 瓶颈
└─ 外部 PSP ▇▇▇▇▇▇▇▇▇▇▇▇▇ 190ms
这条 trace 立刻把延迟定位到 payment 服务的外部调用——任何仪表盘都指不出来。因为追踪每个请求很贵,系统通常会采样(保留一部分,或用尾部采样保留有趣/慢/出错的那些)。
关联:真正的威力
三大支柱合起来远比分开有用。把它们连起来的纽带是一个共享的 trace ID,盖在指标 exemplar、日志和 span 上。排障循环变成:一个指标告警显示错误率上升 → 钻进一个失败请求的追踪看哪个服务坏了 → 跳到那个服务、那个 trace ID 的日志读错误细节。指标(什么 & 何时)→ 追踪(哪里)→ 日志(为什么),全部相连。把这套关联接好,是"有用的可观测性"与"三个互不相连的数据孤岛"之间的区别。
SLI、SLO、SLA 与错误预算
可观测性需要一个"健康"的定义,SRE 词汇提供了:
| 术语 | 含义 |
|---|---|
| SLI(指标) | 一个衡量质量的实测信号,如延迟 < 200ms 的请求占比,或成功率 |
| SLO(目标) | SLI 的目标值,如"30 天内 99.9% 的请求成功" |
| SLA(协议) | 对客户的合同承诺,带罚则(通常比 SLO 宽松) |
| 错误预算 | 1 − SLO:允许的不可靠度(0.1%);用它来发布,耗尽就停 |
错误预算是巧妙之处:它把可靠性变成一个可以花的数字。预算内,就快速发布;烧光了(最近故障太多),就冻结高风险改动、稳一稳。它让开发速度和可靠性对齐,而非对立。
告警
黄金法则:基于症状告警,而非原因。在用户真正受影响时(错误率上升、延迟 SLO 在烧)才呼叫人,而不是每个可能自愈的内部小波动(某个节点 CPU、一次瞬时重试)。基于原因的告警会造成告警疲劳——太多呼叫,真正重要的反被忽略。把告警绑到 SLO 燃尽率,让紧急程度匹配用户影响。Google 的"四个黄金信号"(延迟、流量、错误、饱和度)是一组不错的默认监控项。
OpenTelemetry
过去每种信号、每家厂商都有自己的 agent 和格式。OpenTelemetry(OTel)是如今标准的、厂商中立的框架,用于给代码插桩并以通用格式发出指标、日志、追踪,导出到你选的任何后端(Prometheus、Jaeger、某 SaaS)。它把插桩和后端解耦,所以你插桩一次就能换厂商而不必重新插桩——这正是它成为默认的原因。
常见坑
- 基数爆炸——高基数指标标签产生数百万序列;把标识放进日志/追踪,别放指标。
- 日志成本——大规模冗长日志很贵;采样、结构化、设保留期。
- 仪表盘泛滥——上百个没人看的仪表盘;精选几个对应 SLO 的。
- 告警疲劳——太多基于原因的呼叫训练人去无视告警;基于症状告警。
- 信号不关联——指标、日志、追踪在没有共享 trace ID 的孤岛里,排障很慢。
可观测性是你排查那些复杂到无法手动推理的分布式系统的方式。指标便宜地告诉你出了问题并看趋势;追踪显示跨服务在哪;日志解释为什么——而一个共享的 trace ID 把它们连起来。用 SLO 和错误预算定义可靠性,基于用户可见症状告警以避免疲劳,把采集标准化到 OpenTelemetry,同时提防基数和成本。
监控 vs 可观测性?监控盯着已知故障模式(预设仪表盘/告警);可观测性靠丰富遥测让你事后探查未知问题。
三大支柱?指标(便宜可聚合的数字 → 告警/趋势)、日志(详细的离散事件)、追踪(单个请求跨服务的路径)。用共享 trace ID 关联。
基数陷阱是什么?高基数指标标签(user_id、request_id)产生海量时间序列、撑爆成本——改放进日志/追踪。
SLI vs SLO vs SLA vs 错误预算?SLI = 实测信号,SLO = 内部目标,SLA = 客户合同,错误预算 = 1−SLO,你可以花的那点不可靠度。
该基于什么告警?用户可见的症状(SLO 燃尽、错误率),而非每个内部原因——避免告警疲劳。