在前面的文章中,我们已经完成了 Kubernetes 可观测体系中的两个部分:
Metrics 指标 Prometheus
Logs 日志 Loki
Prometheus 可以帮助我们发现:
-
Node CPU 是否过高
-
Memory 是否不足
-
Pod 是否频繁重启
-
服务错误率是否突然升高
Loki 则可以帮助我们进一步查看:
-
数据库连接是否失败
-
配置文件是否缺失
-
应用是否出现异常
-
某个 Pod 在故障前输出了什么日志
但是,当系统由多个服务组成时,仅仅查看 Metrics 和 Logs 仍然不够。
假设一次用户请求需要经过下面几个服务:
用户请求
│
▼
Frontend
│
▼
Backend
│
▼
Database
现在请求耗时突然从:
200 ms
上升到:
2 s
Prometheus 可以发现:
接口延迟上升
Loki 也可能找到一些相关日志。
但是我们仍然需要回答:
这 2 秒究竟消耗在哪个服务上?
是 Frontend 处理缓慢?
还是 Backend 调用了一个很慢的接口?
或者 Database 查询耗费了大量时间?
这正是分布式链路追踪需要解决的问题。
这一篇,我们继续搭建可观测体系的第三部分:
Tracing 链路追踪 Tempo
本篇将在单节点 Kubernetes 实验环境中部署:
-
Tempo:存储和查询 Trace
-
OpenTelemetry Collector:接收、处理和转发 Trace
-
Python Demo:生成最简单的测试 Trace
-
Grafana:查询和展示完整调用链
最终形成下面这条链路:
Demo Application
│
│ OTLP/gRPC 4317
▼
OpenTelemetry Collector
│
│ OTLP/gRPC 4317
▼
Tempo
│
│ HTTP 3200
▼
Grafana Explore
一、什么是 Trace?
一次请求进入系统后,可能会经过多个处理步骤。
例如:
HTTP Request
│
├── Validate Request
│
├── Query Database
│
└── Build Response
在链路追踪系统中,一次完整请求通常称为:
Trace
Trace 内部的每个处理步骤称为:
Span
例如:
Trace: 用户请求
│
├── Span: HTTP Request 320 ms
│
├── Span: Validate Request 10 ms
│
├── Span: Query Database 250 ms
│
└── Span: Build Response 60 ms
通过这些 Span,我们可以直观看出:
Query Database
占用了大部分时间。
因此,Tracing 主要回答:
一次请求经过了哪些步骤?
以及:
时间究竟消耗在哪里?
二、OpenTelemetry 和 Tempo 分别负责什么?
在本实验中,OpenTelemetry 和 Tempo 承担不同的角色。
OpenTelemetry
OpenTelemetry 是一套开放的可观测标准和工具体系。
它可以处理:
Metrics
Logs
Traces
本篇主要使用其中两个部分:
OpenTelemetry SDK
OpenTelemetry Collector
应用通过 OpenTelemetry SDK 创建 Trace 和 Span,然后将数据发送给 OpenTelemetry Collector。
Collector 主要负责:
-
接收应用发送的遥测数据
-
批量处理 Trace
-
添加或修改属性
-
将 Trace 转发给后端存储系统
-
将应用和具体存储后端解耦
Tempo
Tempo 是 Grafana 生态中的分布式链路追踪后端。
它主要负责:
-
接收 Trace
-
存储 Trace
-
根据 Trace ID 查询
-
根据服务名、Span 名称和属性搜索 Trace
-
将查询结果提供给 Grafana
Tempo 支持单体和微服务两种部署模式。
对于入门、开发和小规模环境,Grafana 官方建议使用单体模式;微服务模式更加复杂,适合需要独立扩展各组件的场景。
本实验室的目标仍然是:
单节点
+
低资源占用
+
便于学习
因此采用:
Tempo Monolithic
也就是单体部署模式。
三、整体架构
本篇完整架构如下:
Kubernetes Cluster
Demo Trace Application
│
│ OpenTelemetry SDK
│ OTLP/gRPC :4317
▼
OpenTelemetry Collector
│
│ Batch Processor
│ OTLP/gRPC :4317
▼
Tempo
│
│ Query API :3200
▼
Grafana
这里为什么不让应用直接连接 Tempo?
理论上可以:
Application
│
▼
Tempo
但是在真实系统中,更常见的结构是:
Application
│
▼
OpenTelemetry Collector
│
▼
Tracing Backend
Collector 相当于应用和后端之间的统一中转站。
以后即使将 Tempo 替换成其他 Trace 后端,应用端也不一定需要修改。
Collector 还可以统一完成:
-
数据批处理
-
数据过滤
-
属性补充
-
采样
-
多后端转发
-
重试和队列缓冲
因此,本实验采用更加标准的结构:
Application
↓
Collector
↓
Tempo
↓
Grafana
四、安装 Tempo
前面的 Prometheus 和 Grafana 已经安装在:
monitoring
Namespace 中。
为了方便 Grafana 访问,这一篇也将 Tempo 安装在:
monitoring
Namespace 中。
1. 添加 Grafana Helm Repository
如果前面安装 Loki 时已经添加过,可以直接执行更新:
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
查看 Tempo Chart:
helm search repo grafana/tempo
Grafana 提供了单体和分布式 Tempo Helm 部署方式;对于本实验,使用单体 grafana/tempo Chart 即可。
2. 准备 tempo-values.yaml
创建配置文件:
vim tempo-values.yaml
内容如下:
tempo:
# 关闭匿名使用情况上报
reportingEnabled: false
# 开启 OTLP 接收端口
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# 单节点实验环境不使用持久化存储
persistence:
enabled: false
service:
type: ClusterIP
这里开启了两个 OTLP 接收协议:
4317 OTLP/gRPC
4318 OTLP/HTTP
本篇主要使用:
4317 OTLP/gRPC
Tempo 的 OTLP Receiver 默认可能只监听本地地址,因此在 Kubernetes 中需要明确配置为:
0.0.0.0:4317
0.0.0.0:4318
这样其他 Pod 才能通过 Kubernetes Service 访问。Tempo 官方配置文档也说明,Receiver 默认可能监听 localhost,若需要接收外部容器发送的数据,应配置监听地址。
3. 关于临时存储
这里设置了:
persistence:
enabled: false
这意味着 Tempo 数据只用于实验。
如果 Tempo Pod 被删除或重新创建,已经存储的 Trace 可能丢失。
因此,这套配置适用于:
-
学习 Tempo
-
验证 OTLP 链路
-
测试 Grafana Trace 查询
-
单节点可观测实验室
不适合生产环境。
生产环境通常应该使用:
-
PVC
-
Amazon S3
-
Google Cloud Storage
-
Azure Blob Storage
-
其他对象存储
Tempo 官方也建议生产环境优先使用对象存储;本地文件系统更加适合单体测试和开发场景。
4. 安装 Tempo
执行:
helm install tempo grafana/tempo \
-n monitoring \
-f tempo-values.yaml
查看 Helm Release:
helm list -n monitoring
查看 Pod:
kubectl get pods -n monitoring
正常情况下应该看到类似结果:
tempo-0 1/1 Running
不同版本的 Helm Chart,Pod 名称和容器数量可能略有差异。

5. 查看 Tempo Service
执行:
kubectl get svc -n monitoring
应该可以看到:
tempo
进一步查看端口:
kubectl get svc tempo -n monitoring
结果中通常可以看到:
3200/TCP
4317/TCP
4318/TCP
它们分别对应:
3200 Tempo HTTP 查询接口
4317 OTLP/gRPC
4318 OTLP/HTTP
Tempo 的完整 Kubernetes Service DNS 为:
tempo.monitoring.svc.cluster.local
后续 OpenTelemetry Collector 将数据发送到:
tempo.monitoring.svc.cluster.local:4317
6. 查看 Tempo 日志
执行:
kubectl logs -n monitoring statefulset/tempo --tail=100
如果当前 Chart 创建的不是 StatefulSet,也可以先查看资源:
kubectl get deployment,statefulset -n monitoring
然后根据实际资源名称查看日志。

如果 Tempo 正常启动,日志中不应该持续出现:
address already in use
或者:
connection refused
等错误。
五、安装 OpenTelemetry Collector
Tempo 已经能够接收 Trace,接下来部署 OpenTelemetry Collector。
Collector 官方 Helm Chart 要求显式设置运行模式,可选值包括:
daemonset
deployment
statefulset
本篇只需要一个集中接收 Trace 的 Collector,因此使用:
deployment
官方当前安装示例推荐使用:
otel/opentelemetry-collector-k8s
镜像。
1. 添加 OpenTelemetry Helm Repository
执行:
helm repo add open-telemetry \
https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
查看 Chart:
helm search repo open-telemetry/opentelemetry-collector
2. 创建 observability Namespace
创建专门用于 Collector 和测试应用的 Namespace:
kubectl create namespace observability
如果 Namespace 已经存在,会提示:
AlreadyExists
也可以使用更加幂等的写法:
kubectl create namespace observability \
--dry-run=client -o yaml | kubectl apply -f -
检查:
kubectl get namespace
应该可以看到:
observability
六、准备 Collector 配置
创建配置文件:
vim otel-collector-values.yaml
内容如下:
mode: deployment
image:
repository: otel/opentelemetry-collector-k8s
alternateConfig:
extensions:
health_check:
endpoint: ${env:MY_POD_IP}:13133
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:MY_POD_IP}:4317
http:
endpoint: ${env:MY_POD_IP}:4318
processors:
memory_limiter:
check_interval: 5s
limit_percentage: 80
spike_limit_percentage: 25
batch: {}
exporters:
otlp/tempo:
endpoint: tempo.monitoring.svc.cluster.local:4317
tls:
insecure: true
service:
extensions:
- health_check
pipelines:
traces:
receivers:
- otlp
processors:
- memory_limiter
- batch
exporters:
- otlp/tempo
ports:
otlp:
enabled: true
otlp-http:
enabled: true
jaeger-compact:
enabled: false
jaeger-thrift:
enabled: false
jaeger-grpc:
enabled: false
zipkin:
enabled: false
service:
type: ClusterIP
resources:
limits:
memory: 256Mi
requests:
cpu: 50m
memory: 128Mi
这里使用了:
alternateConfig:
而不是直接使用:
config:
原因是 OpenTelemetry Collector Chart 自带默认配置,其中包括:
-
Logs Pipeline
-
Metrics Pipeline
-
Debug Exporter
-
Jaeger Receiver
-
Zipkin Receiver
如果直接覆盖部分 config,Helm 会将自定义内容与默认内容合并。
对于本篇只处理 Trace 的最小环境来说,可能会引入不必要的配置。
alternateConfig 不与默认配置合并,可以提供一份完全独立的 Collector 配置。但使用这种方式时,必须保留健康检查扩展,否则 Chart 的 Readiness 和 Liveness Probe 可能失败。
1. Receiver
下面的配置表示 Collector 接收 OTLP 数据:
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:MY_POD_IP}:4317
http:
endpoint: ${env:MY_POD_IP}:4318
支持两种协议:
OTLP/gRPC 4317
OTLP/HTTP 4318
2. Processor
这里配置了两个 Processor:
memory_limiter:
batch:
memory_limiter 用于限制 Collector 内存占用。
batch 会将多个 Span 组成批次后再发送,从而减少网络请求次数。
因此,处理过程大致是:
接收 Span
│
▼
检查内存限制
│
▼
批量处理
│
▼
发送给 Tempo
3. Exporter
下面的配置表示将 Trace 发送到 Tempo:
exporters:
otlp/tempo:
endpoint: tempo.monitoring.svc.cluster.local:4317
tls:
insecure: true
由于 Collector 和 Tempo 都运行在同一个 Kubernetes Cluster 内部,因此可以使用 Kubernetes Service DNS:
tempo.monitoring.svc.cluster.local
本实验没有配置 TLS,所以设置:
insecure: true
4. Pipeline
Trace Pipeline 如下:
pipelines:
traces:
receivers:
- otlp
processors:
- memory_limiter
- batch
exporters:
- otlp/tempo
完整的数据流为:
OTLP Receiver
│
▼
Memory Limiter
│
▼
Batch Processor
│
▼
OTLP Tempo Exporter
七、安装 OpenTelemetry Collector
执行:
helm install otel-collector \
open-telemetry/opentelemetry-collector \
-n observability \
-f otel-collector-values.yaml
查看 Helm Release:
helm list -n observability
1. 查看 Pod
执行:
kubectl get pods -n observability
正常情况下可以看到类似结果:
otel-collector-opentelemetry-collector-xxxxxxxxxx-xxxxx 1/1 Running
2. 查看 Deployment
执行:
kubectl get deployment -n observability
应该可以看到:
otel-collector-opentelemetry-collector
3. 查看 Service
执行:
kubectl get svc -n observability
应该可以看到:
otel-collector-opentelemetry-collector
查看具体端口:
kubectl get svc \
otel-collector-opentelemetry-collector \
-n observability
应该包含:
4317/TCP
4318/TCP
完整 Service DNS 为:
otel-collector-opentelemetry-collector.observability.svc.cluster.local
4. 查看 Collector 日志
执行:
kubectl logs \
-n observability \
deployment/otel-collector-opentelemetry-collector \
--tail=100
不同版本的 Collector 日志格式可能略有差异。
正常情况下应该能够看到 OTLP Receiver 和健康检查扩展启动,并且不应该持续出现:
connection refused
或者:
failed to export
等错误。

此时链路的基础设施部分已经完成:
OpenTelemetry Collector
│
▼
Tempo
│
▼
Grafana
但是目前还没有应用产生 Trace。
接下来部署一个最小 Python 应用。
八、创建最小 Trace Demo
为了避免一次部署复杂的 OpenTelemetry Demo 系统,本篇只创建一个最简单的 HTTP 应用。
每次访问:
/
应用会生成一个 Trace,其中包含三个 Span:
http-request
│
├── step-1
└── step-2
两个子步骤分别模拟:
step-1 100 ms
step-2 200 ms
通过这个简单例子,可以直观看到:
-
Trace
-
Root Span
-
Child Span
-
Span Duration
-
Span Attribute
-
Service Name
1. 创建 demo-config.yaml
创建文件:
vim demo-config.yaml
内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-trace-app
namespace: observability
data:
app.py: |
import os
import time
from flask import Flask
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
app = Flask(__name__)
service_name = os.getenv(
"OTEL_SERVICE_NAME",
"demo-trace"
)
otlp_endpoint = os.getenv(
"OTEL_EXPORTER_OTLP_ENDPOINT",
"otel-collector-opentelemetry-collector."
"observability.svc.cluster.local:4317"
)
resource = Resource.create({
"service.name": service_name,
"deployment.environment": "k8s-lab",
"service.version": "1.0.0"
})
provider = TracerProvider(
resource=resource
)
exporter = OTLPSpanExporter(
endpoint=otlp_endpoint,
insecure=True
)
processor = BatchSpanProcessor(
exporter
)
provider.add_span_processor(
processor
)
trace.set_tracer_provider(
provider
)
tracer = trace.get_tracer(
"demo-trace"
)
@app.route("/")
def hello():
with tracer.start_as_current_span(
"http-request"
) as span:
span.set_attribute(
"demo.tag",
"v1"
)
span.set_attribute(
"http.route",
"/"
)
with tracer.start_as_current_span(
"step-1"
):
time.sleep(0.1)
with tracer.start_as_current_span(
"step-2"
):
time.sleep(0.2)
return "hello otel trace\n"
@app.route("/slow")
def slow():
with tracer.start_as_current_span(
"slow-request"
) as span:
span.set_attribute(
"demo.slow",
True
)
with tracer.start_as_current_span(
"slow-operation"
):
time.sleep(1)
return "slow trace generated\n"
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=8080
)
这个程序显式设置了几个 Resource Attribute:
service.name
deployment.environment
service.version
其中最重要的是:
service.name = demo-trace
后续 Grafana 将根据这个属性查询服务。
应用 ConfigMap:
kubectl apply -f demo-config.yaml
检查:
kubectl get configmap -n observability
应该可以看到:
demo-trace-app
九、部署 Demo Application
创建:
vim demo-app.yaml
内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-trace
namespace: observability
spec:
replicas: 1
selector:
matchLabels:
app: demo-trace
template:
metadata:
labels:
app: demo-trace
spec:
containers:
- name: demo-trace
image: python:3.11-slim
command:
- sh
- -c
args:
- |
pip install --no-cache-dir \
flask \
opentelemetry-api \
opentelemetry-sdk \
opentelemetry-exporter-otlp &&
echo "Starting demo trace application" &&
python /app/app.py
env:
- name: OTEL_SERVICE_NAME
value: demo-trace
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: >-
otel-collector-opentelemetry-collector.observability.svc.cluster.local:4317
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
volumeMounts:
- name: app
mountPath: /app
volumes:
- name: app
configMap:
name: demo-trace-app
应用:
kubectl apply -f demo-app.yaml
查看 Deployment:
kubectl get deployment -n observability
查看 Pod:
kubectl get pods -n observability
刚开始可能会看到:
ContainerCreating
随后进入:
Running
由于容器启动时需要执行:
pip install
第一次启动可能需要等待一段时间。

查看应用日志:
kubectl logs \
-n observability \
deployment/demo-trace \
-f
正常情况下应该可以看到:
Starting demo trace application
Running on http://0.0.0.0:8080

十、访问 Demo 并生成 Trace
为了从 Kubernetes Node 访问 Demo,先安装socat,执行:
sudo apt update
sudo apt install -y socat
验证:
command -v socat
应该输出:
/usr/bin/socat

然后使用:
kubectl port-forward \
-n observability \
deployment/demo-trace \
8080:8080
这里直接使用:
deployment/demo-trace
而不是填写具体 Pod 名称。
因为 Pod 名称包含随机字符串,例如:
demo-trace-5ffd95c654-hs448
Pod 重新创建后名称会发生变化,而 Deployment 名称保持稳定。
端口转发成功后,会看到类似输出:
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
保持这个终端不关闭。

重新打开另一个终端,多访问几次:
curl http://localhost:8080
curl http://localhost:8080
curl http://localhost:8080
输出:
hello otel trace
再访问慢请求:
curl http://localhost:8080/slow
输出:
slow trace generated
每执行一次请求,应用都会生成一条新的 Trace。

此时完整链路为:
curl
│
▼
Demo Application
│
│ OpenTelemetry SDK
▼
OpenTelemetry Collector
│
│ OTLP/gRPC
▼
Tempo
正常使用 kubectl port-forward 时,一般不需要额外安装 socat。
只有在特定旧环境或特殊转发工具明确报告缺少 socat 时,才需要单独处理。
十一、在 Grafana 中配置 Tempo
进入 Grafana。

依次打开:
Connections
↓
Add new connection
↓
Tempo
↓
Add new data source

填写 URL:
http://tempo.monitoring.svc.cluster.local:3200
这里使用的是:
Tempo HTTP Query API
而不是 OTLP 数据接收端口。
两者的作用不同:
4317 Collector 向 Tempo 写入 Trace
3200 Grafana 从 Tempo 查询 Trace
本实验中的 Grafana 和 Tempo 都运行在 Kubernetes Cluster 内部,因此 Grafana 可以通过 Kubernetes Service DNS 访问 Tempo。

点击:
Save & Test
如果看到类似提示:
Data source is working
说明 Grafana 已经能够查询 Tempo。

十二、在 Grafana 中查询 Trace
进入:
Explore
选择数据源:
Tempo
1. 使用 Search 页面查询
可以在查询界面中选择:
Service Name
然后选择:
demo-trace
点击:
Run query
应该可以看到刚才生成的 Trace。

注意:这里的Limit默认是20,查询的Trace可能较少,可以自行改大。
2. 使用 TraceQL 查询
也可以切换到 TraceQL 模式,输入:
{ resource.service.name = "demo-trace" }
这里需要注意,TraceQL 的完整查询需要使用花括号。
不是:
service.name = "demo-trace"
而是:
{ resource.service.name = "demo-trace" }
3. 查询普通请求
查询 Root Span:
{ name = "http-request" }
4. 查询慢请求
查询:
{ name = "slow-request" }
或者根据自定义属性查询:
{ span.demo.slow = true }
5. 根据环境查询
查询:
{
resource.deployment.environment = "k8s-lab"
}
6. 根据版本查询
查询:
{
resource.service.version = "1.0.0"
}
十三、查看完整 Trace
点击其中一条 Trace,可以看到类似结构:
demo-trace: http-request
│
├── http-request 300 ms
│
├── step-1 100 ms
│
└── step-2 200 ms
其中:
http-request
是 Root Span。
下面两个是 Child Span:
step-1
step-2
可以看到:
step-1 大约 100 ms
step-2 大约 200 ms
对于慢请求,则可以看到:
slow-request
└── slow-operation 大约 1 s
这就是链路追踪最核心的能力:
将一次请求拆分成多个步骤,并显示每个步骤的耗时。
如果真实系统中出现延迟,就可以判断时间主要消耗在:
-
HTTP 请求处理
-
数据库查询
-
外部接口调用
-
消息队列
-
缓存访问
-
业务计算
十四、Trace、Span 和上下文传播
在本篇 Demo 中,我们创建了一个父 Span:
with tracer.start_as_current_span(
"http-request"
):
然后在父 Span 内创建两个子 Span:
with tracer.start_as_current_span(
"step-1"
):
以及:
with tracer.start_as_current_span(
"step-2"
):
OpenTelemetry 会自动识别当前上下文,因此形成:
http-request
│
├── step-1
└── step-2
这就是 Span 上下文传播。
如果没有正确传播上下文,可能会变成三条互不相关的 Trace:
Trace A http-request
Trace B step-1
Trace C step-2
而不是一条完整调用链。
在真正的分布式系统中,上下文还需要通过 HTTP Header 在服务之间传播。
常见 Header 为:
traceparent
例如:
Frontend
│
│ traceparent
▼
Backend
│
│ traceparent
▼
Database Service
这样不同服务产生的 Span 才能被组合成同一条 Trace。
本篇 Demo 只在一个进程内部创建父子 Span,因此 OpenTelemetry SDK 可以自动处理上下文。
十五、为什么使用 Collector,而不是直接写入 Tempo?
现在的链路是:
Application
│
▼
Collector
│
▼
Tempo
看起来比直接连接 Tempo 多了一层。
但是 Collector 带来了几个重要好处。
1. 应用与存储后端解耦
应用只需要知道:
OTLP Endpoint
不需要了解 Tempo 的具体实现。
以后后端变更时,可以只修改 Collector。
2. 批量发送
Collector 使用:
batch processor
将多个 Span 合并后发送,减少网络请求次数。
3. 数据处理
Collector 可以在发送前:
-
删除敏感字段
-
添加 Kubernetes Metadata
-
修改属性
-
丢弃无用数据
-
对 Trace 进行采样
4. 多后端输出
同一份 Trace 可以同时发送到多个系统:
Application
│
▼
Collector
│ │
▼ ▼
Tempo Another Backend
5. 统一入口
多个应用可以统一发送到 Collector:
Frontend ──┐
Backend ──┼──► Collector ──► Tempo
Worker ──┘
应用不需要分别维护后端连接。
十六、清理测试资源
本篇创建的:
demo-trace ConfigMap
demo-trace Deployment
主要用于生成测试 Trace。
确认已经能够在 Grafana 中查询 Trace 后,可以删除:
kubectl delete -f demo-app.yaml
kubectl delete -f demo-config.yaml
检查:
kubectl get pods -n observability
此时应该只保留 OpenTelemetry Collector。

Collector 和 Tempo 可以继续保留,因为后续还可以用于:
-
接入真实应用
-
演示跨服务 Trace
-
将 Trace 与 Logs 关联
-
将 Trace 与 Metrics 关联
-
进行故障注入实验
如果希望删除整个 Collector 和 Demo 环境,可以执行:
helm uninstall otel-collector \
-n observability
kubectl delete namespace observability
如果还需要删除 Tempo:
helm uninstall tempo \
-n monitoring
不过本系列后续还会继续使用 Tempo,因此暂时建议保留:
Tempo
OpenTelemetry Collector
Grafana
本系列采用的资源管理原则仍然是:
基础设施组件:继续保留
临时测试业务:验证完成后删除
这样既能保持实验连续性,也可以避免无用 Pod 不断堆积。
十七、小结:Metrics、Logs 和 Tracing
到这里,单节点 Kubernetes 可观测实验室已经完成了三项核心能力。
Metrics
Kubernetes Metrics
│
▼
Prometheus
│
▼
Grafana
Metrics 主要回答:
系统发生了什么?
例如:
-
CPU 是否过高
-
Memory 是否不足
-
Pod 是否频繁重启
-
请求延迟是否上升
Logs
Pod stdout / stderr
│
▼
Fluent Bit
│
▼
Loki
│
▼
Grafana
Logs 主要回答:
为什么会出现问题?
例如:
-
数据库连接失败
-
配置文件缺失
-
权限错误
-
应用异常退出
Tracing
Application
│
▼
OpenTelemetry Collector
│
▼
Tempo
│
▼
Grafana
Tracing 主要回答:
问题发生在哪个调用步骤?
例如:
-
哪个服务最慢
-
哪次数据库查询耗时过长
-
请求经过了哪些服务
-
某个步骤花费了多长时间
将三者放在一起:
Metrics
+
Logs
+
Tracing
就形成了一个相对完整的可观测体系。
可以简单理解为:
Metrics 发现问题
Logs 解释问题
Tracing 定位问题
现在,我们的单节点 Kubernetes 实验室已经具备:
Prometheus
Grafana
Loki
Fluent Bit
Tempo
OpenTelemetry Collector
完整链路为:
Metrics ──► Prometheus ──┐
│
Logs ─────► Loki ────────┼──► Grafana
│
Traces ───► Tempo ───────┘
当然,目前三种数据仍然相对独立。
真正更有价值的可观测体验,是将它们关联起来:
从 Metrics 发现延迟升高
│
▼
进入对应 Trace
│
▼
定位最慢 Span
│
▼
查看相关 Pod 日志
这也是后续可以继续完善的方向。





