返回首页

7.3 Tracing:OpenTelemetry

本文是 Unit 7「可观测性篇」的第 3 章,聚焦 Kubernetes 中的分布式追踪体系。

本章你会学到什么

  • 分布式追踪到底解决什么问题
  • OpenTelemetry 里 TraceSpanContext 的核心概念
  • 在 Kubernetes 中如何接入 Collector 与应用埋点
  • 指标、日志、追踪如何做关联分析

1. 为什么需要分布式追踪

在微服务场景里,一个“接口慢”的问题,往往不是单个进程慢,而是一次请求经过 Ingress、Gateway、API 服务、缓存、数据库、消息队列等多个环节之后,某一跳或多跳共同叠加出来的结果。仅看单点日志,你可能知道哪里报错;仅看指标,你可能知道延迟升高了;但你仍然不知道:这次请求到底走了哪些服务、每一跳花了多久、真正的瓶颈卡在哪一层。

Kubernetes 官方对 observability 的描述里也把 trace 作为三大信号之一:它可以把控制面、插件和应用中的操作关系串起来,帮助我们理解端到端请求流、识别延迟瓶颈和异常交互。::cite[419]

所以,分布式追踪最适合解决下面几类问题:

  1. 慢请求定位:到底是网关慢、应用慢,还是数据库慢
  2. 跨服务因果分析:一个错误是否由上游超时、重试、熔断引起
  3. 架构黑盒可视化:把原本“看不见”的调用链画出来
  4. Metrics 与 Logs 的补盲:当图表只告诉你“慢了”,trace 能告诉你“慢在哪”

2. OpenTelemetry 的核心概念

OpenTelemetry 是当前云原生领域最主流的可观测性标准之一,覆盖 traces、metrics、logs 等多种信号。对于 tracing,先把 3 个词理解透:TraceSpanContext。::cite[223]::cite[378]

2.1 Trace:一次完整请求的全链路记录

可以把一个 Trace 理解为“某次用户请求的全程录像”。只要这次请求在不同服务、不同进程之间传播,同一个 Trace 就会把这些片段串起来。OpenTelemetry 文档也强调:多个 span 通过共同的 trace_id 和父子关系,就构成了一条完整的 trace。::cite[223]

2.2 Span:链路中的一个操作单元

OpenTelemetry 官方文档指出,Span 是一段工作单元或一次操作,是 Trace 的基本构件。一个 HTTP 请求处理、一次数据库查询、一次远程 RPC 调用,都可以对应一个 span。span 里通常会包含:开始时间、结束时间、属性(attributes)、事件(events)、状态(status)、父子关系等信息。::cite[223]

你可以把它理解成:

  • Trace = 一整条故事线
  • Span = 故事线里的一个场景

2.3 Context:让链路不断开的关键机制

OpenTelemetry 官方把 Context Propagation 描述为“使分布式追踪成为可能的核心概念”。它的作用是把 trace ID、span ID 等上下文信息从服务 A 传给服务 B,让下游能够创建属于同一条 trace 的新 span。这样,不同服务产生的片段就能被重新拼接成一条完整调用链。::cite[223]::cite[378]

官方文档还特别指出:有了 context propagation,traces、metrics 和 logs 也可以相互关联,而不只是 trace 内部自己串起来。::cite[378]

2.4 你还需要认识的几个组件

除了 Trace / Span / Context,OpenTelemetry 里还有 3 个高频概念:

  • TracerProvider:负责创建 Tracer,相当于 tracing 的初始化入口。::cite[223]
  • Tracer:负责创建 span。::cite[223]
  • Exporter:负责把追踪数据发送到 Collector 或后端。::cite[223]

3. Kubernetes 中的 Tracing:不只是应用,控制面也能发 traces

很多人一提 tracing,只想到业务应用。其实 Kubernetes 官方文档已经说明:Kubernetes 系统组件也可以通过 OpenTelemetry Protocol(OTLP)和 gRPC exporter 发出 traces,并由 OpenTelemetry Collector 收集后转发到后端。::cite[225]

这件事很重要,因为它意味着 tracing 在 Kubernetes 里至少有两层价值:

  1. 应用层 tracing:看业务链路、接口性能、依赖调用
  2. 平台层 tracing:看 apiserver、控制器、运行时等系统组件的时延关系

对平台工程师来说,这能把“问题到底卡在业务应用,还是卡在 Kubernetes 平台层”分得更清楚。

4. OpenTelemetry 在 Kubernetes 中的典型架构

最常见的落地架构一般长这样:

应用 SDK / 自动注入 Agent
        ↓
OpenTelemetry Collector
        ↓
Jaeger / Tempo / Zipkin / Vendor Backend

如果把它放到 Kubernetes 里,可以进一步细分为:

  • 应用侧:应用通过 SDK 或自动注入方式产生 spans
  • 采集侧:OpenTelemetry Collector 负责接收、处理、批量发送
  • 后端侧:Jaeger、Tempo 等负责存储和查询
  • 可视化侧:Grafana、Jaeger UI 等展示链路图和时延瀑布图

OpenTelemetry 官方的 Kubernetes Operator 文档也明确说明:Operator 负责管理 Collector,以及工作负载的自动埋点(auto-instrumentation)。::cite[444]

5. 实操:在 Kubernetes 中接入 Collector

5.1 安装 OpenTelemetry Operator

OpenTelemetry 官方文档给出的安装方式很直接:先安装 cert-manager,然后应用 operator 的清单。::cite[444]

kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

5.2 创建一个最小可用的 Collector

下面这份 YAML 与官方文档示例思路一致:Collector 暴露 OTLP gRPC / HTTP 接收端口,经过基础处理后,把 trace 输出到调试 exporter。学习环境里这样最容易验证链路是否通。::cite[444]

apiVersion: v1
kind: Namespace
metadata:
  name: tracing
---
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
  namespace: tracing
spec:
  mode: deployment
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

    processors:
      memory_limiter:
        check_interval: 1s
        limit_percentage: 75
        spike_limit_percentage: 15
      batch: {}

    exporters:
      debug: {}

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [debug]

5.3 如果要接 Jaeger / Tempo,只需要替换 exporter

例如导出到 Jaeger OTLP 入口:

exporters:
  otlp:
    endpoint: jaeger-collector.tracing.svc.cluster.local:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp]

6. 实操:让应用把 Trace 发给 Collector

应用接入 tracing 常见有两条路线:

  1. 手工埋点 / SDK 集成:代码里显式接入 OpenTelemetry SDK
  2. 自动注入(auto-instrumentation):由 Operator 注入 agent,尽量少改代码

6.1 先讲最稳妥的一种:应用已集成 SDK,只在 Kubernetes 中配置出口

如果你的应用镜像里已经集成了 OpenTelemetry SDK,那么在 Kubernetes 中往往只需要配置几个环境变量,就能把 spans 发到 Collector:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
  namespace: tracing
spec:
  replicas: 2
  selector:
    matchLabels:
      app: checkout
  template:
    metadata:
      labels:
        app: checkout
    spec:
      containers:
        - name: checkout
          image: ghcr.io/example/checkout:1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: OTEL_SERVICE_NAME
              value: checkout
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://otel-collector-collector.tracing.svc.cluster.local:4318
            - name: OTEL_EXPORTER_OTLP_PROTOCOL
              value: http/protobuf
            - name: OTEL_RESOURCE_ATTRIBUTES
              value: deployment.environment=demo,k8s.namespace.name=tracing
            - name: OTEL_TRACES_SAMPLER
              value: parentbased_traceidratio
            - name: OTEL_TRACES_SAMPLER_ARG
              value: "1.0"

说明:这里假设镜像内部已经完成 SDK 埋点;在真实项目里,代码侧还需要初始化 TracerProvider、Exporter 和框架中间件。

6.2 自动注入怎么理解

OpenTelemetry Operator 支持自动注入。官方文档说明,Instrumentation 资源里的 spec.exporter.endpoint 用来定义遥测数据发送到哪里;如果不写,默认会回落到 http://localhost:4317,这通常并不是你想要的结果。::cite[445]

下面给出一个教学用最小示例:

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: default-instrumentation
  namespace: tracing
spec:
  exporter:
    endpoint: http://otel-collector-collector.tracing.svc.cluster.local:4317
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment
  namespace: tracing
spec:
  replicas: 1
  selector:
    matchLabels:
      app: payment
  template:
    metadata:
      labels:
        app: payment
      annotations:
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
        - name: payment
          image: ghcr.io/example/payment:1.0.0
          ports:
            - containerPort: 8080

说明:自动注入的具体注解键会随语言栈不同而变化,例如 Java、Python、Node.js、.NET 的接入方式不完全一样;如果你追求最可控的行为,建议从手工埋点开始,再逐步引入自动注入。

7. Kubernetes 系统组件接入 tracing 的思路

Kubernetes 官方系统追踪文档指出,系统组件默认使用 OTLP gRPC exporter,默认端口是 4317,可以通过 OpenTelemetry Collector 收集,也可以直接发到后端。::cite[225]

一个教学用的 Collector 接收配置示例如下:

receivers:
  otlp:
    protocols:
      grpc: {}

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug]

对学习者来说,知道这件事就够了:Tracing 不是只给业务微服务准备的,平台层也能用。

8. 指标、日志、追踪如何做关联分析

OpenTelemetry 的 context propagation 文档明确指出,传播上下文后,traces、metrics、logs 可以彼此关联;OpenTelemetry 日志规范也强调了 log correlation 的价值,即日志可以在时间、Trace ID、Span ID 等维度上与其他信号形成关联。::cite[378]::cite[442]

8.1 一个最实用的关联顺序

当线上反馈“接口慢了”,可以按下面顺序联动排查:

  1. 先看 Metrics

    • QPS 是否突然升高
    • 错误率是否上升
    • P95 / P99 是否抬高
  2. 再看 Traces

    • 哪条链路最慢
    • 是网关、应用逻辑、缓存还是数据库占时长最多
    • 是否存在明显的重试、排队、串行调用
  3. 最后看 Logs

    • 对照 trace ID 查出具体异常日志
    • 验证是否有 SQL 超时、依赖连接失败、参数异常

8.2 为什么推荐这个顺序

因为:

  • Metrics 最适合快速发现“有问题”
  • Traces 最适合解释“慢在哪”
  • Logs 最适合还原“具体报了什么错”

这三者并不是替代关系,而是从“面”到“线”再到“点”的互补关系。

8.3 关联分析要落地,日志里最好带 trace_id

如果你希望排障效率真正提升,建议做到这两点:

  1. 应用日志输出 trace ID / span ID
  2. Grafana / Jaeger / Loki 中支持从 trace 跳日志、从日志回溯 trace

只要这一步做好,很多“看图很像有问题,但不知道具体哪条请求出事”的场景,排查速度会明显提升。

9. 一套适合入门的追踪落地建议

如果你是第一次在 Kubernetes 里落 tracing,建议按下面节奏推进:

第一步:先把链路跑通

  • 选 1 个最关键服务
  • 接一个最简单的 Collector
  • 把 trace 发到 debug exporter 或 Jaeger
  • 先确认链路完整可见

第二步:再逐步扩大范围

  • 接入网关
  • 接入核心 API 服务
  • 接入数据库 / MQ 客户端 span
  • 接入关键异步任务

第三步:最后再做治理

  • 采样率调整
  • 敏感字段脱敏
  • 高成本 span 限制
  • 统一 service name 与 resource attributes 命名规范

10. 本章小结

这一章可以记住 5 句话:

  1. Metrics 告诉你“系统是不是有问题”,Tracing 告诉你“问题具体卡在哪一跳”。::cite[419]
  2. OpenTelemetry tracing 的核心是 TraceSpanContext。::cite[223]::cite[378]
  3. Collector 是 Kubernetes 中最常见的遥测汇聚中间层。::cite[444]
  4. 自动注入很方便,但要注意 spec.exporter.endpoint 等关键配置,别让数据默认发到错误地址。::cite[445]
  5. 真正高效的排障,不是只看 trace,而是 Metrics、Logs、Traces 三者联动。::cite[378]::cite[442]

📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

7.2 Logging:Loki + Promtail

下一篇

8.1 Operator 模式与 CRD 开发(Go + kubebuilder)