在单体里,出问题时你读日志、挂调试器。在一个由几十个服务组成的分布式系统里——本站每个设计都是这种——一个用户请求会扇出到很多机器,"为什么慢?"没有单一的地方可看。可观测性(observability)就是给系统插桩,让你能从外部回答关于它行为的问题,包括你没预料到的问题。它的基础是三大支柱:指标(metrics)、日志(logs)、追踪(traces)。这是我们 DDIA"分布式系统的麻烦"笔记 的运维另一面。

⚡ 速览要点
  • 监控 vs 可观测性——监控盯着已知的故障模式(你预设的仪表盘/告警);可观测性让你事后探查未知的问题。
  • 三大支柱:指标、日志、追踪。指标 = 便宜可聚合的数字;日志 = 丰富的离散事件;追踪 = 一个请求跨服务的路径。
  • 指标驱动仪表盘和告警(Prometheus 拉取模型)——但当心基数(cardinality):高基数标签会让存储爆炸。
  • 分布式追踪用一个透传的 trace ID 跟踪单个请求跨服务,暴露延迟去了哪。
  • 用共享的 trace ID 把三支柱关联起来——这才是真正的威力,指标 → 追踪 → 日志一路跳。
  • SLI/SLO/错误预算定义"够可靠";告警基于用户可见的症状而非每个内部原因,避免告警疲劳。
  • OpenTelemetry 是发出这三者的厂商中立标准。
tldr

可观测性给系统插桩,让你能从它的输出理解其内部状态——在请求跨很多服务时至关重要。指标(数值时间序列)驱动仪表盘和告警;日志(离散事件)给细节;追踪用透传的 trace ID 把单个请求跨服务的旅程串起来。关联这三者才能快速排障。用 SLI/SLO 和错误预算定义可靠性,基于症状而非原因告警,并用 OpenTelemetry 统一采集。当心基数和成本。

监控 vs 可观测性

两者相关但不同。监控是盯着预定义信号找预定义问题——"CPU > 90% 或错误率飙升时告警我"。它回答已知问题(known-unknowns)。可观测性是系统的一种属性:它有足够多、足够好、可查询的遥测数据,让你能问你从没预料到的问题——"为什么只有这一个客户、在这个 API 版本、只在欧盟慢?"(unknown-unknowns)。监控告诉你问题;可观测性帮你发现为什么,哪怕是新型故障。

三大支柱

支柱是什么擅长 / 成本
指标(Metrics)随时间的数值测量(计数器、仪表、直方图)便宜、可聚合,适合告警和趋势;细节低
日志(Logs)带时间戳的离散事件记录每条细节丰富;高量时昂贵
追踪(Traces)单个请求跨服务的端到端路径显示延迟/错误在哪;常采样

指标

指标是随时间测量的数字——请求速率、错误数、延迟分位、队列深度、CPU。它们存储便宜、查询快,因为是聚合的,所以非常适合仪表盘和告警。常见模型是 Prometheus,它从每个服务的 /metrics 端点定时拉取(scrape)指标,存为带维度标签的时间序列;Grafana 做可视化。

带标签的指标(Prometheus 风格)
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_idrequest_id)可能产生数百万条序列、撑爆存储和查询成本。让指标标签保持低基数(service、route、status)——把高基数标识放进日志/追踪,而不是指标。

日志

日志是离散、带时间戳的事件记录——最丰富、最详细的信号。关键实践是结构化日志:把日志发成机器可解析的 key-value/JSON(带 trace_iduser_idlatency_ms 等字段),而非自由文本,这样才能搜索和聚合。日志通常被送往像 Elasticsearch 这样的搜索后端(ELK/Elastic 技术栈)或日志服务。它们的弱点是大规模下成本高——高量服务产生海量日志数据——所以团队会采样、设保留期、在热路径上克制地打日志。

追踪

在分布式系统里,单个请求在很多服务间跳转,指标和日志都看不出那段旅程分布式追踪能:每个请求拿到一个唯一的 trace ID,它被透传到请求触及的每个服务,每个工作单元记一个 span(带时延、父 span、元数据)。拼起来,这些 span 组成一棵树,精确显示请求在哪花了时间、在哪失败。

一条 trace = 跨服务的 span(透传 trace_id)
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)。它把插桩和后端解耦,所以你插桩一次就能换厂商而不必重新插桩——这正是它成为默认的原因。

常见坑

总结

可观测性是你排查那些复杂到无法手动推理的分布式系统的方式。指标便宜地告诉你出了问题并看趋势;追踪显示跨服务在哪;日志解释为什么——而一个共享的 trace ID 把它们连起来。用 SLO 和错误预算定义可靠性,基于用户可见症状告警以避免疲劳,把采集标准化到 OpenTelemetry,同时提防基数和成本。

🎯 面试速答

监控 vs 可观测性?监控盯着已知故障模式(预设仪表盘/告警);可观测性靠丰富遥测让你事后探查未知问题。
三大支柱?指标(便宜可聚合的数字 → 告警/趋势)、日志(详细的离散事件)、追踪(单个请求跨服务的路径)。用共享 trace ID 关联。
基数陷阱是什么?高基数指标标签(user_id、request_id)产生海量时间序列、撑爆成本——改放进日志/追踪。
SLI vs SLO vs SLA vs 错误预算?SLI = 实测信号,SLO = 内部目标,SLA = 客户合同,错误预算 = 1−SLO,你可以花的那点不可靠度。
该基于什么告警?用户可见的症状(SLO 燃尽、错误率),而非每个内部原因——避免告警疲劳。

← 上一篇
CI/CD