
【架构实战】OpenTelemetry链路追踪实战从零到生产的完整指南上篇文章我们聊了可观测性的三大支柱——Metrics、Logs、Traces。很多同学说道理懂了代码怎么写还是一头雾水。今天就来手把手实战用OpenTelemetry从零搭建一套生产级的链路追踪系统。一、为什么选OpenTelemetry市面上的APM方案很多Jaeger、Zipkin、SkyWalking但OpenTelemetry简称OTel是CNCF唯一的可观测性标准它有三个优势厂商无关今天用Jaeger明天换Pinpoint改配置就行不用改代码多语言统一Java、Go、Python、Node.js全支持一套协议走天下自动手动零侵入自动埋点 按需手动埋点灵活可控二、环境准备我们用Docker Compose一键启动所有组件version:3.8services:otel-collector:image:otel/opentelemetry-collector-contrib:latestcommand:[--config/etc/otel-collector-config.yaml]volumes:-./otel-collector-config.yaml:/etc/otel-collector-config.yamlports:-4317:4317# gRPC-4318:4318# HTTP-8888:8888# Prometheus metrics-8889:8889# Prometheus exporter metricsjaeger:image:jaegertracing/all-in-one:latestports:-16686:16686# UI-14250:14250# gRPCenvironment:-COLLECTOR_OTLP_ENABLEDtrueprometheus:image:prom/prometheus:latestvolumes:-./prometheus.yml:/etc/prometheus/prometheus.ymlports:-9090:9090grafana:image:grafana/grafana:latestports:-3000:3000environment:-GF_SECURITY_ADMIN_PASSWORDadminvolumes:-./grafana/provisioning:/etc/grafana/provisioningCollector配置文件receivers:otlp:protocols:grpc:endpoint:0.0.0.0:4317http:endpoint:0.0.0.0:4318processors:batch:timeout:5ssend_batch_size:1024memory_limiter:check_interval:2slimit_percentage:80exporters:jaeger:endpoint:jaeger:14250tls:insecure:trueprometheus:endpoint:0.0.0.0:8889service:pipelines:traces:receivers:[otlp]processors:[memory_limiter,batch]exporters:[jaeger]metrics:receivers:[otlp]processors:[memory_limiter,batch]exporters:[prometheus]三、Python服务埋点我们模拟一个典型的微服务调用链API Gateway → User Service → Order Service → Product Service。3.1 安装依赖pipinstallopentelemetry-api\opentelemetry-sdk\opentelemetry-exporter-otlp\opentelemetry-instrumentation-flask\opentelemetry-instrumentation-requests\opentelemetry-instrumentation-redis3.2 初始化SDKfromopentelemetryimporttracefromopentelemetry.sdk.traceimportTracerProviderfromopentelemetry.sdk.trace.exportimportBatchSpanProcessorfromopentelemetry.exporter.otlp.proto.grpc.trace_exporterimportOTLPSpanExporterfromopentelemetry.sdk.resourcesimportResource,SERVICE_NAMEdefinit_tracing(service_name:str,otlp_endpoint:strhttp://localhost:4317):resourceResource.create({SERVICE_NAME:service_name,service.version:1.0.0,deployment.environment:production})providerTracerProvider(resourceresource)exporterOTLPSpanExporter(endpointotlp_endpoint,insecureTrue)provider.add_span_processor(BatchSpanProcessor(exporter))trace.set_tracer_provider(provider)returntrace.get_tracer(service_name)3.3 手动埋点自动埋点只能覆盖HTTP和数据库手动埋点才是精髓fromopentelemetryimporttracefromopentelemetry.traceimportStatus,StatusCode tracerinit_tracing(order-service)defcreate_order(order_id:str,user_id:str):withtracer.start_as_current_span(create_order)asspan:# 设置关键属性span.set_attribute(order.id,order_id)span.set_attribute(order.user_id,user_id)try:# 验证库存withtracer.start_as_current_span(check_inventory)aschild:child.set_attribute(product.count,10)inventorycheck_inventory_db(order_id)child.set_attribute(inventory.available,inventory)# 创建订单记录withtracer.start_as_current_span(save_order):ordersave_order_to_db(order_id,user_id)span.set_attribute(order.total_amount,order.total)# 发送通知异步不阻塞主流程withtracer.start_as_current_span(send_notification,kindSpanKind.PRODUCER):publish_event(order_created,order)span.set_status(Status(StatusCode.OK))returnorderexceptInsufficientStockErrorase:span.set_status(Status(StatusCode.ERROR,str(e)))span.record_exception(e)raise3.4 跨服务上下文传播这是链路追踪最难的部分——Trace如何在服务间传递# 服务A注入TraceContext到HTTP Headerfromopentelemetry.propagateimportinject,extractdefcall_downstream_service(url:str,data:dict):headers{}inject(headers)# 自动把当前TraceContext注入到headersresponserequests.post(url,jsondata,headers{**headers,Content-Type:application/json})returnresponse.json()# 服务B从Header提取TraceContextfromopentelemetry.propagateimportextractfromopentelemetry.traceimportSpanKinddefhandle_request(request):contextextract(request.headers)# 从incoming请求中提取withtracer.start_as_current_span(handle_request,contextcontext,kindSpanKind.SERVER)asspan:span.set_attribute(http.method,request.method)span.set_attribute(http.url,request.url)# 业务逻辑...四、Docker Compose一键启动# 启动所有组件docker-composeup-d# 验证服务状态docker-composeps# 查看Jaeger UI# 浏览器访问 http://localhost:16686五、Grafana大盘配置光有Trace不够还要和Metrics联动。创建一个链路追踪大盘{panels:[{title:请求量 Top 10 服务,type:bargauge,targets:[{expr:topk(10, sum by (service_name) (rate(otelcol_exporter_sent_spans{type\traces\}[5m])))}]},{title:P99延迟分布,type:heatmap,targets:[{expr:histogram_quantile(0.99, sum by (service_name, le) (rate(otelgrpc_io_server_duration_bucket[5m])))}]},{title:错误率热力图,type:stat,targets:[{expr:sum by (service_name) (rate(otel_exporter_sent_spans{status_code\ERROR\}[5m])) / sum by (service_name) (rate(otel_exporter_sent_spans_total[5m])) * 100}]}]}六、生产经验总结跑了半年总结几条避坑指南1. 采样策略决定成本全量采样在生产环境是灾难。推荐固定采样只保留10%的请求尾部采样所有错误请求超过P99的慢请求必须保留samplerTraceIdRatioBased(0.1)# 10%采样率2. 属性命名规范提前制定命名规范避免order_id、“orderId”、orderid混用{attribute}.{sub_attribute} 例order.total_amount, user.id, product.sku3. 不要在Span里放敏感信息信用卡号、密码、完整身份证号不要出现在Attributes里那是给监控用的不是日志用的。4. 异步任务的Trace断开问题Celery/Bull队列里的任务默认会断开Trace。解决方案是用context.propagate()把Context序列化后传到队列里。七、效果验证用JMeter或wrk打个压测# 模拟100并发持续30秒wrk-t4-c100-d30shttp://localhost:8080/api/orders然后去Jaeger里搜索输入服务名过滤service order-service查看耗时最长的Trace选择排序方式为Duration点击任意一个Trace看完整的调用瀑布图你会看到Gateway收到请求0ms→ UserService鉴权3ms→ OrderService创建订单45ms→ ProductService查库存8ms→ 回调通知12ms总耗时68ms每一个环节清晰可见。下期预告链路追踪只是起点如何把这些数据真正用起来下一期聊聊如何用GrafanaOpenTelemetry搭建全链路可观测平台以及如何设置智能告警——不是CPU80%这种粗放告警而是XX接口P99延迟环比增长30%的精准告警。