Python 应用接入 OpenTelemetry

📅 2026/7/14 11:35:10
Python 应用接入 OpenTelemetry
一、系统从单体迁移到微服务半年后服务数量从 3 个膨胀到 30 个。某天凌晨用户投诉下单失败你打开监控面板面对 30 个服务的链路图一头雾水订单服务报错但根因是库存服务的数据库连接池耗尽还是支付服务的超时配置太短没有全链路追踪排查问题如同盲人摸象。可观测性系统的构建不是装个工具就能解决它需要日志、指标、追踪三位一体覆盖应用、基础设施、业务逻辑多个层面。二、可观测性的三大支柱与架构设计可观测性Observability是指系统通过外部输出日志、指标、追踪推断内部状态的能力。它包含三大支柱graph TB A[可观测性三大支柱] -- B[日志br/Logs] A -- C[指标br/Metrics] A -- D[追踪br/Traces] B -- B1[记录离散事件br/调试细节] B -- B2[结构化日志br/JSON格式] B -- B3[集中采集br/ELK/Loki] C -- C1[聚合数值br/性能监控] C -- C2[时序数据库br/Prometheus] C -- C3[告警规则br/阈值/异常检测] D -- D1[请求链路br/跨服务追踪] D -- D2[分布式追踪br/OpenTelemetry] D -- D3[性能分析br/瓶颈定位] style B fill:#e1f5fe style C fill:#fff3e0 style D fill:#e8f5e9**日志Logs**记录离散事件是最传统的可观测性手段。现代日志系统强调结构化日志使用 JSON 格式方便检索和分析。上下文关联每条日志包含 trace_id、span_id、user_id 等字段支持关联分析。集中采集使用 Filebeat、Fluentd 等工具采集存储到 Elasticsearch 或 Loki。**指标Metrics**是聚合数值适合监控趋势和告警。关键特性包括多维标签按服务、实例、端点等维度聚合。时序存储Prometheus、InfluxDB 等时序数据库优化存储和查询。主动拉取Prometheus 主动拉取指标简化客户端实现。**追踪Traces**记录请求在不同服务间的调用链路是分布式系统的调试利器。核心概念包括Trace一个请求的完整调用链路。Span链路中的一个操作如一次 API 调用。上下文传播通过 HTTP Header 传递 trace_id、span_id。三大支柱的关系日志提供调试细节但难以聚合。指标适合监控趋势但缺乏上下文。追踪连接服务和操作但覆盖不全。最佳实践是将三者关联通过 trace_id 关联日志通过 span_id 关联指标实现全链路可观测性。三、生产级可观测性系统的构建以下是一个基于 OpenTelemetry Prometheus Grafana Loki 的可观测性栈实现应用层OpenTelemetry 自动埋点# Python 应用接入 OpenTelemetry from opentelemetry import trace from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.requests import RequestsInstrumentor from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource # 初始化 TracerProvider resource Resource.create({ service.name: order-service, service.version: 1.0.0, deployment.environment: production }) trace.set_tracer_provider(TracerProvider(resourceresource)) # 配置 OTLP Exporter发送到 Jaeger 或 Tempo otlp_exporter OTLPSpanExporter(endpointotel-collector:4317, insecureTrue) span_processor BatchSpanProcessor(otlp_exporter) trace.get_tracer_provider().add_span_processor(span_processor) # 自动埋点 Flask 和 Requests app Flask(__name__) FlaskInstrumentor().instrument_app(app) RequestsInstrumentor().instrument() # 手动埋点可选 app.route(/order, methods[POST]) def create_order(): with trace.get_tracer(__name__).start_as_current_span(create_order) as span: # 添加自定义属性 span.set_attribute(order.id, request.json.get(order_id)) span.set_attribute(user.id, request.json.get(user_id)) # 记录事件 span.add_event(inventory_check_start) check_inventory(request.json) span.add_event(inventory_check_done) # 业务逻辑 order process_order(request.json) return jsonify(order) # 结构化日志包含 trace_id import structlog logger structlog.get_logger() app.route(/order, methods[POST]) def create_order(): with trace.get_tracer(__name__).start_as_current_span(create_order) as span: trace_id span.get_span_context().trace_id logger.info( create_order_start, trace_idformat(trace_id, 032x), order_idrequest.json.get(order_id) ) # 业务逻辑 logger.info( create_order_done, trace_idformat(trace_id, 032x), order_idorder.id ) return jsonify(order)指标采集Prometheus Grafana# Python 应用暴露 Prometheus 指标 from prometheus_client import Counter, Histogram, generate_latest from flask import Response # 定义指标 request_count Counter( http_requests_total, Total HTTP requests, [method, endpoint, status] ) request_latency Histogram( http_request_duration_seconds, HTTP request latency, [method, endpoint] ) app.route(/metrics) def metrics(): return Response(generate_latest(), mimetypetext/plain) app.route(/order, methods[POST]) def create_order(): start_time time.time() try: order process_order(request.json) # 记录指标 request_count.labels(POST, /order, 200).inc() request_latency.labels(POST, /order).observe(time.time() - start_time) return jsonify(order) except Exception as e: request_count.labels(POST, /order, 500).inc() raise e日志采集Loki Promtail# promtail-config.yaml server: http_listen_port: 9080 positions: filename: /tmp/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: flask-app static_configs: - targets: - localhost labels: job: flask-app __path__: /var/log/flask/*.log # 结构化日志格式JSON # 使用 python-json-logger 或 structlog { timestamp: 2024-07-14T10:30:00Z, level: INFO, message: create_order_start, trace_id: 4bf92f3577b34da6a3ce929d0e0e4736, span_id: 00f067aa0ba902b7, service.name: order-service, order.id: 12345 }Grafana 仪表盘配置{ dashboard: { title: Order Service Observability, panels: [ { title: Request Rate, targets: [ { expr: rate(http_requests_total{jobflask-app}[5m]) } ] }, { title: Request Latency (P99), targets: [ { expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{jobflask-app}[5m])) } ] }, { title: Error Rate, targets: [ { expr: rate(http_requests_total{jobflask-app, status500}[5m]) } ] } ] } }四、全链路追踪的实战与性能分析全链路追踪的核心价值是定位性能瓶颈。以下是一个典型的性能分析流程sequenceDiagram participant Client participant API Gateway participant Order Service participant Inventory Service participant Payment Service participant Database Client-API Gateway: POST /order API Gateway-Order Service: POST /api/order Order Service-Inventory Service: GET /api/inventory/check Inventory Service-Database: SELECT * FROM inventory Database--Inventory Service: Result Inventory Service--Order Service: OK Order Service-Payment Service: POST /api/payment Payment Service-Database: UPDATE payments Database--Payment Service: OK Payment Service--Order Service: OK Order Service--API Gateway: Order Created API Gateway--Client: 200 OK Note over Order Service, Payment Service: 瓶颈Payment Service 耗时 800ms通过 Jaeger 或 Tempo 的 UI可以清晰看到整个请求耗时 1.2 秒。Order Service 本身只耗时 50ms。Inventory Service 耗时 100ms。Payment Service 耗时 800ms瓶颈。进一步分析 Payment Service 的 Span发现是数据库查询未走索引添加索引后耗时降到 50ms。性能优化的实战技巧RED 方法Rate请求速率、Errors错误率、Duration延迟分布。这三个指标覆盖大部分性能监控需求。USE 方法Utilization利用率、Saturation饱和度、Errors错误。适用于资源监控CPU、内存、磁盘。黄金信号延迟、流量、错误、饱和度。Google SRE 提出的核心监控指标。告警规则配置Prometheus Alertmanager# alerting-rules.yaml groups: - name: flask-app rules: - alert: HighErrorRate expr: rate(http_requests_total{status~5..}[5m]) 0.05 for: 2m labels: severity: critical annotations: summary: 高错误率: {{ $value }} req/s description: 服务 {{ $labels.job }} 错误率超过 5% - alert: HighLatency expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) 0.5 for: 2m labels: severity: warning annotations: summary: 高延迟: P99 {{ $value }}s description: 服务 {{ $labels.job }} P99 延迟超过 500ms - alert: HighSaturation expr: process_resident_memory_bytes{jobflask-app} / 1024 / 1024 / 1024 2 for: 5m labels: severity: warning annotations: summary: 内存使用率高: {{ $value }}GB description: 服务 {{ $labels.job }} 内存使用超过 2GB五、可观测性系统的代价与工程陷阱可观测性系统不是免费的午餐它有一系列代价代价一性能开销OpenTelemetry 的自动埋点、日志的结构化输出、指标的集合计算都会消耗 CPU 和内存。对于资源受限的环境如边缘设备需要仔细权衡。优化策略采样不是所有请求都追踪可以按比例采样如 10%或按错误采样。异步导出Span 和指标的导出异步执行避免阻塞主请求。过滤器只采集关键服务的追踪数据。代价二存储成本日志、指标、追踪数据都需要存储且数据量增长很快。对于高流量系统存储成本可能超过计算成本。优化策略数据保留策略日志保留 7 天指标保留 30 天追踪保留 3 天。降采样旧数据降采样存储如 1 分钟精度降到 5 分钟。分层存储热数据在 SSD冷数据在对象存储如 S3。代价三复杂度爆炸引入可观测性栈后需要维护 Prometheus、Grafana、Loki、Jaeger/Tempo、OpenTelemetry Collectutor……系统复杂度显著增加。优化策略使用托管服务Grafana Cloud、Datadog、New Relic 等 SaaS 服务可以快速搭建可观测性栈。标准化全公司统一可观测性技术栈避免碎片化。自动化使用 Terraform、Ansible 自动化部署和配置。工程陷阱过度采集采集所有指标、所有日志、所有追踪导致数据爆炸反而难以发现问题。告警疲劳配置过多告警导致团队对告警麻木真正的问题被忽略。缺乏上下文日志和指标缺少关键字段如 user_id、order_id难以关联分析。独立开发者的实用主义建议从简单开始集中式日志如 Loki 关键指标如 Prometheus足以支撑早期产品。先解决痛点哪类问题最难排查就先构建哪方面的可观测性。建立 on-call 机制可观测性系统不是装饰品需要有人响应告警。定期演练每季度进行一次故障演练验证可观测性系统的有效性。深夜的监控面板终于完整咖啡也凉了。可观测性系统不是炫技的舞台而是解决问题的工具。真正重要的是理解你的系统瓶颈选择合适的可观测性手段并在成本、复杂度、有效性之间找到平衡点。毕竟技术的终极目标是让系统可信赖而不是让监控面板好看。