OpenTelemetry 高级架构与实践

OTel Operator 管理 Collector 生命周期与自动插桩的 K8s 实践。涵盖三层架构部署、多租户路由、性能调优、eBPF 集成、mTLS 安全、Jaeger 迁移方案。

OpenTelemetry高级架构

核心内容

1. OTel Operator:K8s 原生自动管理

OTel Operator 是管理 Collector 生命周期 + 自动插桩的 K8s Operator,把 Collector 从”手动部署”提升为”声明式管理”。

1.1 安装

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm install otel-operator open-telemetry/opentelemetry-operator \
  --namespace observability \
  --create-namespace

1.2 CRD:声明式 Collector 部署

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-gateway
  namespace: observability
spec:
  mode: deployment          # Agent: DaemonSet | Gateway: Deployment | Sidecar
  replicas: 3
  resources:
    requests:
      cpu: 500m
      memory: 512Mi
    limits:
      cpu: 2
      memory: 2Gi
  # 自动注入的环境变量
  env:
    - name: OTEL_RESOURCE_ATTRIBUTES
      value: "environment=production,cluster=us-east-1"
  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_mib: 1024
      batch:
        timeout: 1s
        send_batch_size: 1024
      k8sattributes:
        extract:
          metadata:
            - k8s.pod.name
            - k8s.namespace.name
            - k8s.deployment.name
        pod_association:
          - sources:
              - from: resource_attribute
                name: k8s.pod.ip
    exporters:
      otlp/tempo:
        endpoint: tempo.monitoring:4317
        tls:
          insecure: true
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, k8sattributes, batch]
          exporters: [otlp/tempo]

1.3 自动插桩注入(Instrumentation CRD)

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: java-instrumentation
  namespace: production
spec:
  exporter:
    endpoint: http://otel-collector.observability:4317
  propagators:
    - tracecontext
    - baggage
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
  python:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest
# 给目标 namespace 打 annotation 启用自动注入
# kubectl annotate namespace production instrumentation.opentelemetry.io/inject-java=true

一旦启用,所有 Pod 自动注入 Java agent / Python 包,零代码。

2. 大规模 Collector 部署架构

2.1 三层架构(1000+ 服务)

                        ┌─────────────┐
                        │   Tempo     │
                        │  (Traces)   │
                        └──────▲──────┘
                               │ OTLP
                    ┌──────────┴──────────┐
                    │    Gateway Cluster  │
                    │   (3-5 副本)         │
                    │   全局采样 + 聚合    │
                    └──────────▲──────────┘
                               │ OTLP
               ┌───────────────┼───────────────┐
               │               │               │
    ┌──────────┴───┐  ┌───────┴──────┐  ┌─────┴─────────┐
    │ Agent Group A│  │ Agent Group B│  │ Agent Group C │
    │ (DaemonSet)  │  │ (DaemonSet)  │  │ (DaemonSet)   │
    │ 本地收+转     │  │ 本地收+转     │  │ 本地收+转      │
    └──────▲───────┘  └──────▲───────┘  └──────▲────────┘
           │                 │                 │
    ┌──────┴──────┐  ┌──────┴──────┐  ┌──────┴────────┐
    │  K8s 集群 A  │  │  K8s 集群 B  │  │  K8s 集群 C    │
    │  (200+ Pod) │  │  (150+ Pod) │  │  (300+ Pod)   │
    └─────────────┘  └─────────────┘  └───────────────┘

2.2 Agent 模式 vs Gateway 模式的分工

层级角色关键配置资源建议
Agent (DaemonSet)本地收数据、K8s 元数据注入、简单压缩k8sattributes processor、batchmemory_limiter256Mi~512Mi / 200m CPU
Gateway (Deployment)全局采样、聚合多个 Agent、负载均衡、租户隔离tail_samplingroutingbatch(大 batch)1Gi~4Gi / 1-4 CPU

2.3 Gateway 层 Load Balancing

# Agent 端配置:负载均衡到多个 Gateway 实例
exporters:
  loadbalancing:
    routing_key: "traceID"        # 按 traceID hash,保证同一 Trace 去同一 Gateway
    protocol:
      otlp:
        tls:
          insecure: true
    resolver:
      dns:
        hostname: otel-gateway.observability.svc.cluster.local
        port: 4317

关键:routing_key: "traceID" 确保 tail_sampling 能收集到完整的 Trace(所有 Span 落在同一个 Gateway 实例上)。

3. 多租户架构

# Gateway 端配置:按租户路由到不同后端
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  # 提取租户信息(从 HTTP header x-tenant-id 或 grpc metadata)
  attributes/tenant:
    actions:
      - key: tenant_id
        from_context: tenant_id   # 从请求元数据提取
        action: upsert

exporters:
  otlp/tenant-a:
    endpoint: tempo-tenant-a:4317
  otlp/tenant-b:
    endpoint: tempo-tenant-b:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes/tenant]
      exporters: [otlp/tenant-a, otlp/tenant-b]

4. Collector 性能调优

4.1 瓶颈诊断指标

# Collector 自身 Metrics(端口 8888)

# 队列满 → 数据丢失风险
otelcol_exporter_queue_size{exporter="otlp/tempo"} / otelcol_exporter_queue_capacity

# Receiver 拒绝数 → 上游 SDK 开始重试
rate(otelcol_receiver_refused_spans[5m])

# 处理延迟 → 判断是否需要扩容
rate(otelcol_processor_batch_batch_send_size_bytes[5m])

# 内存使用 → OOM 预警
otelcol_process_memory_rss

4.2 常见瓶颈与优化

瓶颈现象优化
gRPC 连接数过多SDK 重连频繁Gateway 层增加副本 + DNS load balancing
batch send 太频繁Exporter 网络开销大增大 send_batch_size 到 4096+
tail_sampling 内存不足OOM Kill减少 num_traces 或缩短 decision_wait
磁盘 I/O 成为瓶颈使用 file exporter 时换成内存队列 + OTLP exporter
Exporter 远端慢队列堆积增加 sending_queue.queue_size + num_consumers

4.3 Exporter 反压配置

exporters:
  otlp/tempo:
    endpoint: tempo:4317
    sending_queue:
      enabled: true
      num_consumers: 10        # 并发发送线程
      queue_size: 5000         # 队列容量(内存中最多缓存 5000 批)
    retry_on_failure:
      enabled: true
      initial_interval: 1s
      max_interval: 30s
      max_elapsed_time: 300s   # 5 分钟后放弃重试

5. OTel 与 eBPF 的结合

当无法修改应用代码或语言不支持自动插桩时,eBPF 提供了操作系统层面的自动 Tracing。

# 使用 Pixie (基于 eBPF) 生产 OTel 数据
# Pixie 不需要任何代码改动,通过 K8s DaemonSet 部署
# 自动生成:
#   - HTTP/gRPC 请求的 Span(包含请求体大小、状态码)
#   - DNS 查询的 Span
#   - 协议级别的 network metrics

# 安装 Pixie
px deploy

# Pixie 自动导出 OTel 数据到 Collector
px run -o otlp px/http_data

OTel 官方也在推进 eBPF agent(Go 语言):不修改二进制,通过 eBPF uprobe 拦截函数调用生成 Span。目前处于实验阶段,但对 Go 服务是巨大的进步。

6. 安全与合规

6.1 mTLS:Agent → Gateway → Backend

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
        tls:
          cert_file: /certs/server.crt
          key_file: /certs/server.key
          client_ca_file: /certs/ca.crt   # 要求客户端证书

exporters:
  otlp/tempo:
    endpoint: tempo:4317
    tls:
      cert_file: /certs/client.crt
      key_file: /certs/client.key
      ca_file: /certs/ca.crt

6.2 敏感数据脱敏

processors:
  redaction:
    # 脱敏正则:替换匹配到的值为 "****"
    allow_all_keys: false      # 默认不保留任何 key(白名单模式)
    allowed_keys:
      - http.method
      - http.status_code
      - http.route
    blocked_values:
      - '\b\d{16}\b'            # 信用卡号
      - '\b[A-Z]{2}\d{18}\b'    # 身份证号
      - '(?i)password|secret|token|api_key'

  attributes/remove_sensitive:
    actions:
      - key: user.email
        action: delete
      - key: user.phone
        action: delete
      - key: http.request.header.authorization
        action: delete

6.3 数据保留策略

Tempo 侧配置:
- 正常 Trace: 保留 7 天
- 错误 Trace: 保留 30 天(通过 WAL 标记)
- 采样下来的 Trace: 按 ingress 速率动态调整(10% 采样 → 保留 14 天)

Collector 侧:通过 routing connector 把错误 Trace 单独导出到不同保留策略的后端

7. 从 Jaeger/Zipkin 迁移到 OTel

7.1 Jaeger → OTel 迁移路径

阶段 1: 双写(2 周)
  App → [Jaeger Agent] → Jaeger Collector → Jaeger Backend
  App → [OTel SDK + Jaeger Exporter] → OTel Collector (吐 Jaeger thrift)

阶段 2: 切换后端(1 周)
  App → [OTel SDK] → OTel Collector → Tempo
  同时 OTel Collector 兼容接收 Jaeger thrift 协议

阶段 3: 清理(1 周)
  移除 Jaeger Agent/Collector
  可选:保留 Jaeger UI 指向 Tempo 后端

7.2 OTel Collector 兼容 Jaeger 接收

receivers:
  jaeger:
    protocols:
      grpc:
        endpoint: 0.0.0.0:14250   # Jaeger gRPC 端口
      thrift_http:
        endpoint: 0.0.0.0:14268   # Jaeger HTTP 端口

这意味着:你可以先不改任何应用代码,只要把 Jaeger Agent 的 exporter 指向 OTel Collector,就可以逐步迁移。

8. Collector 高可用设计

8.1 Agent 层(DaemonSet)

Agent 层天然高可用——每个节点一个,Pod 挂了 K8s 自动重启。但需要注意:

  • 不存在单点问题:本地 Pod 发给本地 Agent,Agent 挂了只影响当前节点
  • 反压处理:Gateway 不可用时,Agent 的 sending_queue 提供缓冲

8.2 Gateway 层(Deployment)

# Gateway 高可用关键配置
spec:
  replicas: 3
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: otel-gateway
        topologyKey: kubernetes.io/hostname  # 每个节点最多一个 Gateway

  # PodDisruptionBudget: 保证至少 2 个实例运行
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: otel-gateway-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: otel-gateway

8.3 灾难场景处理

场景影响缓解
Agent 宕机当前节点数据丢失DaemonSet 自动恢复,临时丢失 30s 数据
Gateway 宕机Agent 队列堆积Agent sending_queue 缓冲 + 多副本
Tempo 宕机数据丢失Collector retry_on_failure 持续重试 5min
网络分区Agent 无法连 GatewayAgent 内存缓冲 → 网络恢复后重发
OOM Kill全量数据丢失memory_limiter + spike_limit 防止雪崩

9. 成本模型与数据量预估

# Trace 数据量估算公式
data_per_second = requests_per_second × avg_spans_per_request × avg_span_size

# 典型值:
# requests_per_second = 1000 (RPS)
# avg_spans_per_request = 10 (一次请求经过 10 个服务)
# avg_span_size = 2KB (含 attributes)
# → data_per_second = 1000 × 10 × 2KB = 20 MB/s (未压缩)
# → 压缩后 (gzip 5x) ≈ 4 MB/s → ~345 GB/天 (未采样时)

# 10% 采样后: ~34.5 GB/天 → ~1 TB/月

成本优化四板斧:

手段节省代价
Head-based 概率采样直接减少 90%+丢失大部分正常 Trace
Tail-based 错误保留正常链路少但错误 100%Collector 内存开销
只保留必要 Attribute20-40%排查信息减少
调整 Tempo 保留期存储成本成比例下降历史数据不可查

10. 生产故障模式集

故障根因检测方式修复
SDK exporter 连不上 Collector网络/地址错误SDK 日志 failed to export检查 endpoint + 防火墙
Collector 频繁 OOM内存配置过低otelcol_process_memory_rss > limit增大 memory_limiter limit
SDK 大量 Span 被丢弃Exporter 队列满otelcol_exporter_queue_size ≈ capacity扩容 Gateway 或增大队列
Trace 不完整(缺 Span)tail_sampling 时间窗口不够火焰图断层增大 decision_wait
Tempo 查询超时数据量过大或索引碎片Grafana 返回 504压缩 index、增加 ingester 副本
span_id 碰撞(极低概率但会发生)随机 id 碰撞火焰图出现环OTel SDK 1.0+ 已使用足够长的随机 id,概率可忽略
OTLP 协议版本不兼容SDK 和 Collector 版本差太大Unimplemented gRPC 错误升级 Collector 或降级 SDK

11. 精通级自检清单

  • 能用 OTel Operator 声明式管理 Collector 的生命周期(包括自动升级)
  • 能设计三层 Collector 架构(Agent × N → Gateway Cluster → Backend)
  • 能配置 loadbalancing exporter 按 traceID 路由到同一 Gateway
  • 能设计多租户方案的隔离和路由策略
  • 能通过 Collector 自身 Metrics 定位性能瓶颈
  • 能配置 mTLS 保证 Agent → Gateway → Backend 全链路加密
  • 能配置 redaction processor 实现 PII/敏感数据脱敏
  • 能设计从 Jaeger 到 OTel 的零停机迁移方案
  • 能根据 RPS × span_count × span_size 估算存储成本
  • 能应对至少 5 种生产故障场景并快速定位根因