mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1586 字
5 分钟
OpenTelemetry 架构
2025-08-19

如果说可观测性是一座大厦,三大信号是建材,那么 OpenTelemetry 就是建筑标准——它定义了信号如何产生、如何传播、如何采集、如何导出。没有 OTel,每个团队用不同的 SDK、不同的格式、不同的 Collector,信号之间无法关联;有了 OTel,所有信号遵循统一的 API 和协议,跨服务、跨语言、跨平台无缝协作。

OpenTelemetry 不是又一个可观测性工具,而是可观测性的TCP/IP——它不关心你用什么存储后端,只关心数据如何产生和传输。

一、OpenTelemetry 的定位#

1.1 从碎片化到标准化#

在 OTel 出现之前,可观测性 SDK 是碎片化的:

信号常见 SDK问题
追踪Jaeger SDK、Zipkin SDK、AWS X-Ray SDK格式不兼容,传播协议不同
指标Prometheus SDK、StatsD SDK、DogStatsD SDK数据模型不同,导出格式不同
日志各语言自带日志库无标准结构化格式

OTel 统一了三大信号的 API 和 SDK,让应用代码与后端实现解耦:

graph TB subgraph 之前["碎片化"] A1["Jaeger SDK"] --> J["Jaeger Backend"] A2["Zipkin SDK"] --> Z["Zipkin Backend"] A3["Prom SDK"] --> P["Prometheus"] end subgraph 之后["OTel 标准化"] B1["OTel SDK"] --> OC["OTel Collector"] OC --> J2["Jaeger"] OC --> P2["Prometheus"] OC --> T["Tempo"] OC --> L["Loki"] end style 之前 fill:#ffcdd2,stroke:#c62828 style 之后 fill:#c8e6c9,stroke:#2e7d32

1.2 OTel 的核心价值#

价值说明
厂商中立应用代码不绑定任何后端
统一 API追踪、指标、日志共享同一套 API
自动仪器化无需修改业务代码即可产生信号
语义约定标准化的属性名称,确保跨服务一致性
OTLP 协议统一的数据传输协议
Collector统一的数据采集、处理、路由

二、OTel 三层架构#

2.1 API → SDK → Collector#

graph TB subgraph API层[" API 层 — 接口定义"] API_T["Tracer API"] API_M["Meter API"] API_L["Logger API"] end subgraph SDK层[" SDK 层 — 实现与导出"] SDK_T["TracerProvider<br/>SpanProcessor"] SDK_M["MeterProvider<br/>MetricReader"] SDK_L["LoggerProvider<br/>LogProcessor"] EXPORT["OTLP Exporter"] end subgraph Collector层[" Collector 层 — 采集与路由"] RECV["Receivers"] PROC["Processors"] EXP["Exporters"] end API_T --> SDK_T API_M --> SDK_M API_L --> SDK_L SDK_T --> EXPORT SDK_M --> EXPORT SDK_L --> EXPORT EXPORT -->|"OTLP"| RECV RECV --> PROC --> EXP style API层 fill:#e8eaf6,stroke:#283593 style SDK层 fill:#e0f2f1,stroke:#00695c style Collector层 fill:#fff3e0,stroke:#e65100

2.2 API 层#

API 层定义了接口,应用代码只依赖 API,不依赖具体实现:

import (
"go.opentelemetry.io/otel/trace" // Tracer API
"go.opentelemetry.io/otel/metric" // Meter API
"go.opentelemetry.io/otel/log" // Logger API(稳定化中)
)
// 应用代码只使用 API,不关心后端实现
func processOrder(ctx context.Context, order Order) error {
// 追踪 API
ctx, span := otel.Tracer("order-service").Start(ctx, "ProcessOrder")
defer span.End()
// 指标 API
requestCounter.Add(ctx, 1,
metric.WithAttributes(semconv.HTTPMethodKey.String("POST")))
// 日志 API
logger.Info(ctx, "Order processed",
log.WithAttributes(log.String("order_id", order.ID)))
return nil
}

2.3 SDK 层#

SDK 层实现了 API 接口,负责数据采集、处理和导出:

import (
"go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
)
func initOTel(ctx context.Context) (func(), error) {
// 创建 OTLP Exporter
traceExporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
metricExporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint("otel-collector:4317"),
otlpmetricgrpc.WithInsecure(),
)
// 创建 TracerProvider
tp := trace.NewTracerProvider(
trace.WithBatcher(traceExporter,
trace.WithBatchTimeout(5*time.Second),
trace.WithMaxExportBatchSize(512),
),
trace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("order-service"),
semconv.ServiceVersionKey.String("v2.3.1"),
)),
)
otel.SetTracerProvider(tp)
// 创建 MeterProvider
mp, err := metric.NewMeterProvider(
metric.WithReader(metric.NewPeriodicReader(metricExporter,
metric.WithInterval(30*time.Second),
)),
)
otel.SetMeterProvider(mp)
return func() {
tp.Shutdown(ctx)
mp.Shutdown(ctx)
}, nil
}

2.4 SpanProcessor 管道#

SpanProcessor 是 SDK 层的核心扩展点,定义了 Span 的处理管道:

graph LR SPAN["Span 创建"] --> SP1["SpanProcessor 1<br/>Batching"] SP1 --> SP2["SpanProcessor 2<br/>Filtering"] SP2 --> EXPORT["OTLP Exporter"] style SPAN fill:#e3f2fd,stroke:#1565c0 style SP1 fill:#e8f5e9,stroke:#2e7d32 style SP2 fill:#fff3e0,stroke:#e65100 style EXPORT fill:#fce4ec,stroke:#880e4f
SpanProcessor功能适用场景
BatchSpanProcessor批量导出,减少网络开销生产环境(推荐)
SimpleSpanProcessor同步逐条导出调试/开发环境
FilterSpanProcessor过滤不需要的 Span移除健康检查 Span
MultiSpanProcessor组合多个 Processor复杂处理管道

三、OTel Collector#

3.1 Collector 架构#

OTel Collector 是可观测性数据管道的核心组件,采用 Receiver → Processor → Exporter 的管道架构:

graph TB subgraph Receivers[" Receivers — 接收数据"] R1["OTLP<br/>gRPC:4317<br/>HTTP:4318"] R2["Prometheus<br/>Scrape"] R3["Jaeger<br/>UDP/HTTP"] R4["FluentForward<br/>TCP"] end subgraph Processors[" Processors — 处理数据"] P1["Batch"] P2["Filter"] P3["Transform"] P4["Sampling"] P5["K8s Attributes"] end subgraph Exporters[" Exporters — 导出数据"] E1["OTLP<br/>→ Tempo"] E2["Prometheus<br/>→ VM/Mimir"] E3["Loki<br/>→ Loki"] E4["Elasticsearch"] end R1 --> P1 R2 --> P1 R3 --> P1 R4 --> P1 P1 --> P2 --> P3 --> P4 --> P5 P5 --> E1 P5 --> E2 P5 --> E3 P5 --> E4 style Receivers fill:#e3f2fd,stroke:#1565c0 style Processors fill:#e0f2f1,stroke:#00695c style Exporters fill:#fff3e0,stroke:#e65100

3.2 Collector 部署模式#

模式架构优点缺点适用场景
Sidecar每个 Pod 一个 Collector隔离性好资源开销大安全要求高
DaemonSet每个节点一个 Collector资源效率高单点故障风险Kubernetes 生产环境
Gateway集中式 Collector易管理、可扩展网络跳数多大规模集群
# Kubernetes DaemonSet 部署
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: otel-collector-agent
spec:
selector:
matchLabels:
app: otel-collector
template:
spec:
containers:
- name: otel-collector
image: otel/opentelemetry-collector-contrib:0.96.0
command: ["--config=/etc/otelcol/config.yaml"]
volumeMounts:
- name: config
mountPath: /etc/otelcol
resources:
limits:
memory: 512Mi
cpu: 200m
requests:
memory: 256Mi
cpu: 100m
volumes:
- name: config
configMap:
name: otel-collector-config

3.3 Collector 配置实战#

otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
max_recv_msg_size_mib: 16
http:
endpoint: 0.0.0.0:4318
processors:
# 批量处理:减少网络请求次数
batch:
send_batch_size: 1024
send_batch_max_size: 2048
timeout: 5s
# K8s 属性注入:自动添加 Pod/Deployment 信息
k8sattributes:
extract:
metadata:
- k8s.namespace.name
- k8s.deployment.name
- k8s.pod.name
# 过滤:移除健康检查 Span
filter:
error_mode: ignore
traces:
span:
- 'attributes["http.route"] == "/healthz"'
- 'attributes["http.route"] == "/readyz"'
# 内存限制:防止 OOM
memory_limiter:
check_interval: 5s
limit_mib: 400
spike_limit_mib: 100
exporters:
# 追踪 → Tempo
otlphttp/traces:
endpoint: http://tempo:4318
# 指标 → Prometheus
prometheus:
endpoint: 0.0.0.0:8889
namespace: otel
# 日志 → Loki
loki:
endpoint: http://loki:3100/loki/api/v1/push
default_labels_enabled:
exporter: false
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, k8sattributes, filter, batch]
exporters: [otlphttp/traces]
metrics:
receivers: [otlp]
processors: [memory_limiter, k8sattributes, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, k8sattributes, batch]
exporters: [loki]
# 健康检查和监控
extensions: [health_check, pprof, zpages]
extensions:
health_check:
endpoint: 0.0.0.0:13133
pprof:
endpoint: 0.0.0.0:1777
zpages:
endpoint: 0.0.0.0:55679

四、自动仪器化#

4.1 原理#

自动仪器化(Auto-Instrumentation)通过拦截框架/库的调用,自动产生追踪、指标和日志,无需修改业务代码:

graph TB subgraph 应用代码[" 应用代码(无需修改)"] CODE["func handleOrder() {<br/> db.Query()<br/> http.Get()<br/>}"] end subgraph 仪器化层[" 自动仪器化层"] HTTP["HTTP 中间件<br/>自动创建 Span"] DB["DB 驱动包装<br/>自动创建 Span"] RPC["gRPC 拦截器<br/>自动创建 Span"] end CODE --> HTTP CODE --> DB CODE --> RPC HTTP --> SDK["OTel SDK"] DB --> SDK RPC --> SDK style 应用代码 fill:#e8eaf6,stroke:#283593 style 仪器化层 fill:#e0f2f1,stroke:#00695c

4.2 Go 自动仪器化#

# 使用 otel-go 自动仪器化
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
go get go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
go get go.opentelemetry.io/contrib/instrumentation/runtime
# HTTP 自动仪器化
import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func main() {
// 包装 HTTP Handler,自动创建 Span
handler := otelhttp.NewHandler(
http.HandlerFunc(handleOrder),
"order-handler",
otelhttp.WithMessageEvents(otelhttp.Read, otelhttp.Write),
)
http.Handle("/api/v1/orders", handler)
http.ListenAndServe(":8080", nil)
}

4.3 Java 自动仪器化#

# 使用 OTel Java Agent(零代码修改)
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=order-service \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-Dotel.instrumentation.common.enabled=true \
-jar my-app.jar

4.4 Python 自动仪器化#

# 使用 OTel Python 自动仪器化
from opentelemetry.instrumentation.auto_instrumentation import sitecustomize
# 或手动初始化
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
FlaskInstrumentor().instrument()
RequestsInstrumentor().instrument()
SQLAlchemyInstrumentor().instrument()

4.5 各语言自动仪器化支持#

语言方式覆盖范围性能开销
JavaJava AgentHTTP、gRPC、JDBC、Kafka 等5-15%
Python装饰器/自动Flask、Django、requests 等5-10%
Go包装函数net/http、gRPC、database/sql3-8%
Node.js模块补丁Express、HTTP、MongoDB 等5-10%
.NETCLR ProfilerASP.NET、HttpClient、SQL5-15%
Note

自动仪器化的性能开销通常在 3-15%,对于大多数生产环境是可接受的。但如果你的服务对延迟极度敏感(如交易系统),建议只仪器化关键路径,而非全量自动仪器化。

五、OTLP 协议#

5.1 OTLP 设计#

OTLP(OpenTelemetry Protocol)是 OTel 定义的统一数据传输协议,支持 gRPC 和 HTTP 两种传输方式:

维度OTLP/gRPCOTLP/HTTP
传输gRPC (HTTP/2)HTTP/1.1 or HTTP/2
编码Protobuf 二进制Protobuf 二进制 or JSON
性能高(二进制 + 多路复用)
兼容性需要 gRPC 支持任何 HTTP 客户端
推荐服务间通信浏览器/边缘场景

5.2 OTLP 数据模型#

// 简化的 OTLP 数据模型
message ExportTraceServiceRequest {
repeated ResourceSpans resource_spans = 1;
}
message ResourceSpans {
Resource resource = 1;
repeated ScopeSpans scope_spans = 2;
}
message ScopeSpans {
InstrumentationScope scope = 1;
repeated Span spans = 2;
}
message Span {
string trace_id = 1;
string span_id = 2;
string parent_span_id = 3;
string name = 4;
SpanKind kind = 5;
fixed64 start_time_unix_nano = 6;
fixed64 end_time_unix_nano = 7;
repeated KeyValue attributes = 8;
SpanStatus status = 9;
repeated Event events = 10;
repeated Link links = 11;
}

六、语义约定#

6.1 为什么需要语义约定?#

语义约定(Semantic Conventions)定义了标准化的属性名称和值,确保跨服务、跨语言的一致性:

// 使用语义约定
span.SetAttributes(
semconv.HTTPMethodKey.String("GET"),
semconv.HTTPStatusCodeKey.Int(200),
semconv.HTTPRouteKey.String("/api/v1/orders"),
semconv.DBSystemKey.String("postgresql"),
semconv.DBOperationKey.String("SELECT"),
)
// 自定义属性名(不一致)
span.SetAttributes(
attribute.String("method", "GET"),
attribute.Int("status", 200),
attribute.String("route", "/api/v1/orders"),
attribute.String("db", "pg"),
attribute.String("op", "select"),
)

6.2 核心语义约定#

领域属性说明
HTTPhttp.method请求方法
HTTPhttp.status_code响应状态码
HTTPhttp.route路由模板(非完整 URL)
HTTPhttp.url完整 URL
Databasedb.system数据库类型
Databasedb.operation操作类型
Databasedb.statementSQL 语句
Messagingmessaging.system消息系统
Messagingmessaging.destination目标主题/队列
RPCrpc.systemRPC 系统
RPCrpc.methodRPC 方法
Resourceservice.name服务名称
Resourceservice.version服务版本

七、动手实践:搭建 OTel 采集管道#

7.1 部署 OTel Collector#

docker compose up -d otel-collector
# 验证 Collector 运行
curl http://localhost:13133/

7.2 验证数据流#

# 发送追踪 → Collector → Tempo
curl -X POST http://localhost:4318/v1/traces \
-H "Content-Type: application/json" \
-d '{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"test"}}]},"scopeSpans":[{"spans":[{"traceId":"01234567890123456789012345678901","spanId":"0123456789012345","name":"test","kind":1,"startTimeUnixNano":"1700000000000000000","endTimeUnixNano":"1700000001000000000"}]}]}]}'
# 发送指标 → Collector → Prometheus
curl -X POST http://localhost:4318/v1/metrics \
-H "Content-Type: application/json" \
-d '{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"test"}}]},"scopeMetrics":[{"metrics":[{"name":"test.counter","gauge":{"dataPoints":[{"asDouble":1,"timeUnixNano":"1700000000000000000"}]}}]}]}]}'
# 发送日志 → Collector → Loki
curl -X POST http://localhost:4318/v1/logs \
-H "Content-Type: application/json" \
-d '{"resourceLogs":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"test"}}]},"scopeLogs":[{"logRecords":[{"timeUnixNano":"1700000000000000000","severityNumber":9,"body":{"stringValue":"Test log message"}}]}]}]}'

7.3 验证清单#

检查项验证方式预期结果
Collector 健康检查curl http://localhost:13133/HTTP 200
OTLP gRPC 接收发送追踪到 4317Tempo 中能看到
OTLP HTTP 接收发送追踪到 4318Tempo 中能看到
三管道独立分别发送追踪/指标/日志各后端都能收到
Batch Processor检查 Collector 指标批量大小符合预期
Tip

OTel Collector 的部署模式直接影响可观测性管道的可靠性。推荐使用 DaemonSet 模式(每个节点一个 Collector)作为 Agent,再加一个 Deployment 模式的 Gateway Collector 做集中处理。这种两层架构既保证了数据采集的可靠性,又避免了 Agent 直接与后端耦合。

八、本章小结#

上一章深入探讨了分布式追踪与 Span 模型。 可观测性的统一标准——OpenTelemetry的要点基本都在这里了。

主题核心要点关键词
三层架构API(接口)→ SDK(实现)→ Collector(采集路由),应用代码只依赖 API。三层架构
CollectorReceiver → Processor → Exporter 管道架构,是可观测性数据的中央枢纽。Collector
自动仪器化无需修改业务代码即可产生信号,Java Agent 最成熟,Go 需要包装函数。自动仪器化
OTLP 协议统一的二进制传输协议,支持 gRPC 和 HTTP。OTLP 协议
语义约定标准化的属性名称,确保跨服务一致性。语义约定

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

OpenTelemetry 架构
https://blog.souloss.com/posts/observability/opentelemetry/
作者
Souloss
发布于
2025-08-19
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时