0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于OpenTelemetry的全链路追踪微服务可观测性实践

马哥Linux运维 来源:马哥Linux运维 2026-02-26 15:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

基于OpenTelemetry的全链路追踪微服务可观测性实践

一、概述

1.1 背景介绍

微服务拆分到第三年,我们的服务数量从最初的5个膨胀到了47个。一个用户下单请求要经过API Gateway -> 用户服务 -> 商品服务 -> 库存服务 -> 订单服务 -> 支付服务,中间还有消息队列和缓存的调用。某天线上出现下单接口P99延迟从200ms飙到了3秒,排查了两个小时才定位到是库存服务调用Redis超时导致的。

这就是没有链路追踪的痛。每个服务自己的日志都正常,但串起来看整个调用链,问题藏在哪个环节根本看不出来。

2019年我们先上了Jaeger + OpenTracing SDK,后来OpenTracing和OpenCensus合并成了OpenTelemetry(简称OTel),2021年开始逐步迁移到OTel。OTel的定位是可观测性的统一标准,一套SDK同时产生Trace、Metric、Log三种信号,不再需要为每种信号单独接入不同的库。

迁移到OTel后,排障效率提升明显。同样的问题,现在从Grafana的Trace面板点进去,直接看到整个调用链的瀑布图,哪个Span耗时异常一目了然,定位时间从小时级降到了分钟级。

1.2 技术特点

厂商中立的统一标准:OTel是CNCF的毕业项目,不绑定任何后端。今天用Jaeger,明天想换成Tempo或者Zipkin,只需要改Collector的exporter配置,业务代码一行不用动。我们从Jaeger迁移到Grafana Tempo的过程中,47个服务的代码零改动。

三信号统一(Trace/Metric/Log):一套OTel SDK同时产生分布式追踪、指标和日志,三种数据通过Trace ID关联。在Grafana里可以从一个异常Trace跳转到对应时间段的指标图表和日志,排障不需要在多个系统之间来回切换。

自动注入能力JavaPythonNode.js等语言支持自动注入(auto-instrumentation),不需要修改业务代码就能自动采集HTTP请求、数据库调用、Redis操作等Span。我们的Java服务用javaagent方式接入,开发团队完全无感知。

1.3 适用场景

微服务架构的请求链路追踪:服务数量超过10个时,没有链路追踪基本等于盲人摸象。OTel可以自动记录每个服务间的调用关系、耗时、状态码,快速定位慢请求和错误请求的根因。

性能瓶颈分析:通过Trace数据可以看到每个环节的耗时占比,比如一个请求总耗时500ms,其中数据库查询占了300ms,就知道该优化SQL了。配合采样策略,可以只采集慢请求的Trace,减少存储开销。

服务依赖关系梳理:OTel Collector可以根据Trace数据自动生成服务拓扑图,哪个服务调用了哪个服务、调用频率多少、错误率多少,一张图全看清。新人入职看这张图就能快速了解系统架构。

1.4 环境要求

组件 版本要求 说明
操作系统 CentOS 7+ / Ubuntu 20.04+ 推荐Ubuntu 22.04 LTS
OTel Collector 0.96+ 本文以0.96.0为例
Jaeger 1.53+ 作为Trace后端存储和查询
Java JDK 11+ Java自动注入需要JDK 11以上
Go 1.21+ Go SDK需要1.21以上
Python 3.8+ Python自动注入需要3.8以上
内存 Collector最低2GB,Jaeger最低4GB 生产环境各8GB+

二、详细步骤

2.1 准备工作

2.1.1 架构说明

我们采用的架构是:

应用服务(OTel SDK) --> OTel Collector(Agent模式) --> OTel Collector(Gateway模式) --> Jaeger/Tempo
                                         --> Prometheus
                                         --> Loki

两层Collector的设计:

Agent模式:以DaemonSet或Sidecar部署在每个节点/Pod旁边,负责接收本地应用的数据,做初步处理(采样、过滤、添加属性)

Gateway模式:集中部署,接收所有Agent的数据,做聚合处理后发送到后端存储

这样设计的好处是Agent挂了只影响单个节点,Gateway可以水平扩展,后端存储的变更只需要改Gateway配置。

2.1.2 安装OTel Collector

# 创建用户和目录
sudo useradd --system --no-create-home --shell /usr/sbin/nologin otelcol
sudo mkdir -p /etc/otelcol /var/log/otelcol /data/otelcol
sudo chown -R otelcol:otelcol /var/log/otelcol /data/otelcol

# 下载OTel Collector Contrib版本(包含更多receiver/exporter)
OTEL_VERSION="0.96.0"
cd/tmp
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol-contrib_${OTEL_VERSION}_linux_amd64.tar.gz

tar xzf otelcol-contrib_${OTEL_VERSION}_linux_amd64.tar.gz
sudo mv otelcol-contrib /usr/local/bin/otelcol
sudo chmod +x /usr/local/bin/otelcol

# 验证安装
otelcol --version

2.1.3 安装Jaeger

# 用Docker部署Jaeger All-in-One(测试环境)
docker run -d --name jaeger 
 -p 16686:16686 
 -p 4317:4317 
 -p 4318:4318 
 -p 14250:14250 
 -e COLLECTOR_OTLP_ENABLED=true
 -e SPAN_STORAGE_TYPE=badger 
 -e BADGER_EPHEMERAL=false
 -e BADGER_DIRECTORY_VALUE=/badger/data 
 -e BADGER_DIRECTORY_KEY=/badger/key 
 -v /data/jaeger:/badger 
 jaegertracing/all-in-one:1.53

# 生产环境用Elasticsearch后端
docker run -d --name jaeger-collector 
 -p 14250:14250 
 -p 4317:4317 
 -p 4318:4318 
 -e SPAN_STORAGE_TYPE=elasticsearch 
 -e ES_SERVER_URLS=http://elasticsearch:9200 
 -e ES_INDEX_PREFIX=jaeger 
 -e ES_NUM_SHARDS=3 
 -e ES_NUM_REPLICAS=1 
 jaegertracing/jaeger-collector:1.53

2.2 核心配置

2.2.1 OTel Collector Agent模式配置

# /etc/otelcol/agent-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
   cors:
    allowed_origins:["*"]

# 采集主机指标(替代node_exporter)
hostmetrics:
 collection_interval:30s
 scrapers:
  cpu:
   metrics:
    system.cpu.utilization:
     enabled:true
  memory:
  disk:
  filesystem:
  load:
  network:
  paging:
  processes:

processors:
batch:
 send_batch_size:1024
 send_batch_max_size:2048
 timeout:5s
 # 攒够1024条或等待5秒就发送一批
 # 默认的8192太大,延迟高;太小又增加网络开销

memory_limiter:
 check_interval:5s
 limit_mib:1536
 spike_limit_mib:512
 # 内存限制1.5GB,超过后开始丢弃数据
 # 这个必须配,不配的话Collector OOM是迟早的事

resource:
 attributes:
  -key:host.name
   from_attribute:host.name
   action:upsert
  -key:deployment.environment
   value:production
   action:upsert
  -key:service.cluster
   value:prod-bj-01
   action:upsert

# 尾部采样在Agent层不做,放到Gateway层统一处理
# Agent层只做概率采样作为兜底
probabilistic_sampler:
 sampling_percentage:100
 # Agent层默认采集100%,采样策略在Gateway层控制

exporters:
otlp:
 endpoint:otel-gateway:4317
 tls:
  insecure:true
 retry_on_failure:
  enabled:true
  initial_interval:5s
  max_interval:30s
  max_elapsed_time:300s
 sending_queue:
  enabled:true
  num_consumers:10
  queue_size:5000

logging:
 loglevel:warn
 # 只在debug时改成info或debug

extensions:
health_check:
 endpoint:0.0.0.0:13133
zpages:
 endpoint:0.0.0.0:55679

service:
extensions:[health_check,zpages]
pipelines:
 traces:
  receivers:[otlp]
  processors:[memory_limiter,resource,batch]
  exporters:[otlp]
 metrics:
  receivers:[otlp,hostmetrics]
  processors:[memory_limiter,resource,batch]
  exporters:[otlp]
 logs:
  receivers:[otlp]
  processors:[memory_limiter,resource,batch]
  exporters:[otlp]
telemetry:
 logs:
  level:warn
 metrics:
  address:0.0.0.0:8888

2.2.2 OTel Collector Gateway模式配置

# /etc/otelcol/gateway-config.yaml
receivers:
otlp:
 protocols:
  grpc:
   endpoint:0.0.0.0:4317
   max_recv_msg_size_mib:32
  http:
   endpoint:0.0.0.0:4318

processors:
batch:
 send_batch_size:2048
 timeout:10s

memory_limiter:
 check_interval:5s
 limit_mib:6144
 spike_limit_mib:1024

# 尾部采样:根据Trace的完整信息决定是否采样
tail_sampling:
 decision_wait:30s
 # 等待30秒收集完整Trace后再决定是否采样
 num_traces:100000
 # 内存中最多保留10万条Trace
 expected_new_traces_per_sec:1000
 policies:
  # 策略1:所有错误的Trace都保留
  -name:errors-policy
   type:status_code
   status_code:
    status_codes:[ERROR]
  # 策略2:延迟超过1秒的Trace保留
  -name:latency-policy
   type:latency
   latency:
    threshold_ms:1000
  # 策略3:正常请求按10%概率采样
  -name:probabilistic-policy
   type:probabilistic
   probabilistic:
    sampling_percentage:10
  # 策略4:特定服务100%采样(比如支付服务)
  -name:payment-always
   type:string_attribute
   string_attribute:
    key:service.name
    values:[payment-service]

# 给Span添加额外属性
attributes:
 actions:
  -key:collector.version
   value:"0.96.0"
   action:upsert

# Span指标生成:从Trace数据中提取RED指标
spanmetrics:
 metrics_exporter:prometheusremotewrite
 dimensions:
  -name:http.method
  -name:http.status_code
  -name:service.name
 histogram:
  explicit:
   buckets:[5ms,10ms,25ms,50ms,100ms,250ms,500ms,1s,2.5s,5s,10s]

exporters:
otlp/jaeger:
 endpoint:jaeger-collector:4317
 tls:
  insecure:true

prometheusremotewrite:
 endpoint:http://prometheus:9090/api/v1/write
 resource_to_telemetry_conversion:
  enabled:true

loki:
 endpoint:http://loki:3100/loki/api/v1/push
 default_labels_enabled:
  exporter:false
  job:true

logging:
 loglevel:warn

extensions:
health_check:
 endpoint:0.0.0.0:13133
zpages:
 endpoint:0.0.0.0:55679

service:
extensions:[health_check,zpages]
pipelines:
 traces:
  receivers:[otlp]
  processors:[memory_limiter,tail_sampling,attributes,spanmetrics,batch]
  exporters:[otlp/jaeger]
 metrics:
  receivers:[otlp]
  processors:[memory_limiter,batch]
  exporters:[prometheusremotewrite]
 logs:
  receivers:[otlp]
  processors:[memory_limiter,batch]
  exporters:[loki]
telemetry:
 logs:
  level:warn
 metrics:
  address:0.0.0.0:8888

关键配置说明

tail_sampling的decision_wait设为30秒,意味着一个Trace的所有Span必须在30秒内到达Collector,否则可能被截断。对于大多数同步调用场景够用了,但如果有异步消息队列的场景,可能需要调大到60秒。

spanmetrics处理器会从Trace数据中自动生成请求速率(Rate)、错误率(Error)、延迟分布(Duration)三个指标,也就是RED指标。这样不需要在应用代码里手动埋点Prometheus指标,Trace数据自动转换。

memory_limiter必须放在processors列表的第一个,这样内存超限时最先触发限流,避免后续处理器继续消耗内存。

2.2.3 Systemd服务配置

# /etc/systemd/system/otelcol.service
[Unit]
Description=OpenTelemetry Collector
Documentation=https://opentelemetry.io/docs/collector/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=otelcol
Group=otelcol
ExecStart=/usr/local/bin/otelcol --config=/etc/otelcol/agent-config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
LimitMEMLOCK=infinity
StandardOutput=journal
StandardError=journal
Environment=GOMAXPROCS=4

[Install]
WantedBy=multi-user.target

2.3 启动和验证

2.3.1 启动服务

# 验证配置文件
otelcol validate --config=/etc/otelcol/agent-config.yaml

# 启动Collector
sudo systemctl daemon-reload
sudo systemctl start otelcol
sudo systemctlenableotelcol

# 检查状态
sudo systemctl status otelcol

# 检查日志
sudo journalctl -u otelcol -f --no-pager

2.3.2 功能验证

# 检查健康状态
curl -s http://localhost:13133/
# 预期输出:{"status":"Server available"...}

# 检查Collector自身指标
curl -s http://localhost:8888/metrics | head -30

# 发送测试Trace(用curl模拟OTLP HTTP协议)
curl -X POST http://localhost:4318/v1/traces 
 -H"Content-Type: application/json"
 -d'{
  "resourceSpans": [{
   "resource": {
    "attributes": [{"key": "service.name", "value": {"stringValue": "test-service"}}]
   },
   "scopeSpans": [{
    "scope": {"name": "test"},
    "spans": [{
     "traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
     "spanId": "051581bf3cb55c13",
     "name": "test-span",
     "kind": 1,
     "startTimeUnixNano": "1704067200000000000",
     "endTimeUnixNano": "1704067200100000000",
     "status": {"code": 1}
    }]
   }]
  }]
 }'

# 在Jaeger UI中查看:http://jaeger-host:16686
# 搜索service=test-service应该能看到刚才的Trace

2.3.3 SDK接入示例

Java自动注入(推荐,零代码改动)

# 下载OTel Java Agent
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.1.0/opentelemetry-javaagent.jar 
 -O /opt/otel/opentelemetry-javaagent.jar

# 启动Java应用时加上javaagent参数
java -javaagent:/opt/otel/opentelemetry-javaagent.jar 
 -Dotel.service.name=order-service 
 -Dotel.exporter.otlp.endpoint=http://localhost:4317 
 -Dotel.exporter.otlp.protocol=grpc 
 -Dotel.traces.sampler=parentbased_always_on 
 -Dotel.metrics.exporter=otlp 
 -Dotel.logs.exporter=otlp 
 -Dotel.resource.attributes=service.namespace=production,service.version=1.2.3 
 -jar order-service.jar

Go手动接入

// main.go
packagemain

import(
 "context"
 "log"
 "net/http"
 "time"

 "go.opentelemetry.io/otel"
 "go.opentelemetry.io/otel/attribute"
 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
 "go.opentelemetry.io/otel/propagation"
 "go.opentelemetry.io/otel/sdk/resource"
  sdktrace"go.opentelemetry.io/otel/sdk/trace"
  semconv"go.opentelemetry.io/otel/semconv/v1.24.0"
 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

funcinitTracer()(*sdktrace.TracerProvider, error){
  ctx := context.Background()

  exporter, err := otlptracegrpc.New(ctx,
    otlptracegrpc.WithEndpoint("localhost:4317"),
    otlptracegrpc.WithInsecure(),
  )
 iferr !=nil{
   returnnil, err
  }

  res, err := resource.New(ctx,
    resource.WithAttributes(
      semconv.ServiceName("inventory-service"),
      semconv.ServiceVersion("1.0.0"),
      attribute.String("deployment.environment","production"),
    ),
  )
 iferr !=nil{
   returnnil, err
  }

  tp := sdktrace.NewTracerProvider(
    sdktrace.WithBatcher(exporter,
      sdktrace.WithBatchTimeout(5*time.Second),
      sdktrace.WithMaxExportBatchSize(512),
    ),
    sdktrace.WithResource(res),
    sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.AlwaysSample())),
  )

  otel.SetTracerProvider(tp)
  otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
    propagation.TraceContext{},
    propagation.Baggage{},
  ))

 returntp,nil
}

funcmain(){
  tp, err := initTracer()
 iferr !=nil{
    log.Fatal(err)
  }
 defertp.Shutdown(context.Background())

 // 使用otelhttp自动注入HTTP处理器
  handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
    ctx := r.Context()
    tracer := otel.Tracer("inventory-service")

   // 手动创建子Span
    ctx, span := tracer.Start(ctx,"check-inventory")
   deferspan.End()

    span.SetAttributes(
      attribute.String("product.id", r.URL.Query().Get("product_id")),
      attribute.Int("inventory.count",42),
    )

    w.Write([]byte("OK"))
  })

  wrappedHandler := otelhttp.NewHandler(handler,"inventory-check")
  log.Fatal(http.ListenAndServe(":8080", wrappedHandler))
}

Python自动注入

# 安装OTel Python包
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

# 启动Python应用(自动注入)
opentelemetry-instrument 
 --service_name user-service 
 --exporter_otlp_endpoint http://localhost:4317 
 --exporter_otlp_protocol grpc 
 --traces_exporter otlp 
 --metrics_exporter otlp 
 python app.py

三、示例代码和配置

3.1 完整配置示例

3.1.1 Collector完整Pipeline配置(生产级)

这是我们线上47个微服务环境跑了10个月的Gateway配置,日均处理Span量约2亿条:

# /etc/otelcol/gateway-production.yaml
receivers:
otlp:
 protocols:
  grpc:
   endpoint:0.0.0.0:4317
   max_recv_msg_size_mib:32
   keepalive:
    server_parameters:
     max_connection_idle:60s
     max_connection_age:300s
     time:30s
     timeout:10s
  http:
   endpoint:0.0.0.0:4318

# 接收Prometheus格式的指标
prometheus:
 config:
  scrape_configs:
   -job_name:'otel-collector'
    scrape_interval:30s
    static_configs:
     -targets:['localhost:8888']

processors:
batch/traces:
 send_batch_size:2048
 send_batch_max_size:4096
 timeout:10s

batch/metrics:
 send_batch_size:4096
 timeout:15s

batch/logs:
 send_batch_size:2048
 timeout:10s

memory_limiter:
 check_interval:5s
 limit_mib:6144
 spike_limit_mib:1024

tail_sampling:
 decision_wait:30s
 num_traces:200000
 expected_new_traces_per_sec:2000
 policies:
  -name:error-traces
   type:status_code
   status_code:
    status_codes:[ERROR]
  -name:slow-traces
   type:latency
   latency:
    threshold_ms:1000
  -name:payment-traces
   type:string_attribute
   string_attribute:
    key:service.name
    values:[payment-service,refund-service]
  -name:health-check-drop
   type:string_attribute
   string_attribute:
    key:http.target
    values:[/health,/ready,/metrics]
    invert_match:true
  -name:default-sampling
   type:probabilistic
   probabilistic:
    sampling_percentage:5

# 过滤掉健康检查的Span,减少存储
filter/traces:
 error_mode:ignore
 traces:
  span:
   -'attributes["http.target"] == "/health"'
   -'attributes["http.target"] == "/ready"'
   -'attributes["http.target"] == "/metrics"'
   -'attributes["http.target"] == "/favicon.ico"'

# 敏感信息脱敏
attributes/sanitize:
 actions:
  -key:http.request.header.authorization
   action:delete
  -key:db.statement
   action:hash
   # 对SQL语句做hash,保留可关联性但不暴露具体内容

spanmetrics:
 metrics_exporter:prometheusremotewrite
 dimensions:
  -name:service.name
  -name:http.method
  -name:http.status_code
  -name:rpc.method
 histogram:
  explicit:
   buckets:[2ms,5ms,10ms,25ms,50ms,100ms,250ms,500ms,1s,2.5s,5s,10s]
 dimensions_cache_size:5000
 aggregation_temporality:AGGREGATION_TEMPORALITY_CUMULATIVE

exporters:
otlp/jaeger:
 endpoint:jaeger-collector.monitoring:4317
 tls:
  insecure:true
 retry_on_failure:
  enabled:true
  initial_interval:5s
  max_interval:60s
 sending_queue:
  enabled:true
  num_consumers:20
  queue_size:10000

prometheusremotewrite:
 endpoint:http://prometheus.monitoring:9090/api/v1/write
 tls:
  insecure:true
 resource_to_telemetry_conversion:
  enabled:true

loki:
 endpoint:http://loki-gateway.monitoring:3100/loki/api/v1/push

extensions:
health_check:
 endpoint:0.0.0.0:13133
zpages:
 endpoint:0.0.0.0:55679
pprof:
 endpoint:0.0.0.0:1777

service:
extensions:[health_check,zpages,pprof]
pipelines:
 traces:
  receivers:[otlp]
  processors:[memory_limiter,filter/traces,attributes/sanitize,tail_sampling,spanmetrics,batch/traces]
  exporters:[otlp/jaeger]
 metrics:
  receivers:[otlp,prometheus]
  processors:[memory_limiter,batch/metrics]
  exporters:[prometheusremotewrite]
 logs:
  receivers:[otlp]
  processors:[memory_limiter,batch/logs]
  exporters:[loki]
telemetry:
 logs:
  level:warn
 metrics:
  address:0.0.0.0:8888

3.1.2 Java自动注入高级配置

通过配置文件精细控制Java Agent的行为:

# /opt/otel/otel-agent.properties
# 启动时通过 -Dotel.javaagent.configuration-file 指定

otel.service.name=order-service
otel.resource.attributes=service.namespace=production,service.version=2.1.0

otel.exporter.otlp.endpoint=http://otel-agent:4317
otel.exporter.otlp.protocol=grpc
otel.exporter.otlp.timeout=10000
otel.exporter.otlp.compression=gzip

otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=1.0

otel.bsp.schedule.delay=5000
otel.bsp.max.queue.size=2048
otel.bsp.max.export.batch.size=512

# 禁用对性能影响大但价值不高的instrumentation
otel.instrumentation.runtime-metrics.enabled=false
otel.instrumentation.log4j-appender.enabled=false

otel.propagators=tracecontext,baggage

otel.span.attribute.value.length.limit=1024
otel.span.attribute.count.limit=64
java -javaagent:/opt/otel/opentelemetry-javaagent.jar 
 -Dotel.javaagent.configuration-file=/opt/otel/otel-agent.properties 
 -jar order-service.jar

3.1.3 Kubernetes DaemonSet部署

# otel-collector-agent-daemonset.yaml
apiVersion:apps/v1
kind:DaemonSet
metadata:
name:otel-collector-agent
namespace:monitoring
spec:
selector:
 matchLabels:
  app:otel-collector-agent
template:
 metadata:
  labels:
   app:otel-collector-agent
 spec:
  serviceAccountName:otel-collector
  containers:
   -name:otel-collector
    image:otel/opentelemetry-collector-contrib:0.96.0
    args:["--config=/etc/otelcol/config.yaml"]
    ports:
     -containerPort:4317
      hostPort:4317
     -containerPort:4318
      hostPort:4318
    resources:
     requests:
      cpu:200m
      memory:512Mi
     limits:
      cpu:1000m
      memory:2Gi
    livenessProbe:
     httpGet:
      path:/
      port:13133
     initialDelaySeconds:15
    volumeMounts:
     -name:config
      mountPath:/etc/otelcol
  volumes:
   -name:config
    configMap:
     name:otel-agent-config

3.2 实际应用案例

案例一:慢请求根因定位

场景描述:线上下单接口P99延迟从200ms突然飙到3秒,需要快速定位是哪个环节变慢了。

排查流程

# 第一步:在Jaeger UI中搜索慢Trace
# Service: api-gateway
# Operation: POST /api/v1/orders
# Min Duration: 2s
# 找到一条耗时3.2秒的Trace

# 第二步:查看Trace瀑布图,发现调用链:
# api-gateway (3.2s)
#  └── order-service (3.1s)
#     ├── user-service (50ms) 正常
#     ├── product-service (80ms) 正常
#     ├── inventory-service (2.8s) <-- 这里有问题
#         │     └── Redis GET (2.7s) <-- 根因
#         └── payment-service (未执行,被inventory阻塞)

# 第三步:看inventory-service的Redis Span属性
# db.system: redis
# db.operation: GET
# net.peer.name: redis-cluster-03
# 发现是redis-cluster-03节点响应慢

# 第四步:用spanmetrics生成的指标确认影响范围
# PromQL: histogram_quantile(0.99,
#   rate(traces_spanmetrics_latency_bucket{
#     service_name="inventory-service",
#     span_name="Redis GET"
#   }[5m]))
# 确认P99从5ms飙到了2.7s

从发现问题到定位根因,整个过程不到5分钟。如果没有链路追踪,光看各服务自己的日志,至少要1-2小时。

案例二:跨服务上下文传播验证

场景描述:新接入OTel的服务和老的Zipkin服务之间Trace断裂,需要排查上下文传播问题。

排查步骤

# OTel默认使用W3C TraceContext格式:
# traceparent: 00---

# 老服务用的是B3格式:
# X-B3-TraceId / X-B3-SpanId / X-B3-Sampled

# 解决方案:Java Agent配置多传播器
# otel.propagators=tracecontext,baggage,b3multi

# 验证方法:用curl手动发请求检查响应头
curl -v 
 -H"traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
 http://new-service:8080/api/test2>&1 | grep -i"traceparent|b3"

迁移期间两种格式并存,等所有服务都迁移到OTel后再去掉B3支持。

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 采样策略选择

采样策略直接决定了存储成本和排障能力之间的平衡。我们踩过的坑:一开始全量采集,47个服务每天产生20亿条Span,Jaeger后端的Elasticsearch撑了两周就扛不住了。

头部采样(Head-based Sampling):在Trace的第一个Span就决定是否采样。优点是实现简单、资源消耗低;缺点是决策时不知道这个Trace后续会不会出错,可能漏掉错误Trace。

# Java Agent头部采样配置
# 按比例采样:只采集10%的Trace
otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=0.1

尾部采样(Tail-based Sampling):等Trace的所有Span都到达Collector后再决定是否采样。优点是可以根据完整信息决策(比如只保留错误的和慢的Trace);缺点是Collector需要在内存中缓存所有Trace,资源消耗大。

# Collector尾部采样配置(推荐放在Gateway层)
tail_sampling:
decision_wait:30s
num_traces:200000
policies:
 -name:errors-always
  type:status_code
  status_code:
   status_codes:[ERROR]
 -name:slow-always
  type:latency
  latency:
   threshold_ms:1000
 -name:normal-sample-5pct
  type:probabilistic
  probabilistic:
   sampling_percentage:5

我们的做法:Gateway层用尾部采样,错误Trace和慢Trace(>1s)100%保留,正常Trace只保留5%。这样存储量从20亿条/天降到了约3亿条/天,但所有有问题的Trace都不会丢。

4.1.2 Span属性规范

Span属性(Attributes)是排障时的关键信息,但不是越多越好。属性太多会增加网络传输和存储开销,太少又查不到有用信息。

我们团队制定的Span属性规范:

# 必须包含的属性(所有服务统一)
service.name:order-service    # 服务名
service.version:1.2.3       # 服务版本
deployment.environment:production # 环境
service.namespace:ecommerce    # 业务域

# HTTP请求Span必须包含
http.method:GET
http.url:/api/v1/orders/12345
http.status_code:200
http.request_content_length:1024
http.response_content_length:2048

# 数据库调用Span必须包含
db.system:mysql
db.name:order_db
db.operation:SELECT
db.statement:"SELECT * FROM orders WHERE id = ?"
# 注意:db.statement只记录参数化的SQL,不记录实际参数值,防止泄露敏感数据

# RPC调用Span必须包含
rpc.system:grpc
rpc.service:inventory.InventoryService
rpc.method:CheckStock

# 禁止包含的属性(敏感信息)
# - 用户密码、Token
# - 信用卡号、身份证号
# - 请求体中的完整JSON(太大)

4.1.3 与Prometheus/Loki关联

OTel的三信号关联是排障效率提升的关键。在Grafana中配置数据源关联:

# Grafana数据源配置 - Jaeger
# Settings -> Trace to logs:
#  Data source: Loki
#  Tags: service.name -> job
#  Span start time shift: -5m
#  Span end time shift: 5m
#  Filter by trace ID: true

# Settings -> Trace to metrics:
#  Data source: Prometheus
#  Tags: service.name -> service_name
#  Span start time shift: -5m
#  Span end time shift: 5m

这样配置后,在Jaeger的Trace详情页可以一键跳转到:

对应时间段的Loki日志(按Trace ID过滤)

对应服务的Prometheus指标图表

4.1.4 安全加固

敏感信息脱敏:在Collector的attributes处理器中删除Authorization头、对SQL语句做hash处理。别把用户Token写到Span里,Jaeger的存储是明文的。

传输加密:生产环境Collector之间的通信用mTLS加密,特别是跨网络的Agent到Gateway链路。

访问控制:Jaeger UI加Nginx反向代理做IP白名单或SSO认证,不要裸露在公网。

数据保留策略:Trace数据保留7-14天就够了,超过这个时间的Trace基本不会再查。Jaeger的Elasticsearch后端配置ILM策略自动清理。

4.2 注意事项

4.2.1 配置注意事项

memory_limiter必须放在processors列表的第一个位置。如果放在batch后面,batch已经消耗了大量内存,memory_limiter来不及限流,Collector直接OOM。我们线上出过一次这个问题,Collector反复重启。

tail_sampling的decision_wait不能设太短。设10秒的话,如果某个服务的Span因为网络延迟晚到了,这个Trace就不完整,采样决策可能不准确。我们设30秒,覆盖了99%的场景。

Java Agent的otel.bsp.max.queue.size默认2048,在高并发场景下可能不够。队列满了新的Span会被丢弃,日志里会出现"Dropping span"警告。我们调到了4096。

别在生产环境开Collector的logging exporter的info级别,每条Span都会打一行日志,IO会被打满。只在debug时临时开启。

4.2.2 常见错误

错误现象 原因分析 解决方案
Trace断裂,父子Span不关联 上下文传播失败,propagator配置不匹配 检查所有服务的propagator配置是否一致
Collector报"dropping spans" 发送队列满或后端写入慢 增大sending_queue.queue_size,检查后端性能
Java Agent启动后应用变慢 自动注入的instrumentation太多 禁用不需要的instrumentation
Span中缺少预期的属性 SDK版本不匹配或属性名不符合语义约定 检查SDK版本,参考OTel Semantic Conventions
尾部采样不生效 decision_wait太短或Span分散在多个Collector 增大decision_wait,确保同一Trace的Span路由到同一Collector
Collector OOM memory_limiter未配置或位置不对 确保memory_limiter在processors列表第一位

4.2.3 兼容性问题

OTel SDK版本:Java Agent、Go SDK、Python SDK的版本尽量保持一致,至少大版本一致。混用不同大版本可能出现协议不兼容。

Collector版本:Contrib版本的Collector包含社区贡献的组件,更新频率快,偶尔会有breaking change。升级前先看Release Notes。

Jaeger兼容:Jaeger 1.35+原生支持OTLP协议,不需要再用jaeger exporter,直接用otlp exporter发送到Jaeger的4317端口

Prometheus兼容:OTel的Metric和Prometheus的Metric在命名规范上有差异(OTel用点号分隔,Prometheus用下划线),Collector的prometheusremotewrite exporter会自动转换。

五、故障排查和监控

5.1 故障排查

5.1.1 日志查看

# Collector日志
sudo journalctl -u otelcol -f --no-pager

# 只看错误
sudo journalctl -u otelcol -p err --since"1 hour ago"

# Collector自身指标(通过/metrics端点)
curl -s http://localhost:8888/metrics | grep otelcol

# zpages调试页面(需要开启zpages扩展)
# 浏览器访问 http://collector-host:55679/debug/tracez
# 可以看到Collector内部处理的Trace样本

5.1.2 常见问题排查

问题一:Trace数据丢失,Jaeger中查不到预期的Trace

# 第一步:检查Collector是否收到了数据
curl -s http://localhost:8888/metrics | grep otelcol_receiver_accepted_spans
# 如果这个值在增长,说明Collector收到了Span

# 第二步:检查Collector是否成功发送
curl -s http://localhost:8888/metrics | grep otelcol_exporter_sent_spans
# 对比accepted和sent的差值,差值大说明有丢弃

# 第三步:检查发送失败数
curl -s http://localhost:8888/metrics | grep otelcol_exporter_send_failed_spans
# 不为0说明发送到后端失败了

# 第四步:检查采样策略是否过滤掉了
curl -s http://localhost:8888/metrics | grep otelcol_processor_tail_sampling

解决方案

发送失败:检查后端(Jaeger/Tempo)是否正常运行,网络是否通

被采样过滤:检查tail_sampling策略,确认目标Trace符合保留条件

队列溢出:增大sending_queue.queue_size

问题二:Collector OOM反复重启

# 检查内存使用
curl -s http://localhost:8888/metrics | grep go_memstats_heap_inuse_bytes

# 检查是否配置了memory_limiter
grep -A5"memory_limiter"/etc/otelcol/config.yaml

# 检查tail_sampling的num_traces是否设太大
# 每个Trace在内存中大约占用2-5KB
# num_traces=200000 大约需要400MB-1GB内存

解决方案

确保memory_limiter在processors列表第一位

减小tail_sampling的num_traces

增大Collector的内存限制

如果是Agent模式,不要在Agent层做tail_sampling

问题三:SDK初始化失败,应用启动报错

# Java Agent常见错误
# "Failed to export spans. The request could not be executed."
# 原因:Collector地址不可达
# 解决:检查otel.exporter.otlp.endpoint配置

# "java.lang.NoClassDefFoundError: io/opentelemetry/..."
# 原因:Agent版本和应用依赖的OTel库版本冲突
# 解决:用Agent自带的instrumentation,移除应用pom.xml中的OTel依赖

# Go SDK常见错误
# "context deadline exceeded"
# 原因:Collector连接超时
# 解决:检查endpoint配置,确认Collector在运行

问题四:跨服务上下文传播断裂

# 排查步骤:
# 1. 确认所有服务使用相同的propagator
# 2. 检查中间件(Nginx/Kong/Envoy)是否透传了traceparent头
# 3. 检查消息队列消费者是否正确提取了上下文

# Nginx需要配置透传trace头
# proxy_set_header traceparent $http_traceparent;
# proxy_set_header tracestate $http_tracestate;

# 验证Nginx是否透传
curl -v -H"traceparent: 00-aaaabbbbccccddddeeeeffffgggghhhh-1111222233334444-01"
 http://nginx-proxy/api/test2>&1 | grep traceparent

5.1.3 调试模式

# 临时开启Collector debug日志
# 修改配置文件中的telemetry.logs.level为debug
# 或者用环境变量
OTEL_LOG_LEVEL=debug otelcol --config=/etc/otelcol/config.yaml

# 使用logging exporter查看处理的数据
# 在配置中临时添加logging exporter
# exporters:
#  logging:
#   verbosity: detailed
#   sampling_initial: 5
#   sampling_thereafter: 200

# Java Agent debug模式
java -javaagent:opentelemetry-javaagent.jar 
 -Dotel.javaagent.debug=true
 -jar app.jar
# 会输出所有instrumentation的加载信息和Span详情

5.2 性能监控

5.2.1 关键指标监控

# Collector接收的Span数量
curl -s http://localhost:8888/metrics | grep otelcol_receiver_accepted_spans

# Collector发送成功的Span数量
curl -s http://localhost:8888/metrics | grep otelcol_exporter_sent_spans

# Collector发送失败的Span数量
curl -s http://localhost:8888/metrics | grep otelcol_exporter_send_failed_spans

# Collector发送队列大小
curl -s http://localhost:8888/metrics | grep otelcol_exporter_queue_size

# Collector内存使用
curl -s http://localhost:8888/metrics | grep go_memstats_heap_inuse_bytes

# 处理器丢弃的Span数量
curl -s http://localhost:8888/metrics | grep otelcol_processor_dropped_spans

5.2.2 监控指标说明

指标名称 正常范围 告警阈值 说明
otelcol_receiver_accepted_spans rate 根据业务量 突降50%以上 接收速率突降说明有服务停止上报
otelcol_exporter_send_failed_spans rate 0 > 0持续5分钟 任何发送失败都需要关注
otelcol_exporter_queue_size < queue_size的70% > queue_size的85% 队列接近满说明后端写入跟不上
go_memstats_heap_inuse_bytes < limit_mib的80% > limit_mib的85% 内存接近限制需要扩容
otelcol_processor_dropped_spans rate 0 > 0 有Span被丢弃
traces_spanmetrics_latency P99 根据SLA 超过SLA阈值 从Trace生成的延迟指标

5.2.3 Prometheus监控规则

# prometheus-rules/otel-collector-alerts.yaml
groups:
-name:otel-collector-alerts
 rules:
  -alert:OtelCollectorDown
   expr:up{job="otel-collector"}==0
   for:2m
   labels:
    severity:critical
   annotations:
    summary:"OTel Collector实例{{ $labels.instance }}不可用"

  -alert:OtelCollectorSpanExportFailure
   expr:rate(otelcol_exporter_send_failed_spans_total[5m])>0
   for:5m
   labels:
    severity:warning
   annotations:
    summary:"OTel Collector Span发送失败"
    description:"失败速率:{{ $value }}/s,检查后端存储是否正常"

  -alert:OtelCollectorQueueNearFull
   expr:otelcol_exporter_queue_size/otelcol_exporter_queue_capacity>0.85
   for:5m
   labels:
    severity:warning
   annotations:
    summary:"OTel Collector发送队列接近满"

  -alert:OtelCollectorMemoryHigh
   expr:go_memstats_heap_inuse_bytes{job="otel-collector"}/1024/1024/1024>5
   for:5m
   labels:
    severity:warning
   annotations:
    summary:"OTel Collector堆内存超过5GB"

  -alert:OtelCollectorSpanDropped
   expr:rate(otelcol_processor_dropped_spans_total[5m])>0
   for:3m
   labels:
    severity:warning
   annotations:
    summary:"OTel Collector有Span被丢弃"

5.3 备份与恢复

5.3.1 备份策略

OTel Collector本身是无状态的,不需要备份数据,只需要备份配置文件。Trace数据的备份由后端存储(Jaeger/Tempo)负责。

#!/bin/bash
# otel-backup.sh
set-euo pipefail

BACKUP_DIR="/backup/otel/$(date +%Y%m%d)"
mkdir -p"$BACKUP_DIR"

# 备份Collector配置
tar czf"$BACKUP_DIR/otel-config.tar.gz"/etc/otelcol/

# 备份Java Agent配置
tar czf"$BACKUP_DIR/otel-agent-config.tar.gz"/opt/otel/ 2>/dev/null ||true

# 备份Jaeger配置(如果有)
tar czf"$BACKUP_DIR/jaeger-config.tar.gz"/etc/jaeger/ 2>/dev/null ||true

# 清理7天前的备份
find /backup/otel -maxdepth 1 -typed -mtime +7 -execrm -rf {} ;

echo"[$(date)] OTel配置备份完成:$BACKUP_DIR"

5.3.2 恢复流程

停止Collector:sudo systemctl stop otelcol

恢复配置:tar xzf /backup/otel/20250101/otel-config.tar.gz -C /

验证配置:otelcol validate --config=/etc/otelcol/agent-config.yaml

重启服务:sudo systemctl start otelcol && curl -s http://localhost:13133/

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • python
    +关注

    关注

    58

    文章

    4882

    浏览量

    90289
  • 微服务
    +关注

    关注

    0

    文章

    150

    浏览量

    8139

原文标题:告别“日志大海捞针”:OpenTelemetry 全链路追踪落地实战

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    关于 eBPF 安全可观测,你需要知道的那些事儿

    安全方面的一些思考和心得,本次分享将从监控和可观测、eBPF 安全可观测分析、内核安全可观测
    发表于 09-08 15:31

    微服务与容器技术实践

    基于微服务架构的技术实践(点击下载演讲PPT) 普元信息主任架构师顾伟在演讲中,分享了他们对微服务架构的认识,包括微服务演进过程、常见认知误区等,并阐述了结合容器云技术,分享在
    发表于 10-10 10:23 1次下载
    <b class='flag-5'>微服务</b>与容器技术<b class='flag-5'>实践</b>

    微服务架构与实践摘要

    本文主要类容是对微服务架构与实践摘要解析。微服务架构中的 “微” 体现了其核心要素,即服务的微型化,就是每个服务微小到只需专注做好一件事。这
    的头像 发表于 02-07 16:57 6849次阅读
    <b class='flag-5'>微服务</b>架构与<b class='flag-5'>实践</b>摘要

    基于拓扑分割的网络可观测分析方法

    针对电力网络可观测分析问题,对量测网络建模、网络拓扑可观测分析理论、不可观测节点的影响范围等方面进行了研究,提出了一种基于拓扑分割的网络
    发表于 03-06 18:03 0次下载
    基于拓扑分割的网络<b class='flag-5'>可观测</b><b class='flag-5'>性</b>分析方法

    eBPF安全可观测的前景展望

    本次分享将从监控和可观测、eBPF安全可观测分析、内核安全可观测展望三个方面展开。
    的头像 发表于 08-17 11:27 2421次阅读

    六大顶级、开源的数据可观测工具

    企业有很多商业数据可观测工具可供选择。商业工具在可伸缩、自动化和支持方面具有一些关键优势。然而,数据可观测的开源工具允许团队在没有前期
    的头像 发表于 12-16 11:29 3262次阅读

    追踪系统SkyWalking的原理

    什么是追踪追踪的原理
    的头像 发表于 01-17 11:00 5683次阅读

    华为云服务治理 | 微服务常见故障模式

    ),微服务可观测性能力(日志、监控、告警等)构建等。 华为云微服务治理专题主要探讨运行时治理。我们首先从常见的故障模式开始。 扩容缩容 在扩容场景下,新启动的微服务实例需要初始化数据库
    的头像 发表于 01-18 17:44 1491次阅读

    基调听云携手道客打造云原生智能可观测平台联合解决方案

    随着信息技术和互联网的发展,终端设备从 PC 端扩展到移动端,应用的架构从单体架构演进为分布式的微服务架构,软件系统服务层之间的交互日益复杂,给系统的运维管理带来了巨大的挑战,应运而生的可观测
    的头像 发表于 03-23 11:02 1493次阅读

    使用APM无法实现真正可观测的原因

    控制理论中的可观测是指:系统可以由其外部输出确定其内部状态的程度。在复杂 IT 系统中,具备可观测是为了让系统能达到某个预定的稳定性、错误率目标。随着
    的头像 发表于 09-18 10:23 2055次阅读
    使用APM无法实现真正<b class='flag-5'>可观测</b><b class='flag-5'>性</b>的原因

    什么是多云? 为什么我们需要多云可观测 (Observability)?

    什么是多云? 为什么我们需要多云可观测 (Observability)?
    的头像 发表于 10-12 17:12 1415次阅读
    什么是多云? 为什么我们需要多云<b class='flag-5'>可观测</b><b class='flag-5'>性</b> (Observability)?

    华为云发布可观测平台 AOM,以 AI 赋能应用运维可观测

    应用可用与稳定性。 该平台发布标志着华为云在推动数字化转型和智能化运维领域的又一重大突破,可观测平台的推出不仅为企业提供了更加全面和深入的系统监控和数据分析能力,还通过集成先进的人工智能技术,实现了对复杂应用环境的实时优化
    的头像 发表于 10-15 09:54 1822次阅读
    华为云发布<b class='flag-5'>全</b>栈<b class='flag-5'>可观测</b>平台 AOM,以 AI 赋能应用运维<b class='flag-5'>可观测</b>

    【质量视角】可观测背景下的质量保障思路

    目前质量团队正在积极建设和完善应用监控能力,旨在能及时发现并解决问题,为线上服务稳定性保驾护航。随着可观测概念的逐渐普及,监控的建设也有了新的挑战和使命。本文将探讨在可观测
    的头像 发表于 10-25 17:21 956次阅读
    【质量视角】<b class='flag-5'>可观测</b><b class='flag-5'>性</b>背景下的质量保障思路

    DeepSeek赋能Vixtel飞思达CloudFox可观测平台,打破可观测工程的实施壁垒

    随着云原生、微服务架构的普及,可观测工程(Observability)变得越来越重要。Vixtel飞思达(IBDT,港交所:1782)的CloudFox可观测
    的头像 发表于 02-21 17:20 880次阅读
    DeepSeek赋能Vixtel飞思达CloudFox<b class='flag-5'>可观测</b><b class='flag-5'>性</b>平台,打破<b class='flag-5'>可观测</b><b class='flag-5'>性</b>工程的实施壁垒

    IBM被 2025年 Gartner® 可观测平台魔力象限™ 评为领导者

    在 Gartner 发布的 2025年《可观测平台魔力象限》[1]中,IBM 被评为领导者(Leader)。我们相信,这是对于我们持续致力于提供创新、易用的可观测
    的头像 发表于 09-02 09:45 1011次阅读
    IBM被 2025年 Gartner® <b class='flag-5'>可观测</b><b class='flag-5'>性</b>平台魔力象限™ 评为领导者