【K8S 运维实战】15-链路追踪Jaeger与OTel

链路追踪:Jaeger 与 OpenTelemetry 实践

一句话定位:跨服务调用怎么追?慢在哪一段?Jaeger + OpenTelemetry 帮你把分布式调用链路画成一张"地铁图"。

写在前面

去年秋天我们有个诡异的故障:订单服务的 P99 延迟偶尔飙升到 3 秒,但 CPU/内存/网络指标一切正常,ALM 没有一条告警触发。当时查了一整天,最后靠 tcpdump 抓包才发现------调用下游支付服务的某个 API 间歇性超时。

那次之后我痛定思痛:没有链路追踪,微服务排查问题基本靠猜。这篇文章把 Jaeger 和 OpenTelemetry 的部署、采样策略、链路日志关联讲透。

读完你能带走:

  • 分布式追踪的核心概念(Trace / Span / Context Propagation)
  • OpenTelemetry Operator 自动注入部署方案
  • Jaeger / Tempo 后端选型决策
  • 采样策略配置(头部采样 vs 尾部采样)
  • 链路与日志、指标关联的实操方法

核心问题

跨服务调用怎么追?慢在哪一段?答案是链路追踪------在每个服务边界插入"埋点",把一次完整的请求链路串起来。


一、原理剖析

1.1 Trace / Span / Context Propagation

分布式追踪的三个核心概念:

复制代码
Trace(追踪链路)
│
├── Span A (根 Span) --- 用户请求入口,API Gateway
│   ├── 开始: 10:00:00.000
│   ├── 结束: 10:00:03.500
│   ├── TraceID: abc123
│   ├── SpanID: span-a
│   └── ParentSpanID: (none)
│
├── Span B --- 调用订单服务
│   ├── 开始: 10:00:00.050
│   ├── 结束: 10:00:02.800
│   ├── TraceID: abc123        ← 同一个 TraceID 关联所有 Span
│   ├── SpanID: span-b
│   └── ParentSpanID: span-a   ← 父子关系链
│
└── Span C --- 调用支付服务(订单服务内部调用)
    ├── 开始: 10:00:01.200
    ├── 结束: 10:00:02.500      ← 可以看到这个 Span 占了 1.3s!
    ├── TraceID: abc123
    ├── SpanID: span-c
    └── ParentSpanID: span-b

从 Trace 视图可以直接看到:
  总耗时 3.5s → 订单服务 2.75s → 支付服务 1.3s → 慢在支付服务

Context Propagation(上下文传播)是怎么把 Span 串起来的:

复制代码
        服务 A                          服务 B
   ┌─────────────────┐            ┌─────────────────┐
   │ 创建 Span B      │            │ 创建 Span C      │
   │ trace_id: abc123 │            │ trace_id: abc123 │
   │ span_id: span-b  │            │ span_id: span-c  │
   │                  │            │ parent_id: span-b│
   └────────┬─────────┘            └────────▲─────────┘
            │                               │
            │  HTTP Request                  │
            │  Headers:                      │
            │    traceparent: 00-abc123-     │
            │      span-b-01                 │
            │    tracestate: vendor=xxx      │
            └───────────────────────────────┘

W3C Trace Context 标准 HTTP Header:
  traceparent: 00-{trace_id}-{span_id}-{trace_flags}
                │   │           │         │
                │   └─ 32hex    │         └─ 采样标志 (01=采样)
                │               └─ 16hex parent span id
                └─ 版本号(00)

1.2 OpenTelemetry 架构

OpenTelemetry(简称 OTel)是 CNCF 继 Kubernetes、Prometheus 之后的第三个"毕业"项目,定位为可观测性数据的统一标准

复制代码
┌─────────────────────────────────────────────────────────┐
│                  OpenTelemetry 架构                       │
├─────────────────────────────────────────────────────────┤
│                                                          │
│   应用代码                                                │
│   ┌─────────────────────────────────────────────┐       │
│   │  OTel SDK (各种语言 SDK)                      │       │
│   │  ├─ TracerProvider  ── 生成 Span              │       │
│   │  ├─ MeterProvider   ── 生成 Metric            │       │
│   │  └─ LoggerProvider  ── 生成 Log               │       │
│   └─────────────────────┬───────────────────────┘       │
│                         │                                │
│              ┌──────────┼──────────┐                     │
│              ▼          ▼          ▼                     │
│        ┌─────────┐ ┌─────────┐ ┌─────────┐              │
│        │  OTLP   │ │  OTLP   │ │  OTLP   │  (统一协议)    │
│        │ Traces  │ │ Metrics │ │  Logs   │              │
│        └────┬────┘ └────┬────┘ └────┬────┘              │
│             │           │           │                    │
│             ▼           ▼           ▼                    │
│        ┌─────────────────────────────────────┐          │
│        │       OTel Collector                 │          │
│        │  ├─ Receiver (接收)                   │          │
│        │  ├─ Processor (处理/过滤/采样)          │          │
│        │  └─ Exporter (导出到后端)              │          │
│        └──┬──────────┬──────────┬───────────┘          │
│           │          │          │                       │
│           ▼          ▼          ▼                       │
│      ┌───────┐ ┌────────┐ ┌─────────┐                  │
│      │Jaeger │ │ Tempo  │ │Prometheus│  (后端存储)       │
│      └───────┘ └────────┘ └─────────┘                  │
│                                                          │
└─────────────────────────────────────────────────────────┘

为什么选 OTel 而不是直接上 Jaeger SDK?

维度 Jaeger SDK OpenTelemetry SDK
标准性 Jaeger 私有协议 OTLP 开放标准,CNCF 毕业项目
后端灵活性 锁死 Jaeger 换后端只需改 Exporter 配置
功能覆盖 仅 Tracing Tracing + Metrics + Logging
社区 维护中但不再活跃开发 最活跃的可观测性项目
厂商中立 需要 Jaeger 后端 任何支持 OTLP 的后端

结论:新项目无条件直接上 OpenTelemetry。老项目从 Jaeger SDK 迁移到 OTel SDK。

1.3 采样策略深度对比

采样是链路追踪中最重要的配置决策,直接影响存储成本和排查能力。

复制代码
采样策略对比:

┌──────────────┬──────────────────┬──────────────────┬──────────────────┐
│    策略      │     头部采样      │    尾部采样       │   自适应采样      │
│              │  (Head Sampling) │ (Tail Sampling)  │(Adaptive Sampling)│
├──────────────┼──────────────────┼──────────────────┼──────────────────┤
│ 决策时机      │ Span 创建时       │ Trace 完成后      │ Span 创建时       │
│ 原理         │ 随机决定是否采样   │ 全量采集后再筛选   │ 根据速率动态调整   │
│ 优点         │ 简单,开销低      │ 不会漏掉异常 Trace │ 平衡存储和覆盖    │
│ 缺点         │ 可能漏掉错误 Trace │ 需要全量采集+存储   │ 实现复杂          │
│ 存储成本      │ 低(1%-10%)     │ 高(100%采集)    │ 中(可配置)      │
│ 错误 Trace   │ 可能丢失          │ ✅ 不会丢失        │ ✅ 大概率保留     │
│ 适用场景      │ 高流量服务        │ 低流量核心服务     │ 中大规模服务      │
│ 代表实现      │ OTel SDK 内置    │ Jaeger Remote     │ Jaeger Adaptive  │
│              │                  │ Sampling          │                  │
└──────────────┴──────────────────┴──────────────────┴──────────────────┘

生产建议:

  • 高流量服务(> 1000 QPS):头部采样 1%-5%
  • 核心业务服务:尾部采样 + 错误 Trace 100% 保留
  • 低流量服务(< 10 QPS):100% 采集(成本可控)

二、实战操作

2.1 部署 OpenTelemetry Operator

bash 复制代码
# 添加 OTel Helm 仓库
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

# 创建命名空间
kubectl create namespace otel

# 安装 OTel Operator(负责自动注入 SDK)
helm upgrade --install otel-operator open-telemetry/opentelemetry-operator \
  --namespace otel \
  --set "manager.collectorImage.repository=otel/opentelemetry-collector-k8s" \
  --set admissionWebhooks.certManager.enabled=true \
  --wait

2.2 部署 OTel Collector 和 Jaeger 后端

otel-collector-values.yaml

yaml 复制代码
# ============================================
# OpenTelemetry Collector + Jaeger 部署配置
# ============================================

mode: deployment

# ── 副本数 ──
replicaCount: 2

image:
  repository: otel/opentelemetry-collector-contrib
  tag: 0.97.0

# ── Collector 配置 ──
config:
  receivers:
    # OTLP 协议接收(gRPC + HTTP)
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318

    # Jaeger 协议兼容(旧版 SDK 兼容)
    jaeger:
      protocols:
        grpc:
          endpoint: 0.0.0.0:14250
        thrift_http:
          endpoint: 0.0.0.0:14268

    # Zipkin 协议兼容
    zipkin:
      endpoint: 0.0.0.0:9411

  processors:
    # 批次处理:平衡延迟和吞吐
    batch:
      send_batch_size: 1024
      timeout: 5s
      send_batch_max_size: 2048

    # 内存限制:防止 OOM
    memory_limiter:
      check_interval: 1s
      limit_mib: 1024
      spike_limit_mib: 256

    # ── 尾部采样配置(关键!) ──
    tail_sampling:
      decision_wait: 30s      # 等待 30 秒完成 Trace 再做采样决策
      num_traces: 50000       # 内存中保留 50000 条 Trace
      expected_new_traces_per_sec: 100
      policies:
        # 策略 1: 包含错误的 Trace 100% 采样
        - name: errors-policy
          type: status_code
          status_code:
            status_codes: [ERROR]

        # 策略 2: 延迟 > 1s 的 Trace 100% 采样
        - name: latency-policy
          type: latency
          latency:
            threshold_ms: 1000

        # 策略 3: 特定服务 100% 采样
        - name: core-services-policy
          type: string_attribute
          string_attribute:
            key: service.name
            values: [payment-service, order-service, user-service]
            enabled_regex_matching: false

        # 策略 4: 其他 Trace 采样 5%
        - name: probabilistic-policy
          type: probabilistic
          probabilistic:
            sampling_percentage: 5

    # 属性处理
    transform:
      trace_statements:
        # 为内部 Span 添加 cluster 信息
        - context: resource
          statements:
            - set(attributes["k8s.cluster.name"], "prod-k8s-1")

    # K8s 属性丰富
    k8sattributes:
      auth_type: serviceAccount
      passthrough: false
      extract:
        metadata:
          - k8s.pod.name
          - k8s.pod.uid
          - k8s.deployment.name
          - k8s.namespace.name
          - k8s.node.name
      pod_association:
        - sources:
            - from: resource_attribute
              name: k8s.pod.ip

  exporters:
    # 导出到 Jaeger
    jaeger:
      endpoint: jaeger-collector.jaeger.svc.cluster.local:14250
      tls:
        insecure: true

    # 调试导出(开发环境,生产关闭)
    # debug:
    #   verbosity: detailed

  service:
    pipelines:
      traces:
        receivers: [otlp, jaeger, zipkin]
        processors: [memory_limiter, k8sattributes, tail_sampling, batch, transform]
        exporters: [jaeger]

  # ── 端口 ──
  ports:
    otlp:
      enabled: true
      containerPort: 4317
      servicePort: 4317
      protocol: TCP
    otlp-http:
      enabled: true
      containerPort: 4318
      servicePort: 4318
      protocol: TCP

# ── 资源配额 ──
resources:
  limits:
    cpu: 1000m
    memory: 2Gi
  requests:
    cpu: 200m
    memory: 512Mi

# ── 容忍度 ──
tolerations:
  - key: node-role.kubernetes.io/control-plane
    operator: Exists
    effect: NoSchedule

部署 OTel Collector:

bash 复制代码
helm upgrade --install otel-collector open-telemetry/opentelemetry-collector \
  --namespace otel \
  --values otel-collector-values.yaml \
  --wait

Jaeger 后端部署:

bash 复制代码
# 使用 Jaeger Operator 或直接 Helm 部署
kubectl create namespace jaeger

# 安装 Jaeger (All-In-One 模式,生产用 Production 模式)
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update

cat <<'EOF' > jaeger-values.yaml
provisionDataStore:
  cassandra: false
  elasticsearch: false

query:
  enabled: true
  service:
    type: ClusterIP
    port: 16686

collector:
  enabled: true
  service:
    otlp:
      grpc:
        port: 4317
      http:
        port: 4318
    zipkin:
      port: 9411

# 生产建议使用 Elasticsearch 做后端存储
storage:
  type: elasticsearch
  elasticsearch:
    host: elasticsearch-master.logging.svc
    port: 9200

# 保留策略
esIndexCleaner:
  enabled: true
  numberOfDays: 7
  schedule: "55 23 * * *"
EOF

helm upgrade --install jaeger jaegertracing/jaeger \
  --namespace jaeger \
  --values jaeger-values.yaml \
  --wait

2.3 应用侧接入 OTel SDK

Python 示例(FastAPI + OTel SDK)

python 复制代码
# app.py - FastAPI 应用接入 OpenTelemetry
from opentelemetry import trace
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.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
from opentelemetry.propagate import set_global_textmap
from opentelemetry.propagators.composite import CompositePropagator
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
from opentelemetry.baggage.propagation import W3CBaggagePropagator
from fastapi import FastAPI, Request
import logging
import uuid

# ── 配置 Tracer ──
resource = Resource.create({
    "service.name": "order-service",
    "service.version": "v2.1.3",
    "deployment.environment": "production",
})

# 头部采样: 10% 采样率
sampler = TraceIdRatioBased(1/10)

provider = TracerProvider(resource=resource, sampler=sampler)

# OTLP Exporter → Collector → Jaeger
otlp_exporter = OTLPSpanExporter(
    endpoint="http://otel-collector.otel.svc.cluster.local:4317",
    insecure=True,
)

provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
trace.set_tracer_provider(provider)

# W3C Trace Context 传播
set_global_textmap(CompositePropagator([
    TraceContextTextMapPropagator(),
    W3CBaggagePropagator(),
]))

# ── FastAPI 自动埋点 ──
app = FastAPI()

# 自动为所有 HTTP 端点创建 Span
FastAPIInstrumentor.instrument_app(app)
# 自动为 requests 库调用创建 Span
RequestsInstrumentor().instrument()

tracer = trace.get_tracer(__name__)

@app.get("/api/orders/{order_id}")
async def get_order(order_id: str, request: Request):
    # 获取当前 Span(自动创建,trace_id 从 Header 传播而来)
    current_span = trace.get_current_span()

    # 添加业务属性
    current_span.set_attribute("order.id", order_id)
    current_span.set_attribute("user.id", request.headers.get("X-User-ID", "unknown"))

    # 关键: 将 trace_id 写入日志,实现关联
    span_context = current_span.get_span_context()
    trace_id_hex = format(span_context.trace_id, '032x')

    # 手动创建子 Span
    with tracer.start_as_current_span("query-database") as db_span:
        db_span.set_attribute("db.statement", f"SELECT * FROM orders WHERE id={order_id}")
        db_span.set_attribute("db.system", "postgresql")
        # ... 数据库查询逻辑

    return {"order_id": order_id, "trace_id": trace_id_hex}

Java 示例(Spring Boot + OTel Agent,零代码侵入)

bash 复制代码
# 方式: Java Agent 自动注入,不需要改一行代码!

# Dockerfile
FROM openjdk:17-slim

# 下载 OTel Java Agent
ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.33.0/opentelemetry-javaagent.jar /opt/opentelemetry-javaagent.jar

COPY target/order-service.jar /app/order-service.jar

ENTRYPOINT ["java", \
  "-javaagent:/opt/opentelemetry-javaagent.jar", \
  "-Dotel.service.name=order-service", \
  "-Dotel.traces.exporter=otlp", \
  "-Dotel.metrics.exporter=none", \
  "-Dotel.logs.exporter=none", \
  "-Dotel.exporter.otlp.endpoint=http://otel-collector.otel.svc:4317", \
  "-Dotel.propagators=tracecontext,baggage", \
  "-jar", "/app/order-service.jar"]

2.4 自动注入(Instrumentation CRD)

OTel Operator 支持通过注解零代码注入:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  annotations:
    # 这是关键!告诉 OTel Operator 自动注入 SDK
    instrumentation.opentelemetry.io/inject-java: "true"
    instrumentation.opentelemetry.io/java-container-names: "order-service"
    # 或者注入 Python SDK
    # instrumentation.opentelemetry.io/inject-python: "true"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
      annotations:
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
        - name: order-service
          image: order-service:v2.1.3
          env:
            - name: OTEL_SERVICE_NAME
              value: order-service
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://otel-collector.otel.svc.cluster.local:4317
            - name: OTEL_TRACES_SAMPLER
              value: parentbased_traceidratio
            - name: OTEL_TRACES_SAMPLER_ARG
              value: "0.1"

Instrumentation CRD 定义:

yaml 复制代码
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: java-instrumentation
  namespace: otel
spec:
  exporter:
    endpoint: http://otel-collector.otel.svc.cluster.local:4317
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "0.1"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:1.33.0
  env:
    - name: OTEL_RESOURCE_ATTRIBUTES
      value: cluster=prod-k8s-1

三、踩坑与排查

3.1 坑一:Context 传播断裂

现象:Jaeger UI 中看到多个孤立的 Span,无法组成完整的 Trace。服务 A 调服务 B,Trace 断开。

原因

  • HTTP 调用时没有传播 traceparent header
  • 消息队列(Kafka/RabbitMQ)中没有在消息体中携带 trace context
  • 不同语言/框架的传播器不一致

排查

bash 复制代码
# 1. 抓包检查 HTTP Header
kubectl exec -n production order-service-xxx -- \
  tcpdump -A -s 0 -i eth0 port 8080 | grep -i traceparent

# 2. 检查 OTel Collector 日志
kubectl logs -n otel deployment/otel-collector | grep -i "traceid"

# 3. 用 Jaeger UI 验证
# 访问: http://jaeger-query.jaeger:16686
# 搜索特定 TraceID,看 Span 数量是否 = 期望的调用次数

# 4. 检查应用日志中的 trace_id
kubectl logs order-service-xxx | grep "trace_id" | head -20

解决方案

python 复制代码
# 确保使用正确的传播器
from opentelemetry.propagate import set_global_textmap
from opentelemetry.propagators.composite import CompositePropagator
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

# 只用 W3C TraceContext(标准),避免混合使用
set_global_textmap(TraceContextTextMapPropagator())

检查服务间调用是否传播了 header:

python 复制代码
# 确保 HTTP 客户端使用了 OTel 自动埋点
import requests
from opentelemetry.instrumentation.requests import RequestsInstrumentor

RequestsInstrumentor().instrument()  # 一行搞定

3.2 坑二:采样策略导致关键 Trace 丢失

现象:生产出问题后,在 Jaeger 中怎么都找不到出错的那条 Trace。

原因:头部采样率设得太低(如 1%),导致错误 Trace 被丢弃。

解决方案头部采样 + 尾部采样 双保险

yaml 复制代码
# OTel Collector 配置 - 双保险采样
processors:
  tail_sampling:
    decision_wait: 30s
    policies:
      # 第一层: 错误 Trace 100% 保留(尾部采样核心价值)
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      # 第二层: 慢 Trace 100% 保留
      - name: slow-traces
        type: latency
        latency:
          threshold_ms: 2000
      # 第三层: 其余按比例采样
      - name: probabilistic
        type: probabilistic
        probabilistic:
          sampling_percentage: 5

同时在 SDK 层设置较高基础采样率作为兜底:

bash 复制代码
# 应用层头部采样保留 20%(保证有基础数据)
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.2

3.3 坑三:Jaeger 存储爆炸

现象:Jaeger 后端存储(ES/Cassandra)磁盘使用量每天增长 50GB+,7 天就接近 400GB。

原因

  • 采样率配置不合理(100% 采样高流量服务)
  • 没有配置数据保留策略
  • ES 索引没有按天轮转

解决方案

yaml 复制代码
# Jaeger - 生产存储配置
storage:
  type: elasticsearch
  elasticsearch:
    host: elasticsearch-master
    port: 9200
    # 关键: 按天创建索引
    indexPrefix: jaeger
    useILM: true          # ES Index Lifecycle Management
    # ILM 策略: 3 天 hot, 7 天 warm, 30 天后删除
    createIndexTemplates: true
    ilmPolicyName: jaeger-ilm-policy

# 定时清理
esIndexCleaner:
  enabled: true
  numberOfDays: 7
  schedule: "55 23 * * *"  # 每天 23:55 清理

ES ILM 策略:

json 复制代码
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50gb",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "shrink": {
            "number_of_shards": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          }
        }
      },
      "delete": {
        "min_age": "7d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

四、最佳实践

部署清单

  • 使用 OTel Operator 管理自动注入,不要手动配置每个应用
  • 部署 OTel Collector 作为中间网关(解耦 SDK 和后端)
  • 生产环境 Collector 部署模式用 Deployment + HPA(非 DaemonSet)
  • 配置 尾部采样 + 头部采样双保险
  • Jaeger 后端存储建议 Elasticsearch 7.x+ (生产)或 Badger(开发/测试)

采样策略清单

  • 高流量服务:头部 5%-20% + 尾部错误/慢 Trace 100% 保留
  • 低流量核心服务:100% 采集
  • 内部/后台服务:头部 1%-5%
  • 确保 Error Trace 和 P99+ 慢 Trace 永远不被丢弃

链路关联清单

  • 应用日志中必须打印 trace_id(关键!关联 LogQL 和 Jaeger 查询)
  • Prometheus 指标中添加 trace_id 作为 exemplar:
promql 复制代码
# Prometheus exemplar(指标中内嵌 trace 示例)
histogram_quantile(0.99,
  rate(http_request_duration_seconds_bucket[5m])
)
# Grafana 中点击数据点可直接跳转到 Jaeger Trace
  • 统一服务命名规范:{team}-{service}-{env},如 payment-order-service-prod

后端选型决策

场景 推荐方案 理由
K8s <= 10 节点 Jaeger All-in-One 简单,内置内存/Badger 存储
K8s 10-50 节点 Jaeger + Elasticsearch 成熟的 ES 后端,查询支持好
K8s > 50 节点 Tempo(Grafana 生态) 对象存储原生,成本低,与 Grafana 无缝集成
已有 Grafana + Loki Tempo 一套 Grafana 搞定日志+指标+链路
高吞吐 + 低成本 Grafana Tempo 不需要索引,对象存储 + 轮询查询

五、小结

链路追踪是一个"慢工出细活"的工程。我的实施建议:

  1. 第一步不要追求全量覆盖:先给 2-3 个核心服务接入 OTel SDK,打通 Trace → Jaeger → Grafana 查询链路
  2. 采样策略是灵魂:头部 + 尾部双保险,确保出错时一定能找到 Trace
  3. trace_id 是一等公民:日志里带 trace_id,指标里带 exemplar,排查问题时一个 trace_id 串起三根支柱

分布式系统排查慢请求的四步法:

复制代码
① 看 Grafana 面板 → 哪个服务 P99 高?
② 点 exemplar 跳 Jaeger → 哪段 Span 慢?
③ 用 trace_id 查 Loki → 这段时间发生了什么?
④ 看 K8s Event → 有 Pod 重启/调度/扩缩容吗?

思考题

  1. 为什么尾部采样需要等待 30 秒才能做决策?这个等待时间怎么权衡?
  2. 如果服务间通过 Kafka 异步通信,如何保证 Consumer 能够继承 Producer 的 TraceID?
  3. Grafana Tempo 为什么能做到不需要建立索引也能快速查询 Trace?和 Loki 的"不索引内容只索引标签"有什么异同?

延伸阅读

相关推荐
霁月的小屋2 小时前
Docker 工程化实践(二):理解镜像分层与容器文件系统
运维·docker·容器
Android系统攻城狮2 小时前
Linux PipeWire深度解析之pw_context_add_spa_lib调用流程与实战(二十九)
linux·运维·服务器·音频进阶·pipewire音频进阶
Dovis(誓平步青云)3 小时前
《如何在CentOS 7中添加Plex官方软件源:解决文件磁盘难管理难题》
linux·运维·服务器·后端·生成对抗网络·centos
草莓熊Lotso3 小时前
【Linux网络】深入理解Linux IO多路复用:select服务器完善、内核原理与poll实战
linux·运维·服务器·c语言·网络·c++
深念Y3 小时前
MSYS2 是什么,要不要装?
运维·环境·开发·工具·msys2
网安老伯3 小时前
网络安全基础要点知识介绍(非常详细),零基础入门到精通,看这一篇就够了
运维·前端·网络协议·web安全·网络安全·职场和发展
迷茫中的自我3 小时前
GitHub Actions自动化运维实战:从CI/CD到云原生部署
运维·自动化·github
XH华3 小时前
Linux系统第二章:常见的Linux指令(上)
linux·运维·服务器
nVisual3 小时前
01-环境监控集成方案
运维·服务器·开发语言·网络·数据库·数据中心布线·综合布线管理软件