本文是 Unit 7「可观测性篇」的第 3 章,聚焦 Kubernetes 中的分布式追踪体系。
本章你会学到什么
- 分布式追踪到底解决什么问题
- OpenTelemetry 里
Trace、Span、Context的核心概念 - 在 Kubernetes 中如何接入 Collector 与应用埋点
- 指标、日志、追踪如何做关联分析
1. 为什么需要分布式追踪
在微服务场景里,一个“接口慢”的问题,往往不是单个进程慢,而是一次请求经过 Ingress、Gateway、API 服务、缓存、数据库、消息队列等多个环节之后,某一跳或多跳共同叠加出来的结果。仅看单点日志,你可能知道哪里报错;仅看指标,你可能知道延迟升高了;但你仍然不知道:这次请求到底走了哪些服务、每一跳花了多久、真正的瓶颈卡在哪一层。
Kubernetes 官方对 observability 的描述里也把 trace 作为三大信号之一:它可以把控制面、插件和应用中的操作关系串起来,帮助我们理解端到端请求流、识别延迟瓶颈和异常交互。::cite[419]
所以,分布式追踪最适合解决下面几类问题:
- 慢请求定位:到底是网关慢、应用慢,还是数据库慢
- 跨服务因果分析:一个错误是否由上游超时、重试、熔断引起
- 架构黑盒可视化:把原本“看不见”的调用链画出来
- Metrics 与 Logs 的补盲:当图表只告诉你“慢了”,trace 能告诉你“慢在哪”
2. OpenTelemetry 的核心概念
OpenTelemetry 是当前云原生领域最主流的可观测性标准之一,覆盖 traces、metrics、logs 等多种信号。对于 tracing,先把 3 个词理解透:Trace、Span、Context。::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 里至少有两层价值:
- 应用层 tracing:看业务链路、接口性能、依赖调用
- 平台层 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 常见有两条路线:
- 手工埋点 / SDK 集成:代码里显式接入 OpenTelemetry SDK
- 自动注入(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 一个最实用的关联顺序
当线上反馈“接口慢了”,可以按下面顺序联动排查:
-
先看 Metrics
- QPS 是否突然升高
- 错误率是否上升
- P95 / P99 是否抬高
-
再看 Traces
- 哪条链路最慢
- 是网关、应用逻辑、缓存还是数据库占时长最多
- 是否存在明显的重试、排队、串行调用
-
最后看 Logs
- 对照 trace ID 查出具体异常日志
- 验证是否有 SQL 超时、依赖连接失败、参数异常
8.2 为什么推荐这个顺序
因为:
- Metrics 最适合快速发现“有问题”
- Traces 最适合解释“慢在哪”
- Logs 最适合还原“具体报了什么错”
这三者并不是替代关系,而是从“面”到“线”再到“点”的互补关系。
8.3 关联分析要落地,日志里最好带 trace_id
如果你希望排障效率真正提升,建议做到这两点:
- 应用日志输出 trace ID / span ID
- Grafana / Jaeger / Loki 中支持从 trace 跳日志、从日志回溯 trace
只要这一步做好,很多“看图很像有问题,但不知道具体哪条请求出事”的场景,排查速度会明显提升。
9. 一套适合入门的追踪落地建议
如果你是第一次在 Kubernetes 里落 tracing,建议按下面节奏推进:
第一步:先把链路跑通
- 选 1 个最关键服务
- 接一个最简单的 Collector
- 把 trace 发到 debug exporter 或 Jaeger
- 先确认链路完整可见
第二步:再逐步扩大范围
- 接入网关
- 接入核心 API 服务
- 接入数据库 / MQ 客户端 span
- 接入关键异步任务
第三步:最后再做治理
- 采样率调整
- 敏感字段脱敏
- 高成本 span 限制
- 统一 service name 与 resource attributes 命名规范
10. 本章小结
这一章可以记住 5 句话:
- Metrics 告诉你“系统是不是有问题”,Tracing 告诉你“问题具体卡在哪一跳”。::cite[419]
- OpenTelemetry tracing 的核心是
Trace、Span、Context。::cite[223]::cite[378] - Collector 是 Kubernetes 中最常见的遥测汇聚中间层。::cite[444]
- 自动注入很方便,但要注意
spec.exporter.endpoint等关键配置,别让数据默认发到错误地址。::cite[445] - 真正高效的排障,不是只看 trace,而是 Metrics、Logs、Traces 三者联动。::cite[378]::cite[442]
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!