一、容器化环境里追踪为什么这么难
想象一下,你在一个微服务架构里开发应用,一个用户请求从网关进来,可能要经过十几个服务才能最终返回结果。在传统部署模式下,你还能靠日志串联一下请求路径,但在 Kubernetes 这种容器编排环境里,情况就完全不一样了。Pod 随时可能被重启、销毁、重新调度到不同的节点上,日志分散在各个容器里,想要搞清楚一个请求到底经过了哪些服务、每个服务花了多少时间、哪一步出了问题,简直是头大。
分布式追踪就是为了解决这个问题而生的技术。它给每一个请求打上一个唯一的追踪ID,然后跟着这个请求一路走,把经过的每一个服务、每一个操作都记录下来,形成一个完整的链路。Jaeger 是目前最流行的开源分布式追踪系统之一,它基于 OpenTracing 标准,采集、处理和可视化追踪数据的能力都非常强。
但把 Jaeger 真正部署到 Kubernetes 里,并且让你的应用能正常上报追踪数据,中间的坑可不少。很多同学直接照着官方文档抄了一遍配置,结果发现 Jaeger 的界面打开是空的,一条链路都看不到。这通常不是 Jaeger 本身的问题,而是配置层面的细节没有处理好。
下面这套内容,我会从零开始,手把手教你怎么在 Kubernetes 里把 Jaeger 跑起来,再把你的 Java 应用接进去,让整个链路追踪真正落地。所有示例都使用 Java (Spring Boot) + Kubernetes 这个技术栈,配置项我会逐行解释,保证你看完就能上手。
二、整体架构和核心组件
在动手之前,先搞清楚 Jaeger 在 Kubernetes 里到底是怎么工作的,这决定了你后面配置的思路。
2.1 Jaeger 的核心组件
Jaeger 系统主要由四个部分组成:
Collector(收集器):它是追踪数据的入口。你的应用把追踪数据通过网络发给 Collector,Collector 负责接收数据、处理数据、然后把数据写到后端存储里。
Query(查询服务):这是用户看到的那个 Web 界面背后的服务。你在浏览器里打开 Jaeger 界面,输入查询条件,Query 服务会从存储里把追踪数据捞出来返回给你。
Storage(存储后端):用于持久化存储追踪数据。常见选择有 Elasticsearch、Cassandra、Badger(Jaeger 自带的轻量级存储)等。生产环境通常用 Elasticsearch,测试环境可以用 Badger。
Agent(代理):部署在每个节点上,本地收集追踪数据然后批量转发给 Collector。不过在全容器化环境下,很多团队选择跳过 Agent,让应用直接发数据给 Collector。
2.2 Kubernetes 中的部署拓扑
在 Kubernetes 集群里,典型的 Jaeger 部署方式如下:
- Jaeger All-in-One 或分拆部署为 Deployment
- Collector 暴露为 ClusterIP 类型的 Service,供集群内应用访问
- Query 暴露为 ClusterIP 或 Ingress 类型,供用户浏览器访问
- 存储后端通过 StatefulSet 部署,挂载持久卷保证数据不丢失
- 可选的 Agent 以 DaemonSet 方式部署在每个节点上
对于学习和中小规模场景,Jaeger 提供了一个 All-in-One 镜像,把所有组件打包在一起,非常适合快速上手。生产环境则建议使用官方的 Helm Chart 做分拆部署。
三、从零开始部署 Jaeger
3.1 使用 Helm Chart 部署生产级 Jaeger
Helm 是 Kubernetes 的包管理工具,用 Helm 部署 Jaeger 是最推荐的方式,因为 Helm Chart 已经帮你处理好了大部分细节配置。
首先安装 Jaeger 官方 Helm Chart 的依赖工具:
# 添加 Jaeger 的 Helm 仓库
helm repo add jaeager https://jaegertracing.github.io/helm-charts
# 更新仓库索引
helm repo update
# 查看可用版本
helm search repo jaeger/jaeger --versions
然后执行安装命令,这里我选择带 Elasticsearch 存储的方案:
# 创建用于安装 Jaeger 的命名空间
kubectl create namespace jaeger
# 安装 Jaeger(带 Elasticsearch 存储后端)
helm install jaeger jaeger/jaeger \
--namespace jaeger \
--set "jaeger.collector.enabled=true" \
--set "jaeger.query.enabled=true" \
--set "elasticsearch.master.enabled=true" \
--set "elasticsearch.data.enabled=true" \
--set "jaegerQuery.ingress.enabled=true" \
--set "jaegerQuery.ingress.hosts[0]=jaeger.example.com" \
--set "jaegerCollector.ingress.enabled=true" \
--set "jaegerCollector.ingress.hosts[0]=collector.example.com"
3.2 验证部署结果
部署完成后,检查所有 Pod 是否正常运行:
# 查看 jaeger 命名空间下的所有 Pod
kubectl -n jaeger get pods
# 查看 Service 的暴露情况
kubectl -n jaeger get svc
# 确认 Ingress 是否创建成功
kubectl -n jaeger get ingress
正常情况下,你应该能看到以下 Pod 都在 Running 状态:
elasticsearch-master-*:Elasticsearch 主节点elasticsearch-data-*:Elasticsearch 数据节点jaeger-collector-*:收集器jaeger-query-*:查询服务
如果一切正常,打开你配置的 Ingress 域名(比如 jaeger.example.com),就能看到 Jaeger 的 Web 界面了。
3.3 快速测试:All-in-One 模式
如果你只是想快速验证追踪功能是否生效,不想折腾 Elasticsearch 这些重资源组件,可以用 All-in-One 模式:
# 创建一个 All-in-One 模式的 Deployment
cat <<EOF | kubectl -n jaeger apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger-allinone
namespace: jaeger
labels:
app: jaeger-allinone
spec:
replicas: 1
selector:
matchLabels:
app: jaeger-allinone
template:
metadata:
labels:
app: jaeger-allinone
spec:
containers:
- name: jaeger
image: jaegertracing/all-in-one:1.52.0
ports:
- containerPort: 16686 # 查询界面端口
name: query
- containerPort: 14250 # gRPC collector 端口
name: grpc-collector
- containerPort: 14268 # HTTP collector 端口
name: http-collector
- containerPort: 5775 # UDP 接收 Zipkin 数据
name: zipkin-udp
- containerPort: 5778 # UDP 接收 OpenTracing 数据
name: otel-udp
env:
- name: COLLECTOR_ZIPKIN_HTTP_PORT
value: "9411"
- name: SPAN_STORAGE_TYPE
value: "badger" # 使用内置 Badger 存储
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 16686
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /
port: 16686
initialDelaySeconds: 5
periodSeconds: 10
---
# 创建 Service 暴露 Jaeger 的查询界面
apiVersion: v1
kind: Service
metadata:
name: jaeger-query
namespace: jaeger
spec:
selector:
app: jaeger-allinone
ports:
- port: 16686
targetPort: 16686
name: query
- port: 14268
targetPort: 14268
name: http-collector
- port: 14250
targetPort: 14250
name: grpc-collector
type: ClusterIP
EOF
3.4 通过端口转发访问界面
在没有 Ingress 的配置环境(比如本地 K3s 或 Minikube)下,可以用端口转发访问 Jaeger 查询界面:
# 转发查询端口到本地 16686
kubectl -n jaeger port-forward svc/jaeger-query 16686:16686
# 转发收集器端口到本地 14268
kubectl -n jaeger port-forward svc/jaeger-query 14268:14268
然后浏览器访问 http://localhost:16686 就能看到 Jaeger 界面。
四、Java 应用接入 Jaeger 的完整配置
4.1 Spring Boot 应用添加依赖
技术栈:Java 17 + Spring Boot 3.x + Kubernetes
在你的 pom.xml 中添加 OpenTelemetry 和 Jaeger 相关的依赖:
<!-- 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger -->
<!-- Spring Boot 已原生集成 OpenTelemetry,这是目前推荐的方式 -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- OpenTelemetry API -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.35.0</version>
</dependency>
<!-- OpenTelemetry SDK -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.35.0</version>
</dependency>
<!-- Jaeger Remote Reporter(直连 Jaeger Collector) -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>1.35.0</version>
</dependency>
<!-- Spring Boot Actuator(健康检查和监控端点) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 服务发现(假设使用 Spring Cloud) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
4.2 配置追踪上报地址
在 application.yml 中配置追踪相关的参数:
# 应用名称(Jaeger 中会以这个名称显示服务)
spring:
application:
name: order-service
server:
port: 8080
# OpenTelemetry 配置
management:
tracing:
# 采样策略:始终采样(生产环境建议改为概率采样)
sampling:
probability: 1.0
# OpenTelemetry 导出配置
otlp:
# Jaeger Collector 的 gRPC 地址
# 在 Kubernetes 集群内,使用 Service 名称即可
endpoint: http://jaeger-collector.jaeger.svc.cluster.local:4317
# 导出协议
protocol: grpc
# Jaeger 特定的配置(如果使用 Jaeger Agent)
# 下面这组配置说明如何让应用通过 Jaeger Agent 上报数据
jaeger:
# Agent 地址(如果启用了 Agent)
agent:
# 本地节点上 Jaeger Agent 的 UDP 端口
# 在 DaemonSet 模式下,Agent 监听在本机的这些端口
host: jaeger-agent.jaeger.svc.cluster.local
collector:
enabled: true
udp:
host: jaeger-agent.jaeger.svc.cluster.local
port: 6831
4.3 自定义 Trace 代码示例
在实际业务中,你可能需要手动创建 Span 来追踪特定操作的耗时:
/**
* 订单服务 - 展示如何在 Spring Boot 中手动创建分布式追踪 Span
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
package com.example.orderservice.service;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.config.Sampler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class OrderTraceService {
private final Tracer tracer;
// 从配置中读取 Jaeger Collector 地址
@Value("${management.tracing.otlp.endpoint}")
private String collectorEndpoint;
// Spring 自动注入 OpenTelemetry 实例
public OrderTraceService(OpenTelemetry openTelemetry) {
// 创建 Tracer,"订单服务" 是追踪标识
this.tracer = openTelemetry.getTracer("OrderService", "1.0.0");
}
/**
* 创建一个简单的 Span 用于追踪
* 方法调用开始时创建 Span,方法结束时关闭
*/
public void traceSimpleOperation() {
// 创建一个新的 Span
Span span = tracer.spanBuilder("创建订单")
.setSpanKind(SpanKind.INTERNAL)
.setAttribute(AttributeKey.stringKey("order.type"), "standard")
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 模拟业务操作
System.out.println("正在处理订单...");
Thread.sleep(100); // 模拟处理耗时
// 标记操作成功
span.setStatus(StatusCode.OK);
} catch (Exception e) {
// 标记操作失败,并记录错误信息
span.setStatus(StatusCode.ERROR, "处理失败");
span.recordException(e);
throw new RuntimeException("订单创建失败", e);
} finally {
// 关闭 Span,上报追踪数据
span.end();
}
}
/**
* 嵌套 Span 示例:展示如何追踪调用链
* 父 Span 代表一个上层操作,子 Span 代表下层调用
*/
public void traceNestedOperations() {
// 父 Span:整个订单处理流程
Span parentSpan = tracer.spanBuilder("订单处理流程")
.setSpanKind(SpanKind.INTERNAL)
.setAttribute(AttributeKey.longKey("order.id"), 12345L)
.startSpan();
try (Scope parentScope = parentSpan.makeCurrent()) {
// 子 Span 1:验证订单
Span validateSpan = tracer.spanBuilder("订单验证")
.setSpanKind(SpanKind.INTERNAL)
.setAttribute(AttributeKey.stringKey("step", "validate"))
.startSpan();
validateSpan.setStatus(StatusCode.OK);
validateSpan.end(); // 子 Span 独立关闭
// 子 Span 2:扣减库存(模拟外部调用)
Span inventorySpan = tracer.spanBuilder("库存扣减")
.setSpanKind(SpanKind.CLIENT) // 标记为客户端调用
.setAttribute(AttributeKey.stringKey("remote.service"), "inventory-service")
.startSpan();
try {
// 模拟远程调用耗时
Thread.sleep(200);
inventorySpan.setStatus(StatusCode.OK);
} catch (Exception e) {
inventorySpan.setStatus(StatusCode.ERROR);
inventorySpan.recordException(e);
} finally {
inventorySpan.end();
}
// 子 Span 3:支付处理
Span paymentSpan = tracer.spanBuilder("支付处理")
.setSpanKind(SpanKind.CLIENT)
.setAttribute(AttributeKey.stringKey("payment.method"), "alipay")
.setAttribute(AttributeKey.doubleKey("payment.amount"), 299.99)
.startSpan();
paymentSpan.setStatus(StatusCode.OK);
paymentSpan.end();
// 父 Span 标记完成
parentSpan.setStatus(StatusCode.OK);
} finally {
parentSpan.end();
}
}
/**
* 通过 OpenTelemetry 自动配置创建 Tracer 的替代方案
* 适合不想使用 Spring Boot 自动配置的情况
*/
private Tracer createTracerManually() {
// 创建 OTLP 导出器,指向 Jaeger Collector
OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder()
.setEndpoint(collectorEndpoint)
.build();
// 配置采样策略
Sampler sampler = Sampler.parentBased(
Sampler.traceIdRatioBased(0.5) // 50% 的追踪请求会被采样
);
// 构建 TracerProvider
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.setResource(
io.opentelemetry.sdk.resources.Resource.builder()
.put(AttributeKey.stringKey("service.name"), "order-service")
.put(AttributeKey.stringKey("service.version"), "1.0.0")
.build()
)
.setSampler(sampler)
.addSpanProcessor(BatchSpanProcessor.builder(exporter).build())
.build();
return tracerProvider.getTracer("manual-tracer", "1.0.0");
}
}
4.4 Spring Boot Controller 示例
/**
* 订单服务 Controller - 展示分布式追踪的完整请求链路
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
package com.example.orderservice.controller;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final Tracer tracer;
private final RestTemplate restTemplate;
public OrderController(OpenTelemetry openTelemetry, RestTemplate restTemplate) {
this.tracer = openTelemetry.getTracer("OrderController");
this.restTemplate = restTemplate;
}
/**
* 创建订单接口 - 会调用库存服务和支付服务
*/
@PostMapping("/create")
public Map<String, Object> createOrder(@RequestBody Map<String, Object> request) {
// 手动创建根 Span,代表整个创建订单的请求
Span rootSpan = tracer.spanBuilder("POST /api/orders/create")
.setSpanKind(SpanKind.SERVER)
.startSpan();
try (Scope scope = rootSpan.makeCurrent()) {
// 设置追踪属性
rootSpan.setAttribute("order.product", request.get("product"));
rootSpan.setAttribute("order.quantity", (int) request.get("quantity"));
// 调用库存服务
Object inventoryResult = restTemplate.getForObject(
"http://inventory-service/api/inventory/check",
String.class
);
// 调用支付服务
Object paymentResult = restTemplate.postForObject(
"http://payment-service/api/payments",
request,
String.class
);
rootSpan.setStatus(StatusCode.OK);
return Map.of(
"orderId", "ORD-" + System.currentTimeMillis(),
"status", "created",
"inventoryResult", inventoryResult,
"paymentResult", paymentResult
);
} catch (Exception e) {
rootSpan.setStatus(StatusCode.ERROR);
rootSpan.recordException(e);
throw new RuntimeException("创建订单失败", e);
} finally {
rootSpan.end();
}
}
/**
* 查询订单接口
*/
@GetMapping("/{orderId}")
public Map<String, Object> getOrder(@PathVariable String orderId) {
// Spring Boot Actuator 会自动为 HTTP 请求创建 Span
// 如果需要额外属性,可以获取当前 Span 并添加
Span currentSpan = Span.current();
currentSpan.setAttribute("order.id", orderId);
// 模拟查询数据库
Thread.yield();
return Map.of(
"orderId", orderId,
"status", "pending",
"product", "示例商品",
"quantity", 1
);
}
}
五、Kubernetes 配置详解
5.1 Deployment 配置
# 订单服务的 Kubernetes Deployment 配置
# 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: default
labels:
app: order-service
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
# PodDisruptionBudget 相关注解
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
spec:
containers:
- name: order-service
image: registry.example.com/order-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
# 应用配置
- name: SPRING_PROFILES_ACTIVE
value: "kubernetes"
# 应用名称(Jaeger 中显示的名称)
- name: SPRING_APPLICATION_NAME
value: "order-service"
# Jaeger Collector 地址
# 在集群内通过 Service DNS 解析
- name: JAEGER_COLLECTOR_ENDPOINT
value: "http://jaeger-collector.jaeger.svc.cluster.local:14268"
# 如果使用 Jaeger Agent 模式
- name: JAEGER_AGENT_HOST
value: "jaeger-agent.jaeger.svc.cluster.local"
# OpenTelemetry 端点
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://jaeger-collector.jaeger.svc.cluster.local:4317"
# 采样比例(生产环境建议降低)
- name: OTEL_TRACES_SAMPLER_ARG
value: "0.5"
# Java 内存配置
- name: JAVA_OPTS
value: "-Xms512m -Xmx1g -XX:+UseG1GC"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
# 健康检查探针
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# 启动探针(针对启动较慢的 Java 应用)
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30
# 可选:Sidecar 模式注入 Jaeger Agent
# 通过 DaemonSet 方式部署 Agent,这里不需要 Sidecar
# 如果使用 Sidecar,配置如下(推荐 DaemonSet 方式)
# initContainers:
# - name: jaeger-agent-init
# image: jaegertracing/agent:1.52.0
# command: ["sleep", "5"] # 等待网络就绪
5.2 Jaeger Agent 的 DaemonSet 部署
如果你希望使用 Jaeger Agent 来收集追踪数据(推荐在生产环境使用),需要以 DaemonSet 方式部署:
# Jaeger Agent 的 DaemonSet 配置
# 每个节点上都会运行一个 Agent 实例
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: jaeger-agent
namespace: jaeger
labels:
app: jaeger-agent
spec:
selector:
matchLabels:
app: jaeger-agent
template:
metadata:
labels:
app: jaeger-agent
spec:
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: jaeger-agent
image: jaegertracing/agent:1.52.0
ports:
# UDP 接收 OpenTracing 数据
- containerPort: 5775
protocol: UDP
name: otel-udp
# UDP 接收 Jaeger Thrift 数据
- containerPort: 6831
protocol: UDP
name: jaeger-thrift-udp
# UDP 接收 Jaeger Thrift 压缩数据
- containerPort: 6832
protocol: UDP
name: jaeger-thrift-compressed
# UDP 接收 Zipkin 数据
- containerPort: 9411
protocol: UDP
name: zipkin-udp
# TCP 接收 gRPC 数据
- containerPort: 5778
protocol: TCP
name: admin
env:
# Collector 地址(Agent 将数据转发到 Collector)
- name: COLLECTOR_HOSTPORT
value: "jaeger-collector.jaeger.svc.cluster.local:14268"
# 上报间隔
- name: REPORTER_BATCH_QUEUE_SIZE
value: "10000"
- name: REPORTER_BATCH_FLUSH_INTERVAL
value: "100" # 毫秒
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
tcpSocket:
port: 5775
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 5775
initialDelaySeconds: 5
periodSeconds: 10
hostNetwork: true # 使用主机网络,确保本地应用可以直接发送数据
5.3 Service 和 Ingress 配置
# Jaeger 相关的 Service 配置
apiVersion: v1
kind: Service
metadata:
name: jaeger-agent
namespace: jaeger
spec:
selector:
app: jaeger-agent
ports:
- port: 5775
protocol: UDP
name: otel-udp
targetPort: 5775
- port: 6831
protocol: UDP
name: jaeger-thrift-udp
targetPort: 6831
- port: 9411
protocol: UDP
name: zipkin-udp
targetPort: 9411
type: ClusterIP
---
# Jaeger Query 的 Ingress 配置(Nginx Ingress Controller)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jaeger-query
namespace: jaeger
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
rules:
- host: jaeger.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jaeger-query
port:
number: 16686
---
# Collector 的 Service 配置
apiVersion: v1
kind: Service
metadata:
name: jaeger-collector
namespace: jaeger
spec:
selector:
app: jaeger-collector
ports:
- port: 14250
name: grpc-collector
targetPort: 14250
- port: 14268
name: http-collector
targetPort: 14268
type: ClusterIP
六、高级配置技巧
6.1 采样策略配置
在生产环境中,不是所有请求都需要全量追踪。Jaeger 支持多种采样策略,合理配置可以显著降低存储压力:
# 采样策略示例配置(Jaeger 配置)
# 方式一:固定概率采样
sampling:
strategies:
default:
type: probabilistic
param: 0.1 # 10% 的请求会被采样
# 方式二:基于服务名的差异化采样
service:
"payment-service":
type: probabilistic
param: 0.5 # 支付服务采样率高一些,因为重要
"health-check-service":
type: probabilistic
param: 0.0 # 健康检查完全不采样
# 方式三:自适应采样(Jaeger 1.36+ 支持)
# 根据负载自动调整采样率
default:
type: jaegerremote
options:
local_sampling_probability: 0.01 # 本地兜底采样率
decision_wait_timeout: 10000 # 等待决策的超时时间(毫秒)
在 Spring Boot 配置中对应如下:
# Spring Boot 中的采样配置
management:
tracing:
sampling:
# 本地采样率
probability: 0.1
# 采样器类型:parentbased_traceidratio
# 表示如果父 Span 被采样,子 Span 也被采样
sampler: parentbased_traceidratio
# 如果使用远程采样决策(推荐生产使用)
opentelemetry:
sampler:
type: jaegerremote
remote:
endpoint: http://jaeger-collector.jaeger.svc.cluster.local:14250
local-configuration:
sampling-server-check-delay: 60000 # 60秒检查一次
use-down-sampling: true # 启用降采样
6.2 标签和元数据丰富化
为了让追踪数据更有价值,你可以在 Span 中添加自定义标签,方便在 Jaeger 界面上做过滤和聚合:
/**
* 为 Span 添加业务标签的实用工具类
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
package com.example.orderservice.util;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
/**
* 追踪标签工具类
* 封装常用的标签操作,让业务代码更简洁
*/
public class TraceTagUtil {
// 预定义常用标签键
public static final AttributeKey<String> USER_ID = AttributeKey.stringKey("user.id");
public static final AttributeKey<String> REQUEST_ID = AttributeKey.stringKey("request.id");
public static final AttributeKey<String> DATABASE_NAME = AttributeKey.stringKey("db.name");
public static final AttributeKey<String> HTTP_METHOD = AttributeKey.stringKey("http.method");
public static final AttributeKey<String> HTTP_STATUS = AttributeKey.stringKey("http.status_code");
public static final AttributeKey<Long> CACHE_HIT = AttributeKey.booleanKey("cache.hit");
/**
* 为当前 Span 添加用户上下文信息
* 通常在请求进入的入口点调用
*/
public static void addUserContext(String userId) {
Span currentSpan = Span.current();
if (currentSpan != null && currentSpan.isRecording()) {
currentSpan.setAttribute(USER_ID, userId);
}
}
/**
* 标记缓存命中情况
* 用于分析缓存命中率,优化性能
*/
public static void markCacheHit(boolean hit) {
Span currentSpan = Span.current();
if (currentSpan != null && currentSpan.isRecording()) {
currentSpan.setAttribute(CACHE_HIT, hit);
}
}
/**
* 为数据库操作创建带标签的 Span
*/
public static Scope traceDatabaseOperation(Tracer tracer, String operationName,
String tableName, String queryType) {
Span span = tracer.spanBuilder(operationName)
.setAttribute(DATABASE_NAME, tableName)
.setAttribute("db.operation", queryType) // SELECT, INSERT, UPDATE, DELETE
.startSpan();
return span.makeCurrent();
}
/**
* 记录异常到当前 Span
*/
public static void recordError(Exception e) {
Span currentSpan = Span.current();
if (currentSpan != null && currentSpan.isRecording()) {
currentSpan.setStatus(
io.opentelemetry.api.trace.StatusCode.ERROR,
e.getMessage()
);
currentSpan.recordException(e);
}
}
}
6.3 Kubernetes 环境变量自动注入
一个实用技巧是利用 Kubernetes 的 Downward API,让应用自动获取自身的 Pod 信息,这些信息会自动成为追踪标签:
# 利用 Downward API 注入 Pod 信息作为追踪上下文
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-with-pod-info
namespace: default
spec:
template:
spec:
containers:
- name: order-service
image: registry.example.com/order-service:latest
env:
# 自动注入 Pod 名称
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# 自动注入命名空间
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# 自动注入节点名称
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# 自动注入 Pod IP
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# 自动注入服务账号
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
# OpenTelemetry 资源自动检测
- name: OTEL_RESOURCE_ATTRIBUTES
value: "k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(POD_NAMESPACE),k8s.node.name=$(NODE_NAME)"
- name: OTEL_SERVICE_NAME
value: "order-service"
七、应用场景分析
7.1 微服务性能瓶颈定位
在一个典型的用户下单场景中,请求链路可能是这样的:
用户 -> API网关 -> 订单服务 -> 库存服务 -> 数据库
-> 支付服务 -> 第三方支付API
-> 通知服务 -> 短信/邮件服务
当用户反馈"下单特别慢"时,如果没有分布式追踪,你只能挨个查日志,很难快速定位是哪一步出了问题。有了 Jaeger 之后,你在界面上选择用户对应的订单ID,就能看到完整的时间线:
- 库存服务查询数据库耗时 3 秒
- 支付服务调用第三方支付 API 耗时 5 秒
- 其他环节都在毫秒级
这样你就能精准地知道,优化应该从支付服务的第三方支付调用入手,而不是盲目地优化数据库或者加机器。
7.2 故障排查与根因分析
假设某天上午,大量用户收到"订单创建失败"的告警。传统的排查方式是登录每个服务看日志,然后试图关联时间线。这个过程既慢又容易出错。
使用 Jaeger 后,排查流程变成:
- 在 Jaeger 界面上筛选错误状态的请求(Status: Error)
- 按时间范围过滤(比如告警触发前后 10 分钟)
- 找到具体的错误链路
- 展开链路,看到错误发生在哪一步
- 查看错误详情和堆栈信息
- 定位到是库存服务的数据库连接池耗尽
整个过程从小时级缩短到分钟级,而且不需要在多个系统间切换上下文。
7.3 服务依赖关系可视化
Jaeger 的依赖图(Dependencies)功能可以自动分析服务之间的调用关系。你不需要手动维护架构图,只要服务正常运行并上报追踪数据,Jaeger 就会自动生成依赖关系图。
这对以下场景特别有用:
- 新团队接手老系统时,快速了解服务间的调用关系
- 评估某个服务下线的影响范围
- 发现意料之外的服务依赖(比如服务A不应该调用服务B,但实际上调用了)
- 设计服务拆分方案时评估影响
7.4 性能基线建立与监控
通过持续收集追踪数据,你可以为每个关键接口建立性能基线。比如:
- 订单创建接口 P50 耗时 200ms,P99 耗时 1.5s
- 商品查询接口 P50 耗时 50ms,P99 耗时 200ms
- 用户登录接口 P50 耗时 100ms,P99 耗时 500ms
当性能指标超过基线时,可以自动触发告警。这比传统的 QPS 和响应时间监控更精准,因为你可以知道具体是哪个 Span 导致了性能下降。
八、技术优缺点分析
8.1 优势
降低故障排查成本:分布式追踪让故障定位从"大海捞针"变成"按图索骥"。开发者不需要在多个日志系统间来回切换,一个界面就能看完整个请求链路。这对于微服务架构来说,是降本增效的核心工具。
性能优化有据可依:传统的性能优化往往是"感觉哪里慢就优化哪里",没有数据支撑。有了追踪数据后,每个操作的实际耗时都清晰可见,优化目标明确,投入产出比高。
服务依赖关系自动维护:随着业务演进,服务间的调用关系会不断变化。手动维护的架构图很快就会过时。Jaeger 的依赖图基于实际流量数据生成,永远是最新的。
与 Kubernetes 生态无缝集成:Jaeger 的 Helm Chart 提供了丰富的配置选项,支持各种 Kubernetes 特性(如自动扩缩、节点调度等),且社区活跃,持续更新。
8.2 劣势
存储成本随流量线性增长:每一条追踪数据都要存储,流量越大,存储压力越大。虽然采样策略可以缓解这个问题,但高流量系统仍然需要投入相当的资源。
采样率与完整性的权衡:高采样率能获得完整的链路信息,但存储成本高;低采样率存储成本低,但可能丢失关键链路。自适应采样虽然能平衡两者,但配置复杂度较高。
对开发者的要求较高:合理的埋点需要理解业务逻辑和系统架构。如果埋点过多或过少,都会影响追踪效果。同时,开发者需要理解 OpenTracing/OpenTelemetry 的基本概念。
Jaeger 自身的运维成本:Jaeger 本身也是一个需要运维的系统。它的 Collector、Query、Agent 组件都需要关注健康状态、资源使用、版本升级等。
8.3 与替代方案的对比
与 Zipkin 对比:Zipkin 也是主流的分布式追踪系统,上手更简单,但功能上不如 Jaeger 丰富。Jaeger 在 UI 体验、依赖图生成、自适应采样等方面有明显优势。
与 Prometheus + Tempo 对比:Grafana 生态中的 Tempo 更轻量,与 Grafana 集成度更高,适合已经使用 Grafana 的团队。但 Jaeger 在分布式追踪的专业功能上更成熟。
与 AWS X-Ray 对比:X-Ray 是 AWS 原生的追踪服务,免运维,但如果你的基础设施在 AWS 以外,使用 X-Ray 的成本和复杂度都会显著增加。Jaeger 的开源特性使其更适合多云或混合云环境。
九、注意事项与最佳实践
9.1 不要在追踪中记录敏感信息
追踪数据可能被持久化存储,如果 Span 中包含了用户的密码、身份证号、银行卡号等敏感信息,会造成数据泄露风险。一定要建立规范,禁止在追踪标签中记录敏感数据。
/**
* 脱敏处理工具 - 避免在追踪数据中记录敏感信息
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
public class SensitiveDataFilter {
// 敏感字段关键词列表
private static final List<String> SENSITIVE_KEYS = List.of(
"password", "secret", "token", "credit_card",
"id_number", "bank_card", "ssn", "private_key"
);
/**
* 检查属性名是否包含敏感信息
* @param key 属性名
* @return true 表示该属性不应被记录
*/
public static boolean isSensitive(String key) {
String lowerKey = key.toLowerCase();
return SENSITIVE_KEYS.stream().anyMatch(lowerKey::contains);
}
/**
* 对值进行脱敏处理
* @param value 原始值
* @return 脱敏后的值
*/
public static String maskValue(String value) {
if (value == null || value.length() <= 4) {
return "****";
}
// 只保留最后4位
return "****" + value.substring(value.length() - 4);
}
/**
* 安全的标签设置
* 自动过滤敏感信息
*/
public static void setSafeAttribute(Span span, String key, String value) {
if (!isSensitive(key)) {
span.setAttribute(AttributeKey.stringKey(key), value);
} else {
// 敏感字段只记录是否设置了值,不记录实际内容
span.setAttribute(AttributeKey.stringKey(key + ".present"),
value != null);
}
}
}
9.2 控制追踪数据量
在高流量场景下,全量追踪会产生海量的数据。建议:
- 生产环境默认采样率不超过 10%,关键服务可以适当提高
- 健康检查、心跳请求完全不采样
- 使用自适应采样,让 Jaeger 根据负载自动调整采样率
- 设置合理的数据保留策略(比如追踪数据保留 7 天,依赖图数据保留 30 天)
9.3 注意网络通信方式的配置
这是最容易踩坑的地方之一。在 Kubernetes 中,应用和 Jaeger Collector 之间的网络通信需要特别注意:
如果使用 gRPC(推荐):
- 应用端配置:
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger-collector.jaeger.svc.cluster.local:4317 - 注意 gRPC 端口是 4317,不是 4318
如果使用 HTTP:
- 应用端配置:
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger-collector.jaeger.svc.cluster.local:4318 - HTTP 端口是 4318
如果使用 Jaeger Agent(UDP):
- 应用端配置:
JAEGER_AGENT_HOST=jaeger-agent.jaeger.svc.cluster.local - UDP 端口默认是 5775(OpenTracing)或 6831(Jaeger Thrift)
9.4 DNS 解析和 Service 名称确认
Kubernetes 中 Service 的 DNS 解析遵循 service-name.namespace.svc.cluster.local 的格式。常见错误包括:
- 忘记加 namespace:应该用
jaeger-collector.jaeger.svc.cluster.local,而不是jaeger-collector.svc.cluster.local - 使用了 Deployment 名称而不是 Service 名称
- 使用了 Pod IP 而不是 Service 名称(Pod IP 会变化)
在配置前,先在集群内验证 DNS 解析是否正常:
# 在集群中创建一个临时 Pod 来测试 DNS 解析
kubectl run dns-test --rm -it --restart=Never --image=busybox:1.36 -- sh
# 在 shell 中测试解析
nslookup jaeger-collector.jaeger.svc.cluster.local
# 测试连接
nc -zv jaeger-collector.jaeger.svc.cluster.local 14268
9.5 版本兼容性
Jaeger 和 OpenTelemetry 之间有版本兼容性问题。以下是已知兼容的版本组合:
# 推荐使用的版本组合(截至 2024 年)
# Jaeger 版本: 1.52+
# OpenTelemetry Java SDK 版本: 1.35+
# Spring Boot 版本: 3.2+
# 检查当前 Jaeger 版本
kubectl -n jaeger exec deploy/jaeger-collector -- jaeger-collector --version
# 查看 OpenTelemetry Java SDK 版本
mvn dependency:tree | grep opentelemetry
9.6 持久化存储配置
Jaeger 默认使用 Badger 存储(嵌入式,仅适合测试),生产环境必须配置外部存储。以下是 Elasticsearch 存储的配置:
# Jaeger Collector 的 Elasticsearch 存储配置
apiVersion: v1
kind: ConfigMap
metadata:
name: jaeger-collector-config
namespace: jaeger
data:
# Elasticsearch 连接配置
STORAGE_TYPE: "elasticsearch"
ES_SERVER_URLS: "http://elasticsearch-data.jaeger.svc.cluster.local:9200"
# 是否启用 SSL
ES_TLS_SKIP_HOST_VERIFY: "false"
# 索引名称模板
ES索引_PREFIX: "jaeger-v1"
# 创建索引时的模板
ES_CREATE_INDEX: "true"
# 批量写入配置
ES_BATCH_INDEX: "true"
ES_BATCH_EMIT: "100"
ES_NUM_SHARDS: "3"
ES_NUM_REPLICAS: "1"
ES_REFRESH_INTERVAL: "10s"
# 健康检查超时
ES_HEALTHCHECK_TIMEOUT: "5s"
十、调试和排错指南
10.1 常见问题排查清单
当追踪数据没有出现在 Jaeger 界面上时,按以下顺序排查:
# 第一步:确认应用 Pod 运行正常
kubectl get pods -l app=order-service
# 第二步:确认应用能否连接到 Jaeger Collector
kubectl exec -it <order-service-pod-name> -- sh
# 在容器内执行:
wget -qO- http://jaeger-collector.jaeger.svc.cluster.local:14268/
# 第三步:确认 Jaeger Collector 日志中没有错误
kubectl -n jaeger logs deploy/jaeger-collector --tail=100
# 第四步:确认存储后端正常
kubectl -n jaeger logs statefulset/elasticsearch-master --tail=50
# 第五步:直接在 Jaeger 界面查看是否有数据
kubectl -n jaeger port-forward svc/jaeger-query 16686:16686
# 然后浏览器访问 http://localhost:16686
# 第六步:检查网络策略是否阻止了通信
kubectl -n jaeger get networkpolicy
# 第七步:查看应用的追踪配置是否正确加载
kubectl exec -it <order-service-pod-name> -- env | grep -i jaeger
kubectl exec -it <order-service-pod-name> -- env | grep -i otlp
kubectl exec -it <order-service-pod-name> -- env | grep -i otelemetry
10.2 验证追踪数据的完整性
确认数据上报正常后,还需要验证追踪数据的完整性:
/**
* 追踪数据验证工具 - 用于自测追踪功能是否正常
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
package com.example.orderservice.test;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class TracingHealthIndicator implements HealthIndicator {
private final Tracer tracer;
public TracingHealthIndicator(io.opentelemetry.api.OpenTelemetry openTelemetry) {
this.tracer = openTelemetry.getTracer("TracingHealthIndicator");
}
@Override
public Health health() {
// 创建测试 Span 来验证追踪功能
Span testSpan = tracer.spanBuilder("healthcheck-tracing-verification")
.setAttribute("healthcheck", true)
.startSpan();
try (Scope scope = testSpan.makeCurrent()) {
// 检查 Span 是否处于活跃状态
boolean isRecording = testSpan.isRecording();
if (isRecording) {
// Span 正在记录数据,说明追踪配置正常
testSpan.setStatus(
io.opentelemetry.api.trace.StatusCode.OK,
"Tracing is working"
);
return Health.up()
.withDetail("tracing", "active")
.withDetail("spanId", testSpan.getSpanContext().getSpanId())
.build();
} else {
// Span 没有处于记录状态,追踪可能未正确配置
return Health.down()
.withDetail("tracing", "inactive")
.withDetail("reason", "Span is not recording")
.build();
}
} finally {
testSpan.end();
}
}
}
10.3 使用 Kubernetes Debug 工具排查网络问题
# 使用 kubectl debug 创建调试 Pod
# 这个 Pod 拥有主机网络权限,适合排查网络问题
kubectl debug node/<node-name> -it --image=nicolaka/netshoot -- sh
# 在调试 Pod 中测试连接
# 测试到 Jaeger Collector 的 TCP 连接
nc -zv jaeger-collector.jaeger.svc.cluster.local 14250
# 测试 DNS 解析
nslookup jaeger-collector.jaeger.svc.cluster.local
# 使用 curl 测试 HTTP 端点
curl -v http://jaeger-collector.jaeger.svc.cluster.local:14268/
# 查看网络命名空间信息
ip addr show
ip route show
cat /etc/resolv.conf
# 测试 UDP 端口(Jaeger Agent 使用 UDP)
nc -zu jaeger-agent.jaeger.svc.cluster.local 5775
十一、完整实战:从零搭建追踪系统
下面是一个完整的实战流程,展示如何在 30 分钟内搭建一个可用的分布式追踪环境。
11.1 创建命名空间
# 创建专用的追踪命名空间
kubectl create namespace jaeger
# 给命名空间打上标签,方便管理
kubectl label namespace jaeger app.kubernetes.io/part-of=observability
kubectl label namespace jaeger app.kubernetes.io/managed-by=helm
11.2 部署 Jaeger
# 使用 Helm 部署 All-in-One 版本(快速验证用)
helm install jaeger jaeger/jaeger \
--namespace jaeger \
--set "jaegerCollector.enabled=true" \
--set "jaegerQuery.enabled=true" \
--set "elasticsearch.master.enabled=false" \
--set "elasticsearch.data.enabled=false" \
--set "jaeger.allInOne.enabled=true" \
--set "jaeger.allInOne.ports.query.containerPort=16686" \
--set "jaeger.allInOne.ports.grpcCollector.containerPort=14250" \
--set "jaeger.allInOne.ports.httpCollector.containerPort=14268" \
--set "jaeger.allInOne.ports.zipkinUdp.containerPort=9411" \
--set "jaeger.allInOne.ports.otlpGrpc.containerPort=4317" \
--set "jaeger.allInOne.env.SPAN_STORAGE_TYPE=badger" \
--set "jaeger.allInOne.ports.grpcQuery.containerPort=16685" \
--wait \
--timeout 5m
# 验证部署状态
kubectl -n jaeger rollout status deploy/jaeger-allinone
kubectl -n jaeger get svc jaeger-query
# 创建端口转发,访问 Jaeger 界面
kubectl -n jaeger port-forward svc/jaeger-query 16686:16686 &
11.3 部署示例应用
/**
* 完整的 Spring Boot 应用入口
* 技术栈:Java 17 + Spring Boot 3.2 + OpenTelemetry + Jaeger
*/
package com.example.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
System.out.println("OrderService started - Jaeger tracing enabled!");
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
11.4 部署到 Kubernetes
# 构建并推送镜像
docker build -t registry.example.com/order-service:1.0.0 .
docker push registry.example.com/order-service:1.0.0
# 应用 Kubernetes 配置
kubectl apply -f order-service-deployment.yaml
kubectl apply -f order-service-service.yaml
# 等待 Pod 就绪
kubectl rollout status deploy/order-service --timeout=120s
# 验证 Pod 状态
kubectl get pods -l app=order-service
kubectl describe pod -l app=order-service
# 发送测试请求
# 先在集群内创建一个临时 Pod
kubectl run curl-test --rm -it --image=curlimages/curl -- sh
# 在 curl-test 中发送请求
curl -X POST http://order-service/api/orders/create \
-H "Content-Type: application/json" \
-d '{"product": "测试商品", "quantity": 1, "userId": "user123"}'
11.5 验证追踪数据
# 确认 Jaeger 中有数据
kubectl -n jaeger logs deploy/jaeger-allinone --tail=20
# 打开 Jaeger 界面(http://localhost:16686)
# 在 Services 下拉框选择 "order-service"
# 应该能看到刚才测试请求产生的 Span 数据
# 查看依赖图
# 点击 Jaeger 界面右上角的 Dependencies 按钮
# 可以看到 order-service 到 inventory-service、payment-service 的调用关系
十二、总结
在容器化环境中落地分布式追踪,本质上就是在 Kubernetes 的网络、存储、调度这些特性基础上,把 Jaeger 的各组件和应用串联起来。这个过程看似复杂,但只要理清组件之间的关系,按照步骤配置,其实并没有想象中那么难。
从实战经验来看,以下几个要点最为关键:
第一,网络连通性是一切的基础。在 Kubernetes 中,Service 名称的拼写、namespace 的正确性、端口号的选择,任何一个细节错了都会导致追踪数据丢失。所以配置完成后一定要做网络连通性验证。
第二,采样策略要合理。不是所有流量都需要追踪。生产环境全量追踪不仅浪费存储资源,还可能影响系统性能。建议默认 10% 采样率起步,关键服务适当提高。
第三,标签体系要规范。Span 的标签设计直接影响追踪数据的可用性。建议团队制定统一的标签规范,包括必须包含的字段(如用户ID、请求ID、服务名称)和禁止包含的字段(如密码、身份证号等敏感信息)。
第四,把追踪作为长期工程来做。Jaeger 不是一个装完就不用管的工具。存储的容量规划、版本的升级维护、采样策略的调优,都需要持续关注。建议把 Jaeger 相关的运维工作纳入日常巡检流程。
分布式追踪的价值在于,它把一个黑箱变成了一本透明的操作手册。每一次请求从哪里来、经过哪里、到哪里去,都一目了然。这种透明度带来的不仅是故障排查效率的提升,更是整个团队对系统健康状况的掌控力。
如果你的项目正在 Kubernetes 上运行,还在为"请求慢在哪一步"而头疼,那分布式追踪就是你需要的工具。从 Jaeger 开始,投入的时间会给你回报很多倍的效率提升。
评论
围绕“Jaeger与Kubernetes深度集成的配置技巧解决容器化环境追踪难题”参与讨论