「知识图谱生成工具」一键将文件夹内容变身为交互式知识图谱的免安装桌面工具文末附免费下载链接-CSDN博客目录开篇当微服务变成黑盒链路追踪核心概念Trace、Span、BaggageJaeger架构全景从Agent到UI数据存储选型Cassandra vs Elasticsearch vs BadgerOpenTelemetry标准化采集方案采样策略如何在性能和精度间找平衡与Prometheus/Grafana集成Metrics Traces联动实战代码从零搭建Jaeger环境文末三件套开篇当微服务变成黑盒你是否遇到过这些让人抓狂的场景用户反馈页面加载慢但你不知道慢在哪一层一个API调用涉及8个服务出问题像大海捞针服务间调用关系像 spaghetti code理不清头绪明明每个服务都很快但整体响应就是慢这就像你家水管漏水但你不知道漏在厨房、卫生间还是墙里。链路追踪Distributed Tracing就是解决这个问题的管道探测仪。而Jaeger正是CNCF孵化的开源链路追踪神器由Uber开源现已成为云原生可观测性的标配工具。本文将带你从概念到实战彻底掌握Jaeger的使用。链路追踪核心概念Trace、Span、BaggageTrace调用链Trace是一次完整请求的人生轨迹。从用户点击按钮开始到最终返回结果中间经过的所有服务调用构成一条Trace。每个Trace有一个唯一的Trace ID就像快递单号全程跟踪不丢失。┌─────────────────────────────────────────────────────────────┐ │ Trace ID: abc123-def456-ghi789 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Gateway │───▶│ Order │───▶│ Payment │───▶│ Inventory│ │ │ │ 50ms │ │ 120ms │ │ 200ms │ │ 80ms │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ └──────────────┴──────────────┴──────────────┘ │ │ Total: 450ms │ └─────────────────────────────────────────────────────────────┘Span单次调用Span是Trace的细胞代表一次具体的操作。每个Span包含Operation Name操作名称如GET /api/usersStart Time / Duration开始时间和耗时Tags元数据标签如http.methodGET, http.status_code200Logs结构化日志事件SpanContext上下文信息Trace ID, Span ID等// Span的结构示意 type Span struct { TraceID string // 所属Trace的ID SpanID string // 自己的ID ParentSpanID string // 父Span的ID根Span为空 OperationName string // 操作名 StartTime time.Time // 开始时间 Duration time.Duration // 耗时 Tags map[string]interface{} // 标签 Logs []Log // 日志事件 }⚠️避坑警告不要把所有操作都塞进一个Span粒度太粗会失去追踪意义粒度太细会产生数据爆炸。建议按服务边界或关键操作划分。Baggage上下文传递Baggage是跨服务的传声筒让上下文信息随请求流动。想象你在餐厅点餐服务员把你的忌口不吃香菜写在订单上传给厨房。Baggage就是干这个的——把用户ID、请求来源、灰度标记等信息从入口服务传递到所有下游服务。// 设置Baggage span.SetBaggageItem(user-id, 12345) span.SetBaggageItem(request-source, mobile-app) // 在下游服务获取Baggage userID : span.BaggageItem(user-id)效率技巧Baggage会随每个Span传播存储成本较高。只放必要且精简的信息别把整个User对象塞进去Jaeger架构全景从Agent到UIJaeger采用经典的分层架构各组件职责清晰┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger UI │ │ (可视化查询与分析界面) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ HTTP/gRPC ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Query │ │ (查询API从存储检索数据) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────────────────────────────────────────────────────────────┐ │ Storage Backend │ │ (Cassandra / Elasticsearch / Badger / Kafka) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Collector │ │ (接收、验证、转换、批量写入存储) │ │ 处理能力10万 Span/秒 │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ UDP/HTTP/gRPC ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Agent │ │ (本地守护进程批量转发给Collector) │ │ 部署在每个主机/容器/K8s节点上 │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Service A │ │ Service B │ │ Service C │ │ Service D │ │ (Java) │ │ (Go) │ │ (Python) │ │ (Node.js) │ │ OpenTelemetry│ │ OpenTelemetry│ │ OpenTelemetry│ │ OpenTelemetry│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘各组件详解组件职责部署方式Agent本地数据收集和批量转发DaemonSetK8s或 SidecarCollector接收多协议数据处理后写入存储多实例部署支持负载均衡Storage持久化存储Span数据独立集群或托管服务Query提供查询API与Collector同机或独立部署UI可视化界面通常与Query一起部署效率技巧Agent作为本地缓冲层能显著降低Collector压力。即使Collector短暂不可用Agent也会缓存数据避免丢数据。数据存储选型Cassandra vs Elasticsearch vs BadgerJaeger支持多种存储后端选择取决于你的数据规模和查询需求存储方案对比特性CassandraElasticsearchBadger写入性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐查询灵活性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐存储压缩比~1:10~1:5~1:10运维复杂度高中低适用规模大规模生产中小规模复杂查询小规模/测试成本高中低选型建议选Cassandra如果你日Span量超过10亿有专业的运维团队追求极致写入性能查询模式相对固定选Elasticsearch如果你需要灵活的查询能力全文搜索、聚合分析已经使用ELK栈中小规模日Span量1亿希望与Metrics/Logs统一存储选Badger如果你本地测试或开发环境小规模部署单机不想维护外部存储快速验证Jaeger功能⚠️避坑警告Elasticsearch作为存储时索引分片数要合理规划默认5个分片对小集群是浪费大集群又可能成为瓶颈。建议根据数据量动态调整。OpenTelemetry标准化采集方案为什么选OpenTelemetry以前每个追踪系统都有自己的SDKZipkin用Brave、Jaeger用jaeger-client就像每个餐厅有自己的点餐App。OpenTelemetry是CNCF推出的通用语言一个SDK适配所有后端。┌─────────────────────────────────────────────────────────────────┐ │ OpenTelemetry SDK │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ Traces │ │ Metrics │ │ Logs │ │ │ │ (链路追踪) │ │ (指标) │ │ (日志) │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ OpenTelemetry Collector │ │ (接收OTLP导出到Jaeger/Zipkin/Prometheus等) │ └─────────────────────────────────────────────────────────────────┘多语言SDK支持语言SDK成熟度自动埋点支持Java⭐⭐⭐⭐⭐自动AgentGo⭐⭐⭐⭐⭐手动部分自动Python⭐⭐⭐⭐⭐自动InstrumentationNode.js⭐⭐⭐⭐自动Instrumentation.NET⭐⭐⭐⭐⭐自动AgentC⭐⭐⭐⭐手动Java自动埋点示例# 下载OpenTelemetry Java Agent wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.32.0/opentelemetry-javaagent.jar # 启动应用时附加Agent java -javaagent:opentelemetry-javaagent.jar \ -Dotel.service.nameorder-service \ -Dotel.traces.exporterotlp \ -Dotel.exporter.otlp.endpointhttp://jaeger-collector:4317 \ -jar order-service.jar**就这么简单**不用改一行代码自动追踪HTTP调用、数据库访问、消息队列等。效率技巧生产环境建议开启批处理和压缩减少网络开销-Dotel.exporter.otlp.protocolgrpc \ -Dotel.bsp.schedule.delay1000 \ -Dotel.exporter.otlp.compressiongzip采样策略如何在性能和精度间找平衡如果全量采集数据量会大到让你破产。采样策略就是抓重点的艺术。三种采样策略┌─────────────────────────────────────────────────────────────────┐ │ 采样策略对比 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【头部采样】Head-based Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 采样决策在请求入口处做出 │ │ └─────────┘ │ 要么全采要么全不采 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ 【尾部采样】Tail-based Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 所有Span先缓存等Trace完成后再决策 │ │ └─────────┘ │ 可以根据整体特征如错误、延迟采样 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ └──────────────┴──────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 采样决策器 │ │ │ │ (延迟1s? │ │ │ │ 有错误?) │ │ │ └─────────────┘ │ │ │ │ 【自适应采样】Adaptive Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 根据流量模式动态调整采样率 │ │ └─────────┘ │ 高频接口低采样低频接口高采样 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘策略详解策略原理优点缺点适用场景头部采样在请求入口决定是否采样简单、低延迟可能错过下游错误高流量、均匀分布尾部采样收集完整Trace后再决策精准捕获异常内存开销大异常排查为主自适应采样根据接口频率动态调整平衡覆盖面和成本配置复杂多接口混合场景Jaeger客户端采样配置# sampler.type 可选值 # - const: 固定采样0或1 # - probabilistic: 概率采样0.0-1.0 # - ratelimiting: 速率限制每秒N个 # - remote: 从Agent动态获取配置 sampler: type: probabilistic param: 0.1 # 采样10%的请求 # 自适应采样配置 adaptive_sampling: enabled: true target_samples_per_second: 10 # 每个操作每秒目标采样数⚠️避坑警告尾部采样需要缓存未完成Trace的Span内存占用与最大Trace时长成正比。如果设置不当OOM会来找你与Prometheus/Grafana集成Metrics Traces联动可观测性三大支柱┌─────────────────────────────────────┐ │ 可观测性三大支柱 │ └─────────────────────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Metrics │ │ Logs │ │ Traces │ │ (指标) │ │ (日志) │ │ (链路追踪) │ ├─────────────┤ ├─────────────┤ ├─────────────┤ │ 什么出错了 │ │ 为什么出错 │ │ 错在哪里 │ │ CPU 80% │ │ NullPointer │ │ DB查询慢 │ │ QPS下降 │ │ Exception │ │ 在Order服务 │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └───────────────────────────┼───────────────────────────┘ ▼ ┌─────────────────────────────────────┐ │ 联动分析 快速定位根因 │ │ Metrics发现异常 → Traces定位服务 │ │ → Logs查看详情 → 快速修复 │ └─────────────────────────────────────┘Jaeger与Prometheus集成场景Prometheus告警订单服务P99延迟2s如何快速定位解决方案在Grafana中添加Jaeger链接一键跳转。# Grafana数据源配置 apiVersion: 1 datasources: - name: Jaeger type: jaeger url: http://jaeger-query:16686 jsonData: tracesToLogs: datasourceUid: loki # 关联日志数据源 tags: [pod, namespace] tracesToMetrics: datasourceUid: prometheus # 关联指标数据源 queries: - name: Request rate query: sum(rate(http_requests_total{service$service}[5m]))从Metrics到Traces的跳转在Grafana图表中添加Exemplar典型样本点击即可跳转到对应Trace# PromQL查询启用exemplar histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le) )┌─────────────────────────────────────────────────────────────────┐ │ Grafana Dashboard │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ P99 Latency: 2.3s ⚠️ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ /\ │ │ │ │ │ │ / \ ┌───┐ ← 点击这个点 │ │ │ │ │ │ ─────/────\──/─────\───────────────────── │ │ │ │ │ │ / \/ \ │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 点击后跳转: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Jaeger UI │ │ │ │ Trace ID: a1b2c3d4e5f6... │ │ │ │ Duration: 2.31s │ │ │ │ Services: gateway → order → payment → inventory │ │ │ │ └─ payment: 1.8s (瓶颈!) │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘效率技巧在Jaeger中给关键Span打标签如errortrue、slow_querytrue可以更方便地从Metrics过滤出异常Trace。实战代码从零搭建Jaeger环境步骤1Docker Compose一键启动# docker-compose.yml version: 3.8 services: # Jaeger All-in-One开发测试用 jaeger: image: jaegertracing/all-in-one:1.50 container_name: jaeger ports: - 16686:16686 # UI - 4317:4317 # OTLP gRPC - 4318:4318 # OTLP HTTP - 14250:14250 # Model proto - 14268:14268 # Jaeger HTTP thrift - 14269:14269 # Admin - 9411:9411 # Zipkin兼容 environment: - COLLECTOR_OTLP_ENABLEDtrue networks: - jaeger-net # 示例服务订单服务Go order-service: build: ./order-service environment: - OTEL_EXPORTER_OTLP_ENDPOINThttp://jaeger:4317 - OTEL_SERVICE_NAMEorder-service depends_on: - jaeger networks: - jaeger-net # 示例服务支付服务Go payment-service: build: ./payment-service environment: - OTEL_EXPORTER_OTLP_ENDPOINThttp://jaeger:4317 - OTEL_SERVICE_NAMEpayment-service depends_on: - jaeger networks: - jaeger-net networks: jaeger-net: driver: bridge步骤2Go服务接入OpenTelemetry// order-service/main.go package main import ( context fmt 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/sdk/resource sdktrace go.opentelemetry.io/otel/sdk/trace semconv go.opentelemetry.io/otel/semconv/v1.21.0 go.opentelemetry.io/otel/trace google.golang.org/grpc google.golang.org/grpc/credentials/insecure ) var tracer trace.Tracer func initTracer() func() { ctx : context.Background() // 创建OTLP exporter conn, err : grpc.DialContext(ctx, jaeger:4317, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), ) if err ! nil { log.Fatal(err) } exporter, err : otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) if err ! nil { log.Fatal(err) } // 创建TracerProvider tp : sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceName(order-service), attribute.String(environment, dev), attribute.String(version, v1.0.0), )), ) otel.SetTracerProvider(tp) tracer tp.Tracer(order-service) return func() { if err : tp.Shutdown(ctx); err ! nil { log.Printf(Error shutting down tracer provider: %v, err) } } } func orderHandler(w http.ResponseWriter, r *http.Request) { ctx : r.Context() // 创建Span ctx, span : tracer.Start(ctx, create-order) defer span.End() // 添加标签 span.SetAttributes( attribute.String(user.id, r.Header.Get(X-User-ID)), attribute.String(order.type, standard), ) // 模拟处理 time.Sleep(50 * time.Millisecond) // 调用支付服务模拟 if err : callPaymentService(ctx); err ! nil { span.RecordError(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintln(w, Order created successfully!) } func callPaymentService(ctx context.Context) error { ctx, span : tracer.Start(ctx, call-payment-service) defer span.End() // 模拟HTTP调用 req, _ : http.NewRequestWithContext(ctx, POST, http://payment-service:8081/pay, nil) client : http.Client{Timeout: 5 * time.Second} resp, err : client.Do(req) if err ! nil { return err } defer resp.Body.Close() span.SetAttributes(attribute.Int(http.status_code, resp.StatusCode)) return nil } func main() { cleanup : initTracer() defer cleanup() http.HandleFunc(/order, orderHandler) log.Println(Order service starting on :8080) log.Fatal(http.ListenAndServe(:8080, nil)) }步骤3启动并验证# 启动服务 docker-compose up -d # 访问Jaeger UI open http://localhost:16686 # 发送测试请求 for i in {1..10}; do curl -H X-User-ID: user-$i http://localhost:8080/order done步骤4Kubernetes部署# jaeger-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: jaeger spec: replicas: 1 selector: matchLabels: app: jaeger template: metadata: labels: app: jaeger spec: containers: - name: jaeger image: jaegertracing/all-in-one:1.50 env: - name: COLLECTOR_OTLP_ENABLED value: true ports: - containerPort: 16686 - containerPort: 4317 - containerPort: 4318 --- apiVersion: v1 kind: Service metadata: name: jaeger spec: selector: app: jaeger ports: - name: ui port: 16686 targetPort: 16686 - name: otlp-grpc port: 4317 targetPort: 4317 - name: otlp-http port: 4318 targetPort: 4318文末三件套1. 【源码获取】关注此系列获取后续更新后台回复‘jaeger’获取完整源码和配置文件。2. 【思考题】你们的链路追踪采样率是多少是固定采样还是自适应采样欢迎在评论区分享你的实践经验3. 【系列预告】云原生技术栈系列持续更新中高可用架构设计→ 多活、熔断、限流实战服务网格Istio→ 零信任网络与流量治理混合云部署→ 跨云容灾与数据同步边缘计算→ K3s与边缘节点管理总结要点内容核心概念Trace调用链, Span单次调用, Baggage上下文传递架构Agent → Collector → Storage → Query → UI存储选型大规模选Cassandra中小规模选ES测试用Badger采集方案OpenTelemetry统一SDK支持多语言自动埋点采样策略头部采样简单尾部采样精准自适应采样平衡性能指标10万 Span/秒/Collector存储压缩1:10查询100ms链路追踪不是银弹但没有链路追踪的微服务就是盲人摸象。Jaeger作为CNCF毕业项目已经证明了它在生产环境的可靠性。希望本文能帮你快速上手让性能瓶颈无处遁形标签Jaeger链路追踪OpenTelemetry分布式追踪可观测性微服务监控
云原生技术14-OpenTelemetry + Jaeger:云原生可观测性的新标配
发布时间:2026/6/15 12:53:54
「知识图谱生成工具」一键将文件夹内容变身为交互式知识图谱的免安装桌面工具文末附免费下载链接-CSDN博客目录开篇当微服务变成黑盒链路追踪核心概念Trace、Span、BaggageJaeger架构全景从Agent到UI数据存储选型Cassandra vs Elasticsearch vs BadgerOpenTelemetry标准化采集方案采样策略如何在性能和精度间找平衡与Prometheus/Grafana集成Metrics Traces联动实战代码从零搭建Jaeger环境文末三件套开篇当微服务变成黑盒你是否遇到过这些让人抓狂的场景用户反馈页面加载慢但你不知道慢在哪一层一个API调用涉及8个服务出问题像大海捞针服务间调用关系像 spaghetti code理不清头绪明明每个服务都很快但整体响应就是慢这就像你家水管漏水但你不知道漏在厨房、卫生间还是墙里。链路追踪Distributed Tracing就是解决这个问题的管道探测仪。而Jaeger正是CNCF孵化的开源链路追踪神器由Uber开源现已成为云原生可观测性的标配工具。本文将带你从概念到实战彻底掌握Jaeger的使用。链路追踪核心概念Trace、Span、BaggageTrace调用链Trace是一次完整请求的人生轨迹。从用户点击按钮开始到最终返回结果中间经过的所有服务调用构成一条Trace。每个Trace有一个唯一的Trace ID就像快递单号全程跟踪不丢失。┌─────────────────────────────────────────────────────────────┐ │ Trace ID: abc123-def456-ghi789 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Gateway │───▶│ Order │───▶│ Payment │───▶│ Inventory│ │ │ │ 50ms │ │ 120ms │ │ 200ms │ │ 80ms │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ └──────────────┴──────────────┴──────────────┘ │ │ Total: 450ms │ └─────────────────────────────────────────────────────────────┘Span单次调用Span是Trace的细胞代表一次具体的操作。每个Span包含Operation Name操作名称如GET /api/usersStart Time / Duration开始时间和耗时Tags元数据标签如http.methodGET, http.status_code200Logs结构化日志事件SpanContext上下文信息Trace ID, Span ID等// Span的结构示意 type Span struct { TraceID string // 所属Trace的ID SpanID string // 自己的ID ParentSpanID string // 父Span的ID根Span为空 OperationName string // 操作名 StartTime time.Time // 开始时间 Duration time.Duration // 耗时 Tags map[string]interface{} // 标签 Logs []Log // 日志事件 }⚠️避坑警告不要把所有操作都塞进一个Span粒度太粗会失去追踪意义粒度太细会产生数据爆炸。建议按服务边界或关键操作划分。Baggage上下文传递Baggage是跨服务的传声筒让上下文信息随请求流动。想象你在餐厅点餐服务员把你的忌口不吃香菜写在订单上传给厨房。Baggage就是干这个的——把用户ID、请求来源、灰度标记等信息从入口服务传递到所有下游服务。// 设置Baggage span.SetBaggageItem(user-id, 12345) span.SetBaggageItem(request-source, mobile-app) // 在下游服务获取Baggage userID : span.BaggageItem(user-id)效率技巧Baggage会随每个Span传播存储成本较高。只放必要且精简的信息别把整个User对象塞进去Jaeger架构全景从Agent到UIJaeger采用经典的分层架构各组件职责清晰┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger UI │ │ (可视化查询与分析界面) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ HTTP/gRPC ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Query │ │ (查询API从存储检索数据) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────────────────────────────────────────────────────────────┐ │ Storage Backend │ │ (Cassandra / Elasticsearch / Badger / Kafka) │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Collector │ │ (接收、验证、转换、批量写入存储) │ │ 处理能力10万 Span/秒 │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ UDP/HTTP/gRPC ┌─────────────────────────────────────────────────────────────────────┐ │ Jaeger Agent │ │ (本地守护进程批量转发给Collector) │ │ 部署在每个主机/容器/K8s节点上 │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Service A │ │ Service B │ │ Service C │ │ Service D │ │ (Java) │ │ (Go) │ │ (Python) │ │ (Node.js) │ │ OpenTelemetry│ │ OpenTelemetry│ │ OpenTelemetry│ │ OpenTelemetry│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘各组件详解组件职责部署方式Agent本地数据收集和批量转发DaemonSetK8s或 SidecarCollector接收多协议数据处理后写入存储多实例部署支持负载均衡Storage持久化存储Span数据独立集群或托管服务Query提供查询API与Collector同机或独立部署UI可视化界面通常与Query一起部署效率技巧Agent作为本地缓冲层能显著降低Collector压力。即使Collector短暂不可用Agent也会缓存数据避免丢数据。数据存储选型Cassandra vs Elasticsearch vs BadgerJaeger支持多种存储后端选择取决于你的数据规模和查询需求存储方案对比特性CassandraElasticsearchBadger写入性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐查询灵活性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐存储压缩比~1:10~1:5~1:10运维复杂度高中低适用规模大规模生产中小规模复杂查询小规模/测试成本高中低选型建议选Cassandra如果你日Span量超过10亿有专业的运维团队追求极致写入性能查询模式相对固定选Elasticsearch如果你需要灵活的查询能力全文搜索、聚合分析已经使用ELK栈中小规模日Span量1亿希望与Metrics/Logs统一存储选Badger如果你本地测试或开发环境小规模部署单机不想维护外部存储快速验证Jaeger功能⚠️避坑警告Elasticsearch作为存储时索引分片数要合理规划默认5个分片对小集群是浪费大集群又可能成为瓶颈。建议根据数据量动态调整。OpenTelemetry标准化采集方案为什么选OpenTelemetry以前每个追踪系统都有自己的SDKZipkin用Brave、Jaeger用jaeger-client就像每个餐厅有自己的点餐App。OpenTelemetry是CNCF推出的通用语言一个SDK适配所有后端。┌─────────────────────────────────────────────────────────────────┐ │ OpenTelemetry SDK │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ Traces │ │ Metrics │ │ Logs │ │ │ │ (链路追踪) │ │ (指标) │ │ (日志) │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ OpenTelemetry Collector │ │ (接收OTLP导出到Jaeger/Zipkin/Prometheus等) │ └─────────────────────────────────────────────────────────────────┘多语言SDK支持语言SDK成熟度自动埋点支持Java⭐⭐⭐⭐⭐自动AgentGo⭐⭐⭐⭐⭐手动部分自动Python⭐⭐⭐⭐⭐自动InstrumentationNode.js⭐⭐⭐⭐自动Instrumentation.NET⭐⭐⭐⭐⭐自动AgentC⭐⭐⭐⭐手动Java自动埋点示例# 下载OpenTelemetry Java Agent wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.32.0/opentelemetry-javaagent.jar # 启动应用时附加Agent java -javaagent:opentelemetry-javaagent.jar \ -Dotel.service.nameorder-service \ -Dotel.traces.exporterotlp \ -Dotel.exporter.otlp.endpointhttp://jaeger-collector:4317 \ -jar order-service.jar**就这么简单**不用改一行代码自动追踪HTTP调用、数据库访问、消息队列等。效率技巧生产环境建议开启批处理和压缩减少网络开销-Dotel.exporter.otlp.protocolgrpc \ -Dotel.bsp.schedule.delay1000 \ -Dotel.exporter.otlp.compressiongzip采样策略如何在性能和精度间找平衡如果全量采集数据量会大到让你破产。采样策略就是抓重点的艺术。三种采样策略┌─────────────────────────────────────────────────────────────────┐ │ 采样策略对比 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【头部采样】Head-based Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 采样决策在请求入口处做出 │ │ └─────────┘ │ 要么全采要么全不采 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ 【尾部采样】Tail-based Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 所有Span先缓存等Trace完成后再决策 │ │ └─────────┘ │ 可以根据整体特征如错误、延迟采样 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ └──────────────┴──────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 采样决策器 │ │ │ │ (延迟1s? │ │ │ │ 有错误?) │ │ │ └─────────────┘ │ │ │ │ 【自适应采样】Adaptive Sampling │ │ ┌─────────┐ │ │ │ Gateway │────┐ 根据流量模式动态调整采样率 │ │ └─────────┘ │ 高频接口低采样低频接口高采样 │ │ │ ▼ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ └───▶│ Service │───▶│ Service │───▶│ Service │ │ │ │ A │ │ B │ │ C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘策略详解策略原理优点缺点适用场景头部采样在请求入口决定是否采样简单、低延迟可能错过下游错误高流量、均匀分布尾部采样收集完整Trace后再决策精准捕获异常内存开销大异常排查为主自适应采样根据接口频率动态调整平衡覆盖面和成本配置复杂多接口混合场景Jaeger客户端采样配置# sampler.type 可选值 # - const: 固定采样0或1 # - probabilistic: 概率采样0.0-1.0 # - ratelimiting: 速率限制每秒N个 # - remote: 从Agent动态获取配置 sampler: type: probabilistic param: 0.1 # 采样10%的请求 # 自适应采样配置 adaptive_sampling: enabled: true target_samples_per_second: 10 # 每个操作每秒目标采样数⚠️避坑警告尾部采样需要缓存未完成Trace的Span内存占用与最大Trace时长成正比。如果设置不当OOM会来找你与Prometheus/Grafana集成Metrics Traces联动可观测性三大支柱┌─────────────────────────────────────┐ │ 可观测性三大支柱 │ └─────────────────────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Metrics │ │ Logs │ │ Traces │ │ (指标) │ │ (日志) │ │ (链路追踪) │ ├─────────────┤ ├─────────────┤ ├─────────────┤ │ 什么出错了 │ │ 为什么出错 │ │ 错在哪里 │ │ CPU 80% │ │ NullPointer │ │ DB查询慢 │ │ QPS下降 │ │ Exception │ │ 在Order服务 │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └───────────────────────────┼───────────────────────────┘ ▼ ┌─────────────────────────────────────┐ │ 联动分析 快速定位根因 │ │ Metrics发现异常 → Traces定位服务 │ │ → Logs查看详情 → 快速修复 │ └─────────────────────────────────────┘Jaeger与Prometheus集成场景Prometheus告警订单服务P99延迟2s如何快速定位解决方案在Grafana中添加Jaeger链接一键跳转。# Grafana数据源配置 apiVersion: 1 datasources: - name: Jaeger type: jaeger url: http://jaeger-query:16686 jsonData: tracesToLogs: datasourceUid: loki # 关联日志数据源 tags: [pod, namespace] tracesToMetrics: datasourceUid: prometheus # 关联指标数据源 queries: - name: Request rate query: sum(rate(http_requests_total{service$service}[5m]))从Metrics到Traces的跳转在Grafana图表中添加Exemplar典型样本点击即可跳转到对应Trace# PromQL查询启用exemplar histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le) )┌─────────────────────────────────────────────────────────────────┐ │ Grafana Dashboard │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ P99 Latency: 2.3s ⚠️ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ /\ │ │ │ │ │ │ / \ ┌───┐ ← 点击这个点 │ │ │ │ │ │ ─────/────\──/─────\───────────────────── │ │ │ │ │ │ / \/ \ │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 点击后跳转: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Jaeger UI │ │ │ │ Trace ID: a1b2c3d4e5f6... │ │ │ │ Duration: 2.31s │ │ │ │ Services: gateway → order → payment → inventory │ │ │ │ └─ payment: 1.8s (瓶颈!) │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘效率技巧在Jaeger中给关键Span打标签如errortrue、slow_querytrue可以更方便地从Metrics过滤出异常Trace。实战代码从零搭建Jaeger环境步骤1Docker Compose一键启动# docker-compose.yml version: 3.8 services: # Jaeger All-in-One开发测试用 jaeger: image: jaegertracing/all-in-one:1.50 container_name: jaeger ports: - 16686:16686 # UI - 4317:4317 # OTLP gRPC - 4318:4318 # OTLP HTTP - 14250:14250 # Model proto - 14268:14268 # Jaeger HTTP thrift - 14269:14269 # Admin - 9411:9411 # Zipkin兼容 environment: - COLLECTOR_OTLP_ENABLEDtrue networks: - jaeger-net # 示例服务订单服务Go order-service: build: ./order-service environment: - OTEL_EXPORTER_OTLP_ENDPOINThttp://jaeger:4317 - OTEL_SERVICE_NAMEorder-service depends_on: - jaeger networks: - jaeger-net # 示例服务支付服务Go payment-service: build: ./payment-service environment: - OTEL_EXPORTER_OTLP_ENDPOINThttp://jaeger:4317 - OTEL_SERVICE_NAMEpayment-service depends_on: - jaeger networks: - jaeger-net networks: jaeger-net: driver: bridge步骤2Go服务接入OpenTelemetry// order-service/main.go package main import ( context fmt 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/sdk/resource sdktrace go.opentelemetry.io/otel/sdk/trace semconv go.opentelemetry.io/otel/semconv/v1.21.0 go.opentelemetry.io/otel/trace google.golang.org/grpc google.golang.org/grpc/credentials/insecure ) var tracer trace.Tracer func initTracer() func() { ctx : context.Background() // 创建OTLP exporter conn, err : grpc.DialContext(ctx, jaeger:4317, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), ) if err ! nil { log.Fatal(err) } exporter, err : otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) if err ! nil { log.Fatal(err) } // 创建TracerProvider tp : sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceName(order-service), attribute.String(environment, dev), attribute.String(version, v1.0.0), )), ) otel.SetTracerProvider(tp) tracer tp.Tracer(order-service) return func() { if err : tp.Shutdown(ctx); err ! nil { log.Printf(Error shutting down tracer provider: %v, err) } } } func orderHandler(w http.ResponseWriter, r *http.Request) { ctx : r.Context() // 创建Span ctx, span : tracer.Start(ctx, create-order) defer span.End() // 添加标签 span.SetAttributes( attribute.String(user.id, r.Header.Get(X-User-ID)), attribute.String(order.type, standard), ) // 模拟处理 time.Sleep(50 * time.Millisecond) // 调用支付服务模拟 if err : callPaymentService(ctx); err ! nil { span.RecordError(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintln(w, Order created successfully!) } func callPaymentService(ctx context.Context) error { ctx, span : tracer.Start(ctx, call-payment-service) defer span.End() // 模拟HTTP调用 req, _ : http.NewRequestWithContext(ctx, POST, http://payment-service:8081/pay, nil) client : http.Client{Timeout: 5 * time.Second} resp, err : client.Do(req) if err ! nil { return err } defer resp.Body.Close() span.SetAttributes(attribute.Int(http.status_code, resp.StatusCode)) return nil } func main() { cleanup : initTracer() defer cleanup() http.HandleFunc(/order, orderHandler) log.Println(Order service starting on :8080) log.Fatal(http.ListenAndServe(:8080, nil)) }步骤3启动并验证# 启动服务 docker-compose up -d # 访问Jaeger UI open http://localhost:16686 # 发送测试请求 for i in {1..10}; do curl -H X-User-ID: user-$i http://localhost:8080/order done步骤4Kubernetes部署# jaeger-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: jaeger spec: replicas: 1 selector: matchLabels: app: jaeger template: metadata: labels: app: jaeger spec: containers: - name: jaeger image: jaegertracing/all-in-one:1.50 env: - name: COLLECTOR_OTLP_ENABLED value: true ports: - containerPort: 16686 - containerPort: 4317 - containerPort: 4318 --- apiVersion: v1 kind: Service metadata: name: jaeger spec: selector: app: jaeger ports: - name: ui port: 16686 targetPort: 16686 - name: otlp-grpc port: 4317 targetPort: 4317 - name: otlp-http port: 4318 targetPort: 4318文末三件套1. 【源码获取】关注此系列获取后续更新后台回复‘jaeger’获取完整源码和配置文件。2. 【思考题】你们的链路追踪采样率是多少是固定采样还是自适应采样欢迎在评论区分享你的实践经验3. 【系列预告】云原生技术栈系列持续更新中高可用架构设计→ 多活、熔断、限流实战服务网格Istio→ 零信任网络与流量治理混合云部署→ 跨云容灾与数据同步边缘计算→ K3s与边缘节点管理总结要点内容核心概念Trace调用链, Span单次调用, Baggage上下文传递架构Agent → Collector → Storage → Query → UI存储选型大规模选Cassandra中小规模选ES测试用Badger采集方案OpenTelemetry统一SDK支持多语言自动埋点采样策略头部采样简单尾部采样精准自适应采样平衡性能指标10万 Span/秒/Collector存储压缩1:10查询100ms链路追踪不是银弹但没有链路追踪的微服务就是盲人摸象。Jaeger作为CNCF毕业项目已经证明了它在生产环境的可靠性。希望本文能帮你快速上手让性能瓶颈无处遁形标签Jaeger链路追踪OpenTelemetry分布式追踪可观测性微服务监控