Chapter 07

流水线的交通枢纽

Collector 是 OTel 的第二颗心脏——应用 SDK 只管把数据丢进去,Collector 负责接收、加工、路由到一个或多个后端。换后端改配置、采样在这里统一、PII 脱敏在这里做——应用层完全无感。

为什么需要 Collector

"SDK 直连后端"不是不行,但会有一堆问题:

厂商绑定
SDK 里填了 Datadog endpoint → 换 Honeycomb 要改所有服务代码 + 重启
采样策略
每个服务单独决策,不一致;tail-based sampling 根本做不了(要看完整 trace)
数据加工
改 attribute、脱敏 PII、聚合 metric —— 每个服务都写一遍?
网络抖动
SDK 缓冲小,后端抖一下就丢数据;Collector 可以大缓冲、落盘重试
多后端
想同时发到 Jaeger + Datadog + 自研数仓?SDK 配三个 exporter 费劲,Collector 分叉一行配置

Collector 把这些痛点统一解决——应用只认 Collector 地址,其他任由运维调配。

管道模型:receiver → processor → exporter

receivers(接收)  →  processors(加工)  →  exporters(发出)
      │                      │                       │
      ├ otlp                  ├ batch                 ├ otlp
      ├ prometheus            ├ memory_limiter        ├ jaeger
      ├ jaeger                ├ attributes            ├ prometheus
      ├ zipkin                ├ resourcedetection     ├ datadog
      ├ kafka                 ├ tail_sampling         ├ loki
      └ filelog               └ transform             └ kafka

三种组件各司其职:

Receivers(接收端)

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  prometheus:          # 主动去 scrape Prom 指标
    config:
      scrape_configs:
        - job_name: app
          static_configs:
            - targets: [app:8080]
  jaeger:              # 兼容 Jaeger 老格式
    protocols:
      thrift_http:
  filelog:             # 读本地日志文件
    include: [/var/log/**/*.log]

OTel 支持 80+ receiver——几乎你见过的任何数据源都能接。

Processors(加工)

processors:
  batch:                     # 几乎必装:批量发出提升吞吐
    timeout: 10s
    send_batch_size: 1024
  memory_limiter:            # 必装:防 OOM
    check_interval: 1s
    limit_mib: 512
  resourcedetection:         # 自动探测 host/k8s/cloud
    detectors: [env, system, k8s]
  attributes:                # 改 attribute
    actions:
      - key: email
        action: delete
      - key: env
        value: prod
        action: upsert
  transform:                 # OTTL 表达式,威力最大
    trace_statements:
      - 'set(attributes["http.status_code"], Int(attributes["status_code"]))'
      - 'delete_key(attributes, "password")'
  tail_sampling:             # 看完整条 trace 再决定
    decision_wait: 30s
    policies:
      - name: errors
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: slow
        type: latency
        latency: {threshold_ms: 500}
      - name: random_10pct
        type: probabilistic
        probabilistic: {sampling_percentage: 10}
processor 顺序有讲究
推荐顺序:memory_limiter → resourcedetection → 业务处理 → tail_sampling → batch
memory_limiter 放最前,防止后面爆内存;batch 放最后,让送到 exporter 的是打好包的数据。

OTTL:通用转换语言

OTTL(OpenTelemetry Transformation Language)是 transform processor 的配置语言——威力类似 Datadog 的 pipeline 或 Splunk 的 SPL。

transform:
  trace_statements:
    - 'set(name, "masked") where attributes["user.email"] != nil'
    - 'set(attributes["http.route"], Substring(attributes["http.url"], 0, 20))'
    - 'keep_keys(attributes, ["http.method", "http.status_code", "user.id"])'
  log_statements:
    - 'set(severity_text, "ERROR") where severity_number >= 17'
    - 'set(body, Concat([body, " [trace=", trace_id.string, "]"], ""))'

学习曲线略陡,但一个团队学会后,所有数据加工都能在 Collector 层完成——应用零改动。

Exporters(发出)

exporters:
  otlp/datadog:
    endpoint: ap1.datadoghq.com:443
    headers:
      DD-API-KEY: ${env:DD_API_KEY}
  otlphttp/honeycomb:
    endpoint: https://api.honeycomb.io
    headers:
      x-honeycomb-team: ${env:HC_KEY}
  prometheusremotewrite:       # 送到 Prom/Mimir
    endpoint: https://mimir:9009/api/v1/push
  loki:                         # 日志给 Loki
    endpoint: http://loki:3100/loki/api/v1/push
  kafka:                        # 异步走 Kafka 再消费
    brokers: [kafka:9092]
    topic: otel-traces
  file:                         # 落盘调试
    path: /tmp/otel.json
  debug:                        # 控制台打印,调试利器
    verbosity: detailed

OTel Collector 支持 100+ exporter,包括主流 APM/日志/Metric 后端,以及存储层(Kafka、S3、ClickHouse)。

Pipeline 组装

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resourcedetection, tail_sampling, batch]
      exporters: [otlp/datadog, otlphttp/honeycomb]   # 同时发俩
    metrics:
      receivers: [otlp, prometheus]
      processors: [memory_limiter, batch]
      exporters: [prometheusremotewrite]
    logs:
      receivers: [otlp, filelog]
      processors: [memory_limiter, transform, batch]
      exporters: [loki]
  extensions: [health_check, pprof, zpages]
  telemetry:
    logs:
      level: info
    metrics:
      address: 0.0.0.0:8888

Pipeline 是"3D"交叉:三种信号 × 三种组件,任意拼接。一个 Collector 实例可以同时跑 traces/metrics/logs 三条 pipeline。

部署模式:Agent vs Gateway

Agent(边车)Gateway(集群)
位置每个主机 / 每个 K8s 节点 DaemonSet独立 Deployment,有多副本
职责就近接收、基础加工、转发集中策略、tail sampling、后端路由
规模随节点数自动扩独立水平扩容
延迟低(同节点)多一跳
应用 → Agent(节点上) → Gateway(集群) → 后端
      localhost     同 k8s              多后端

生产推荐:Agent + Gateway 两层——Agent 低延迟就近接,Gateway 做 tail sampling 和多后端扇出。

Tail-based Sampling 深入

头部采样(SDK 层)的缺陷:请求开始时你不知道它会不会出错。决定采 10%,9 条慢请求里可能只留下 1 条。

Tail-based Sampling(Collector 层)在整条 trace 聚齐后再决策:

processors:
  tail_sampling:
    decision_wait: 30s      # 等 30s 凑齐整条 trace
    num_traces: 100000       # 最多缓存的 trace 数
    policies:
      - name: keep_errors
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: keep_slow
        type: latency
        latency: {threshold_ms: 500}
      - name: keep_vip_tenant
        type: string_attribute
        string_attribute:
          key: tenant.id
          values: [vip-customer-1, vip-customer-2]
      - name: baseline
        type: probabilistic
        probabilistic: {sampling_percentage: 1}
架构陷阱
Tail sampling 要求一条 trace 的所有 span 都到同一个 Collector 实例——否则决策时看不到全貌。多副本 Gateway 需要用 loadbalancing exporter 按 trace_id 哈希分发。

负载均衡(loadbalancing exporter)

Agent → Gateway LB → {Gateway A, Gateway B, Gateway C}  →  tail sampling 各自决策
         按 trace_id 哈希:同一条 trace 永远去同一台
exporters:
  loadbalancing:
    routing_key: traceID
    protocol:
      otlp:
        tls: {insecure: true}
    resolver:
      k8s:
        service: otel-gateway.observability
        ports: [4317]

运维可观测性:Collector 自己的 metrics

Collector 自己也有 metrics——要不它挂了你咋知道?

这些 metrics 自己也用 telemetry.metrics.address 暴露出来,你的 Prometheus 可以 scrape——把 Collector 纳入自己的监控体系。

配置热加载

改了配置?不想重启?

# 发 SIGHUP 触发 reload
kill -HUP $(pgrep otelcol)

# 或者用 config_sources 拉远程配置 + file watch

热加载不是所有组件都支持,但 receivers/exporters/processors 大多数都行。生产上建议滚动重启更稳妥。

OTel Collector vs Fluent Bit

OTel CollectorFluent Bit
起家可观测性全栈日志专家
覆盖trace/metric/log 三合一日志 + metric(弱)
生态OTel 生态核心K8s 集群日志首选
体积~30MB~500KB
场景可观测性统一管道轻量日志 forwarder

不互斥:很多团队用 Fluent Bit 做 K8s 日志采集 → 送到 OTel Collector 统一加工 → 送多后端。

本章小结