想象你是一座大型游乐城的运营总监。小丑摊位、过山车、海盗船……每个项目都是独立的“微服务”。传统管理方式下,游客在过山车门口的留言簿记录感受,旋转木马那里有一台人流量计数器,小丑摊位自己记着谁玩了什么。你每天要跑三个办公室、看三种报表,才能拼出游客体验的全貌。更麻烦的是,每个摊位的记录格式还不一样。 OpenTelemetry 就是游管委发布的一份“全园区统一管理标准”:每个摊位统一安装感应器,按同一套格式上报“游玩流水(链路)”“排队统计(指标)”“游客留言(日志)”;中央调度室统一收拢、过滤、分拣,再按需送到不同的档案室。这样一来,无论园区里的项目怎么变,你都能用一套管道看清整个园区发生了什么。

岗位关联

学懂 OpenTelemetry,你就能用一套标准为云原生系统搭建可观测性底座,避免在多套工具间来回切换,这也是大规模分布式系统排障和稳定性建设的核心能力。

目录
  1. 可视化演示
  2. 背景定位:为什么需要统一标准
  3. 三支柱:日志、指标、链路
  4. 核心架构:API、SDK、Exporter
  5. Collector管道:接收、处理、导出
  6. 上下文传播:让链路不迷路
  7. 部署模式:Agent 与 Gateway
  8. 后端对接与生态:数据送往哪里
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示OTel分布式链路追踪、指标采集与日志聚合三大遥测信号,交互切换查看全链路可观测性。

本节配有交互动效(进入视口后自动加载,约 3KB)。

分布式链路追踪 请求经多个微服务,每个Span记录耗时与上下文 TraceID串联全链路,SpanID标识父子关系 Trace = Span1 + Span2 + ... + SpanN 调节采样率查看不同采样密度下的链路覆盖

背景定位:为什么需要统一标准

早晨,游乐城里的过山车突然停了十分钟。作为运营总监,你需要知道:排队区有多少人?游客有没有抱怨?过山车前后调用了哪些设备?可是小丑摊位、旋转木马、过山车各自用不同的表格记录,你要跑三个办公室才能拼出线索。云原生系统也一样——几十个微服务分散在不同容器、节点和云上,日志、指标、链路分别存在三套系统里,出了问题很难快速定位。

OpenTelemetry(简称 OTel)的出现,就是要解决这种“各记各账”的混乱。它由 OpenTracing 和 OpenCensus 两个项目合并而来,是 CNCF 孵化的云原生可观测性事实标准。它做的事情可以概括为:用一套统一的 API、SDK、协议和语义约定,采集日志(Logs)、指标(Metrics)、链路追踪(Traces),并通过统一的 Collector 管道送往后端。

在 OTel 之前,团队为了观察系统往往要同时接入 Prometheus、ELK、Jaeger 三套体系,埋点代码被三种 SDK 绑定,迁移成本很高。OTel 把“产生数据”与“数据去哪”解耦:应用只面向标准 API 埋点,由 Exporter 或 Collector 决定把数据发给谁。就算后端从 Jaeger 换成 Tempo,应用代码也不需要大改。这就是“一次埋点,随处对接”的含义。

回到游乐城,这相当于游管委统一发放了接线规范:每台设备上都保留统一的插口,后面接哪家档案室,则由中央调度室决定,而不是每个摊位自己各接一套线。

三支柱:日志、指标、链路

游乐城最想让运营者看到三类信息:游客留言簿记录某个时刻发生了什么;入口计数器每小时汇总入园人数;游客手环记录从入园到出园玩了哪些项目、在每个项目前等了多久。这三类信息刚好对应可观测性的三支柱:日志、指标、链路追踪。

日志是一条条离散的事件,适合记录错误堆栈、用户动作;指标是聚合后的数值,适合反映 CPU、QPS、延迟;链路追踪则把一次请求跨多个服务的完整路径串起来,回答“这个请求到底走了哪些路”。它们各有侧重,组合起来才能既看到树木,又看到森林。

在 OTel 中,三者通过统一的数据模型和语义约定对齐:日志里带 trace_id 和 span_id,就能跳转到对应的链路;链路由若干 Span 构成,Span 的属性又可以联动指标。不过,三支柱并不是三个独立系统,而是同一套管道里的三种信号。下面用一条公式说明指标最常见的用途——服务可用性:

所以不要只把日志、指标、链路当三个孤岛。它们就像留言簿、计数器、手环记录,虽然载体不同,但都描述同一批游客的经历;在 OTel 的标准里,三者的关联字段让它们能拼成一张完整的乐园体验地图。

A=SreqNreqA = \displaystyle\frac{S_{\text{req}}}{N_{\text{req}}}
它用成功请求数占总请求数的比例,量化服务到底有多“健康”。
  1. $N_{\text{req}}$一段时间内系统收到的总请求数,相当于园区一天的游客总人数。
    NreqN_{\text{req}}
  2. $S_{\text{req}}$其中处理成功并且响应正确的请求数,相当于顺利玩完并给出好评的游客数。
    SreqS_{\text{req}}
  3. $A$服务可用性,是 $S_{\text{req}}$ 与 $N_{\text{req}}$ 的比值,值越接近 1 越健康。
    A=SreqNreqA = \displaystyle\frac{S_{\text{req}}}{N_{\text{req}}}

核心架构:API、SDK、Exporter

为了给全园区设备统一装感应器,游管委规定每个摊位必须使用标准插头(API)、标准适配器(SDK)和标准传送带(Exporter)。无论摊位后面接什么系统,插头长得都一样。

OTel 的架构顶层是 API,它定义 Tracer、Meter、Logger 等接口;SDK 是实现这些接口的默认工具,负责生成 Span、累计指标、处理采样、管理 Context;Exporter 则负责把生成的数据序列化并发送出去。这种分层让应用可以不依赖任何具体后端。OTel 还为 Java、Python、Go 等主流语言提供自动埋点能力,借助 agent 或库的 instrumentation 自动为 HTTP、数据库等框架生成 Span,减少手动埋点工作量。

除 API/SDK 外,还有几个关键概念:Resource 用来描述数据来自哪个服务、版本、环境;语义约定(Semantic Conventions)规定属性命名,保证不同团队埋点能互相理解。典型的数据流是:应用调用 API → SDK 生成 Span/Metric/Log → Exporter 发送到 OTel Collector 或后端。

这就像每个摊位只要遵守标准插头,里面的电路怎么布置是摊位自己的事;如果以后要更换电表(后端),只需要在配电房改一下线路,不必把摊位重新装修一遍。

Collector管道:接收、处理、导出

游乐城的中央调度室是数据的“快递分拣中心”。摊位上报的数据先到这里,而不是直接冲向不同的档案室。调度室统一收货、分拣、打包,再按目的地把包裹送上不同的车。

在 OTel 中,这个调度室就是 Collector。它是一个独立进程,接收来自多个应用的 OTLP 或专有格式数据;它由 Pipeline(管道)定义处理流程,管道由三部分组成:Receiver 负责收包,Processor 负责加工,Exporter 负责发送。

Processor 可以做很多事情:批处理合并小数据包、过滤无用的属性、给数据打标、限流,以及最重要的——采样。面对海量链路,我们可以只保留一部分样本。比如每秒产生 10000 条链路,只保留 1000 条,那么采样比例就是:

调度室不是简单搬箱子,它可以根据目的地、数据量、敏感程度动态调整路线。正因为有了 Collector,应用侧可以保持轻量,复杂的数据治理都收拢到管道中完成。

p=nsampledntotalp = \displaystyle\frac{n_{\text{sampled}}}{n_{\text{total}}}
$p$ 表示保留样本的比例,$p$ 越小,存储和带宽成本越低,但能看见的细节越少。
  1. $n_{\text{total}}$进入 Collector 的数据总量,比如每秒产生的全量链路数。
    ntotaln_{\text{total}}
  2. $n_{\text{sampled}}$经过采样后实际保留的数据量。
    nsampledn_{\text{sampled}}
  3. $p$采样比例,等于保留量与总量之比,是控制成本与可观测精度的重要旋钮。
    p=nsampledntotalp = \displaystyle\frac{n_{\text{sampled}}}{n_{\text{total}}}

上下文传播:让链路不迷路

想象一位游客在游乐城玩了“穿越魔法城堡”,城堡里的三个项目分别由三个摊位负责。如果每个摊位只记录自己的客流,管理处就无法知道他到底怎么从入口走到出口。要让路线完整,必须有一张游园卡,上面印着同一个游玩团体的暗号,每经过一个项目就盖一个章。

分布式链路追踪的“游园卡”就是 Trace Context。一个请求从服务 A 调用服务 B 时,上游会把当前 trace_id、span_id、采样标记等信息放进请求头,下游拿到后继续生成子 Span,从而把整条调用链串起来。OpenTelemetry 默认支持 W3C Trace Context 标准,在 HTTP 中通过 traceparent 头传递。

核心数据结构是 SpanContext,它包含 TraceId(全局唯一链路号)、SpanId(当前节点号)、TraceFlags(采样标记)和 TraceState(供应商扩展信息)。它们组合起来就是 traceparent 头的格式,可以写成:

除了 traceparent,OTel 还支持 Baggage 传递跨服务的业务属性(例如用户等级、页面来源),这些属性会以独立头部传递,帮助下游服务做出更精细的记录。有了这张标准游园卡,无论游客在魔法城堡里绕多少圈、经过多少摊位,只要每个摊位都按规则盖章,管理处就能在事后完整还原他的游玩路线。这就是上下文传播的意义——让分布式世界里散落的痕迹,重新长成一条完整的轨迹。

T=(v,tid,sid,f)T = (v, t_{\text{id}}, s_{\text{id}}, f)
$T$ 表示 traceparent 头,四个字段用短横线分隔,如 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01。
  1. $v$版本号,当前固定为 00,告诉接收方按哪个版本解析。
    vv
  2. $t_{\text{id}}$32 位十六进制的 TraceId,是一条链路全局唯一的身份标识。
    tidt_{\text{id}}
  3. $s_{\text{id}}$16 位十六进制的 SpanId,标识当前这段调用在链路中的位置。
    sids_{\text{id}}
  4. $f$8 位十六进制的 Flags,最低位为 1 表示这条数据应该被采样记录。
    ff

部署模式:Agent 与 Gateway

调度室该建在哪里?可以直接在每台设备旁边放一个小型收集箱,也可以在一个中心位置建大型调度总站,或者两者都建。游乐城会根据园区大小和每日游客量选择最合适的方案。

OTel Collector 有三种典型部署:Agent 模式——把 Collector 以 sidecar 或 DaemonSet 形式和应用跑在同一台机器上,数据近端采集,延迟低、资源消耗小;Gateway 模式——把 Collector 作为独立集群集中部署,所有 Agent 或应用直接把数据发给它,便于统一脱敏、过滤、聚合与鉴权;混合模式——边缘用 Agent 快速收数,中心用 Gateway 做二次治理,再导出到后端。

选择哪种模式通常看规模和合规要求。小型项目直接 Agent 连后端就够;大型或多集群环境则建议 Agent + Gateway,这样既能在边缘快速处理高频数据,又能在中心统一控制数据出网。混合模式也方便水平扩展:Gateway 可以根据后端压力动态扩容。

游乐城的管理也是这样:小园区可能两台收集箱就够,大型主题乐园则需要在每个片区设收集点,再统一汇总到中央控制室。部署模式没有银弹,只有适合当前规模的配置。

后端对接与生态:数据送往哪里

数据在中央调度室分拣后,要送去不同类型的档案室:人流量档案室看统计图,留言档案室看文字记录,游玩路线档案室看轨迹。每类档案室的存储格式不同,但只要数据已经标准化,送过去就只是换个车的问题。

OTel 的 Exporter 专门负责换车。指标数据可以导出到 Prometheus(通过 Remote Write),链路可以导出到 Jaeger、Tempo,日志可以导出到 ELK/Loki;而支持 OTLP 协议的后端,可以直接接收统一的三种信号。这意味着后端可以替换,埋点不需要重写。

在这个生态里,OTLP 是 OTel 自己的原生协议,传输效率高、支持三种信号;语义约定则保证无论数据去哪一种后端,字段含义都不会扭曲。常见的架构是:应用 SDK → OTLP → Collector → Prometheus/Jaeger/ELK。如果未来公司引入新的可观测平台,只需要调整 Collector 的 exporter 配置。

就像中央调度室知道哪类包裹送往哪个档案室一样,OTel 通过标准协议和 Exporter,让同一份游乐数据可以在不同档案室间自由流转,而不需要每个摊位重新填表。

AI 应用场景

智能运维:统一数据喂给根因分析引擎

阶段:智能运维数据接入

解决的问题:传统监控数据分散在日志、指标、链路三套仓库中,AI 根因分析引擎很难把它们关联起来。OTel 用统一语义和 trace_id 把三类数据串成一条完整时间线,为 AIOps 提供干净、对齐的输入。

具体案例:某电商公司在促销期间,订单服务出现延迟。通过 OTel 采集的链路和日志,根因分析引擎自动找到“第三方支付接口超时”这一共同上游,SRE 原本需要半小时的排查缩短到五分钟。

大规模 Kubernetes 集群统一采集

阶段:数据采集与管道处理

解决的问题:K8s 环境 Pod 频繁重建,传统 agent 配置难统一、数据易丢失。OTel Collector 以 DaemonSet 部署在每个节点,用 Agent 模式采集日志和指标;再用 Gateway 集群做多集群聚合和脱敏,导出到托管 Prometheus 与对象存储。

具体案例:某平台团队在 100 个节点上统一部署 OTel Agent,资源占用比原来 Fluentd + node-exporter 降低约 30%;通过 processor 过滤 /health 探针,指标量减少 40%。

合规与多租户数据治理

阶段:数据治理与出口控制

解决的问题:金融行业敏感数据不能直接出网,每条链路可能携带用户 ID。OTel Gateway 集中做属性脱敏,如把 user_id 替换为哈希,再按租户打标签,分别导出到不同后端。

具体案例:某银行使用 OTel Collector 的 attributes processor 删除请求体中的身份证号,并依据 namespace 将数据路由到不同 Kafka topic,满足合规审计要求。

Python 演示

python58 行
import timeimport uuid  class TelemetryItem:    """Unified container for logs, metrics and traces."""     def __init__(self, kind, name, value=0.0):        self.kind = kind        self.name = name        self.value = value        self.trace_id = uuid.uuid4().hex[:16]        self.span_id = uuid.uuid4().hex[:16]        self.parent_id = ""        self.timestamp = time.time()     def inherit_context(self, parent):        self.trace_id = parent.trace_id        self.parent_id = parent.span_id     def __repr__(self):        return "{0}:{1}(value={2})".format(self.kind, self.name, self.value)  class CollectorPipeline:    """Simplified OpenTelemetry Collector: receive-process-export."""     def __init__(self, name):        self.name = name        self.queue = []        self.result = []     def receive(self, item):        self.queue.append(item)     def process(self, keep):        self.result = [item for item in self.queue if keep(item)]        return self     def export(self):        for item in self.result:            print("[{0}] export {1}".format(self.name, item))        self.queue.clear()        self.result.clear()  if __name__ == "__main__":    pipeline = CollectorPipeline("playground")    parent_span = TelemetryItem("trace", "GET /order", 42)    child_log = TelemetryItem("log", "user.login", 1)    child_log.inherit_context(parent_span)    metric = TelemetryItem("metric", "cpu_usage", 0.31)     pipeline.receive(parent_span)    pipeline.receive(child_log)    pipeline.receive(metric)    pipeline.process(lambda item: item.value >= 0.5 or item.kind == "log")    pipeline.export()
目的:用 Python 模拟一个极简版 OTel 管道,演示日志、指标、链路三类数据经过上下文传播、过滤处理后统一导出的流程。
  1. child_log.inherit_context(parent_span)把父 Span 的 trace_id 和 span_id 复制到日志里,模拟上下文传播。
  2. self.result = [item for item in self.queue if keep(item)]对应 Collector 中 Processor 的过滤逻辑。
  3. pipeline.process(lambda item: item.value >= 0.5 or item.kind == "log")只保留数值较大的请求或日志数据,模拟采样/过滤。
  4. pipeline.export()对应 Exporter,把处理结果送往后端并清空队列。

常见误区

误区我装好 OpenTelemetry 之后,应该能在它的界面里直接看到曲线图和调用链吧?

正解OTel 不是数据展示或存储系统。它的职责是采集、处理和传输数据,好比城市里的统一管道和水表,而不是档案馆或展厅。曲线图、告警要看 Grafana/Prometheus 等后端,链路搜索要看 Jaeger/Tempo 等工具。

为何常见:因为很多文章把 OTel 和数据抓取工具混在一起,又没强调 OTel 本身不带 UI。

误区引入 OTel 我们必须重写所有服务,这是个大工程。

正解OTel 提供了多种语言的 SDK,并支持自动埋点,能自动为 HTTP、数据库等常用库生成 Span。手动埋点只用于补充业务自定义信息,很多服务甚至不用改业务代码。

为何常见:许多人把 OTel 想象成需要手工改每一行代码的旧式埋点,忽略了零代码自动插桩能力。

误区我用了 OTel,为什么日志平台里搜不到 trace_id?

正解将 trace_id/span_id 自动注入日志,需要日志库与 OTel 的日志桥接配合,并在采集时保留这些字段。统一管道只负责传输,字段对齐还要靠配置和语义约定。

为何常见:以为标准协议能解决一切,却不知道日志关联需要额外的 handler 或 formatter。

误区Collector 必须和应用绑在同一台机器上,否则就失效。

正解Collector 是独立进程,既可以 Agent 模式就近部署,也可以 Gateway 模式集中部署,还可以混合。按规模和合规要求选型,不是固定形态。

为何常见:很多教程以 Kubernetes DaemonSet 为例,让人误以为这是唯一模式。

误区链路追踪要保存每一次请求,否则排查问题时找不到现场。

正解全量保存成本极高,通常并不需要。通过头采样或尾采样保留“有错、高延迟、代表性”的请求,同时保留聚合指标反映全局趋势,是可观测性工程的最佳实践。

为何常见:新手容易认为观测数据越全越好,忽略了海量数据下的存储和性能开销。

面试题

Q1. OpenTelemetry 在可观测性体系中的核心定位是什么?

单选题 · easy

  • A.负责存储监控数据
  • B.负责采集传输数据← 正确答案
  • C.负责绘制监控大屏
  • D.负责自动恢复故障

答案:B。OTel 像城市统一管道,主要负责采集、处理和传输数据。存储与可视化由后端系统承担,绘制大屏和自动恢复也不在 OTel 的职责范围内。

Q2. OpenTelemetry 三支柱中,哪种数据适合描述离散事件?

单选题 · easy

  • A.日志← 正确答案
  • B.指标
  • C.链路
  • D.报警

答案:A。日志像留言簿,记录某一个时刻发生了什么;指标是聚合数值;链路记录调用路径。报警不属于 OTel 三支柱。

Q3. 在 OTel 中,负责把数据发送给后端的组件是?

单选题 · easy

  • A.Receiver
  • B.Processor
  • C.Exporter← 正确答案
  • D.Propagator

答案:C。Exporter 是传送带,负责把处理后的数据送往后端;Receiver 负责接收,Processor 负责加工,Propagator 负责上下文传播。

Q4. 按 W3C Trace Context 标准,traceparent 头中不会包含下面哪项?

单选题 · mid

  • A.协议版本号
  • B.TraceId
  • C.Parent SpanId
  • D.服务实例名← 正确答案

答案:D。traceparent 头只包含版本号、TraceId、Parent SpanId 和 Flags。服务实例名属于 Resource 信息,不会出现在 traceparent 中。

Q5. 下面哪些属于 OpenTelemetry Collector 的部署模式?

多选题 · mid

  • A.Agent 模式← 正确答案
  • B.Gateway 模式← 正确答案
  • C.混合模式← 正确答案
  • D.函数计算模式

答案:ABC。Collector 支持 Agent 就近采集、Gateway 集中治理、二者混合的模式;函数计算并不是 OTel 定义的部署模式。

Q6. 关于 OTel SDK 的职责,下列说法正确的是?

单选题 · mid

  • A.它只提供接口不做实现
  • B.它负责采样导出管理← 正确答案
  • C.它负责存储长期数据
  • D.它需要改业务核心逻辑

答案:B。API 是插头标准,SDK 是适配器,负责实现接口、管理采样和导出;存储由后端负责,SDK 不需要改动业务核心逻辑。

Q7. 在大型系统中使用 Gateway 模式部署 Collector,主要收益包括下面哪些?

多选题 · hard

  • A.统一脱敏过滤策略← 正确答案
  • B.聚合多集群数据← 正确答案
  • C.降低应用侧资源消耗← 正确答案
  • D.让后端免运维

答案:ABC。Gateway 集中处理后可以统一治理、多集群聚合,也让应用侧保持轻量;但它不是后端,无法代替后端的存储和运维。

Q8. 要让业务日志与链路追踪自动关联,最佳实践是什么?

单选题 · mid

  • A.把所有日志集中存储
  • B.日志中保留链路上下文← 正确答案
  • C.关闭链路数据采样
  • D.每个服务各自建库

答案:B。在日志中输出 trace_id 和 span_id,是关联日志与链路的通用做法。集中存储、关闭采样、各自建库都不能让日志自动挂到链路上。

Q9. 关于 OTLP 协议,下列描述正确的是?

单选题 · hard

  • A.只能传输链路数据
  • B.只能传输指标数据
  • C.可统一传输三种信号← 正确答案
  • D.不支持二进制编码

答案:C。OTLP 是 OTel 的原生协议,最大的特点是能同时承载日志、指标和链路三种信号,并且支持高效的二进制编码。

Q10. 在超大规模集群中,为了平衡成本与可观测性,哪些做法是合理的?

多选题 · extreme

  • A.Agent 与 Gateway 分层部署← 正确答案
  • B.对高流量服务尾部采样← 正确答案
  • C.用统一语义关联三种信号← 正确答案
  • D.关闭所有日志采集

答案:ABC。分层部署、尾部采样、统一语义约定都是规模化可观测性常用的降本增效手段;关闭所有日志会让排障失去关键线索,不可取。