如果你读过我关于 OpenTelemetry 链路追踪 的文章,你已经知道如何给应用埋点产生 span。但这些 span 得送到某个地方。那个”地方”通常不是你的应用代码——而是一个独立的基础设施组件:OpenTelemetry Collector。这篇文章讲 Collector 做什么、管道模型怎么工作、以及你什么时候真正需要它。
为什么需要 Collector
没有 Collector 时,每个应用都得知道怎么跟每个后端通信:Jaeger 的 API、Prometheus 的 remote-write、Loki 的 push 接口、Honeycomb 的 key、Datadog 的 agent。换后端就意味着重新给每个服务埋点。这是供应商锁定的噩梦。
Collector 反转了这个关系。你的应用只说 OTLP(OpenTelemetry 协议)——一个协议、一次 SDK 调用——Collector 负责翻译成你今天用的任何后端。从 Jaeger 换到 Tempo?改一个配置文件,重新部署一个二进制,应用零改动。
两种部署模式:
- Agent(代理)模式:每台主机跑一个 Collector,收集本地应用数据。类似本地 syslog 守护进程。
- Gateway(网关)模式:中心化 Collector 集群接收所有 Agent 的 OTLP 数据,统一做重处理(tail sampling、 enrichment)。
对于跑几个服务的自部署场景,每台机器上的 Agent 模式就够了。
管道模型:Receivers → Processors → Exporters
Collector 的配置由三种组件类型通过命名管道连接而成:
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, attributes]
exporters: [debug]
metrics:
receivers: [otlp, prometheus]
processors: [memory_limiter, batch]
exporters: [debug]
每种信号类型(traces、metrics、logs)有自己的管道。Receiver 接收数据,Processor 转换数据,Exporter 输出数据。组件按名称引用——同一个 otlp receiver 可以同时喂给 traces 和 metrics 管道。
Receivers:数据入口
Receiver 监听传入的遥测数据。实际会用到的两个:
otlp— 通过 gRPC(端口 4317)和 HTTP(端口 4318)接收 OTLP。你的埋点应用发送的就是这个协议。支持 protobuf 和 JSON 编码。prometheus— 抓取 Prometheus 格式的端点。适合已有的 exporter,不需要重写。
这是我测试容器中的 receiver 配置:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus:
config:
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 5s
static_configs:
- targets: ['127.0.0.1:8888']
我通过 OTLP/HTTP JSON 发送了一条真实的 trace:
curl -s -X POST http://127.0.0.1:4318/v1/traces \
-H "Content-Type: application/json" \
-d @trace.json
# Response: {"partialSuccess":{}}
Processors:数据转换
Processor 位于 receiver 和 exporter 之间。真正重要的几个:
memory_limiter
在其他 processor 之前检查内存使用。接近上限时丢弃数据而不是让 Collector 被 OOM Kill。始终放在第一位:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 100
spike_limit_mib: 20
Collector 启动时打印了日志:
2026-10-14T04:31:49.205Z info memorylimiter@v0.162.0/memorylimiter.go:109
Memory limiter configured {"limit_mib": 100, "spike_limit_mib": 20, "check_interval": 1}
batch
缓冲遥测数据后批量发送,而不是每个 span 发一次请求。大幅降低 CPU 和网络开销:
processors:
batch:
send_batch_size: 512
timeout: 5s
在 Collector 自身指标中可以看到 batch processor 的工作情况:
otelcol_processor_batch_batch_send_size_count{processor="batch"} 7
otelcol_processor_batch_batch_send_size_sum{proprocessor="batch"} 141
attributes
在遥测数据上插入、更新或删除属性。我用它给每个 span 打上环境标签:
processors:
attributes:
actions:
- key: environment
value: dev
action: insert
- key: dropped_field
action: delete
debug exporter 打印的 span 中包含 environment: dev——证明 processor 确实运行了:
Span #0
Name : test-operation
...
Attributes:
-> http.method: Str(GET)
-> http.status_code: Int(200)
-> environment: Str(dev)
filter
丢弃匹配条件的遥测数据。我用它排除了 Collector 自身的 uptime 和 CPU 指标:
processors:
filter/metrics:
metrics:
exclude:
match_type: strict
metric_names:
- otelcol_process_uptime
- otelcol_process_cpu_seconds
filter processor 记录了配置日志,debug 输出确认那两个指标被丢弃了——otelcol_process_memory_rss 和 scrape_samples_scraped 还在,但排除的两个不见了。
tail_sampling
最复杂的 processor。它等一条 trace 完整结束后再决定是否保留。这样你可以 100% 保留错误和慢请求,只对常规流量采样 1%:
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
expected_new_traces_per_sec: 10000
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: sample-1-percent
type: probabilistic
probabilistic:
sampling_percentage: 1
对自部署场景来说,tail_sampling 可能过度了——但它解决了”如何对感兴趣的请求保留完整 trace 而不被数据淹没”的问题。
Exporters:数据出口
Exporter 把遥测数据发到后端。值得了解的几个:
otlp— 转发 OTLP 到另一个 Collector 或直接到支持 OTLP 的后端(Tempo、带 OTLP 支持的 Jaeger)。debug— 把遥测数据打印到 Collector 日志。调试时很有用,生产环境无用。prometheusremotewrite— 推送指标到 Prometheus 兼容后端(Mimir、Cortex、VictoriaMetrics)。loki— 推送日志到 Loki。
典型的自部署配置:
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
prometheusremotewrite:
endpoint: http://victoria:8428/api/v1/write
Collector 自身指标
Collector 在 :8888/metrics 暴露 Prometheus 格式指标——接收了多少 span、处理了多少、发送了多少,以及自身的内存和 CPU 使用:
otelcol_receiver_accepted_spans{receiver="otlp",transport="http"} 1
otelcol_exporter_sent_spans{exporter="debug"} 1
otelcol_process_memory_rss 2.1272576e+08
otelcol_process_uptime 54.007314091
这些指标告诉你 Collector 是否健康、是否积压、是否丢数据。我的配置里 Prometheus receiver 每 5 秒抓取一次这个端点——Collector 监控自己。
Agent 还是 Gateway
Agent 模式在每台主机运行一个 Collector,通常作为 sidecar 或 systemd 服务。简单直接:本机应用发 OTLP 到本地,Agent 转发到后端。我推荐自部署用这种模式。
Gateway 模式运行中心化集群。Agent 转发 OTLP 到网关,由网关做重处理(tail sampling、一致性哈希路由)。当你有几十个服务、想集中决策时才需要。
一台服务器加几个容器?那台服务器上跑一个 Agent 模式就够了。完事。
配置验证
Collector 在启动时和 --config ... validate 时验证配置。配置有问题会快速失败:
$ otelcol-contrib --config bad-config.yaml validate
Error: service::pipelines::traces: references processor "memory_limiter" which is not configured
Exited with code 1
部署配置变更前务必先验证。
实在的建议
Collector 很强大,但它是另一个要运行的东西。如果你只有一个应用和一个后端,可以完全跳过它——SDK 直接指向后端。如果你有多个应用、想以后换后端、或者需要 tail sampling,Collector 立刻就回本。
自部署建议:每台主机跑一个 Agent,配置 memory_limiter → batch → 你的 exporter。就这样。你获得协议解耦、批量发送、以及换后端时不用改应用代码的灵活性。这就是全部意义。
我的 结构化日志 配置可以通过同一个 Collector 发送日志——一个 traces 管道、一个 metrics 管道、一个 logs 管道,全在一个 YAML 文件里配置。一个进程,三种信号,换后端时应用零改动。
Collector 不让遥测数据变得好——它让数据变得可移植。质量取决于你怎么埋点,那部分我在 OpenTelemetry 链路追踪 里讲了。但没有 Collector,你的遥测数据就被锁死在第一天选的后端上。有了它,你明天就能换主意,代价只是一个配置文件。