Jaeger快速指南
承蒙大家厚爱,我的《Go语言之路》的纸质版图书已经上架京东,有需要的朋友请点击 此链接 购买。
分布式追踪可观测平台(如 Jaeger)对于架构为微服务的现代软件应用程序至关重要。Jaeger 可以映射分布式系统中的请求流和数据流。这些请求可能会调用多个服务,而这些服务可能会带来各自的延迟或错误。Jaeger 将这些不同组件之间的点连接起来,帮助识别性能瓶颈、排除故障并提高整体应用程序的可靠性。Jaeger是100%开源、云原生、可无限扩展的。
什么是Jaeger
Jaeger 是一个分布式追踪系统。Jaeger的灵感来自 Dapper 和 OpenZipkin,是一个由 Uber 创建并捐赠给 云原生计算基金会(CNCF) 的分布式跟踪平台。它可以用于监控基于微服务的分布式系统:
- 分布式上下文传递
- 分布式事务监听
- 根因分析
- 服务依赖性分析
- 性能/延迟优化
Jaeger架构
graph TD SDK["OpenTelemetry SDK"] --> |HTTP or gRPC| COLLECTOR COLLECTOR["Jaeger Collector"] --> STORE[Storage] COLLECTOR --> |gRPC| PLUGIN[Storage Plugin] COLLECTOR --> |gRPC/sampling| SDK PLUGIN --> STORE QUERY[Jaeger Query Service] --> STORE QUERY --> |gRPC| PLUGIN UI[Jaeger UI] --> |HTTP| QUERY subgraph Application Host subgraph User Application SDK end end
特性
高可扩展性
Jaeger后端设计为无单点故障,并可根据业务需求进行扩展。例如,通常 Uber 的任何一个 Jaeger 实例每天都要处理数十亿个 span。
与OpenTelemetry的关系
Jaeger 项目和 OpenTelemetry 项目有着不同的目标。OpenTelemetry 的目标是提供多种语言的应用程序接口(API)和 SDK,允许应用程序将各种遥测数据输出到任意数量的度量和跟踪后端。Jaeger 项目主要是跟踪后端,它接收跟踪遥测数据,并对数据进行处理、汇总、数据挖掘和可视化。如需了解更多信息,请参阅博客 《Jaeger and OpenTelemetry》。
Jaeger 最初是为支持 OpenTracing 标准而设计的,它实现了一套客户端工具——Jaeger SDK,但是目前 Jaeger 项目推荐使用 OpenTelemetry SDK 作为仪器(Jaeger SDK已废弃)。
Jaeger Collector 现在已能够接收 OTLP 格式的数据。
支持多种存储后端
Jaeger 可以与多种存储后端配合使用,它原生支持两种流行的开源 NoSQL 数据库作为跟踪存储后端:Cassandra
和 Elasticsearch
,此外它通过 gRPC API 与 ClickHouse 集成。
现代化的Web UI
Jaeger Web UI 使用流行的开源框架(React)实现。 v1.0 版支持高效处理大量数据,并显示数以万计 span 的 trace。
云原生部署
Jaeger 后端以 Docker 镜像集的形式发布。二进制文件支持多种配置方法,包括命令行选项、环境变量和多种格式的配置文件(yaml、toml 等)。
在生产 Kubernetes 集群中部署 Jaeger 的推荐方式是通过 Jaeger Operator。Jaeger Operator提供了一个 CLI,用于从 Jaeger CR 生成 Kubernetes manifests 。
Jaeger 生态系统还提供了 Helm chart 部署 Jaeger 的替代方式。
可观测性
默认情况下,所有 Jaeger 后端组件都公开 Prometheus 指标(也支持其他指标后端)。使用结构化日志库 zap 将日志写入标准输出。
使用 Docker 在本地运行 Jaeger
Jaeger 提供了一个随时可用的 all-in-one Docker 镜像,其中包含 Jaeger UI、jaeger-collector、jaeger-query 和 jaeger-agent,以及一个内存存储组件。
在你的电脑上执行以下命令运行 Jaeger:
docker run --rm --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.55
容器公开以下端口:
Port | Protocol | Component | Function |
---|---|---|---|
6831 | UDP | agent | accept jaeger.thrift over Thrift-compact protocol (used by most SDKs) |
6832 | UDP | agent | accept jaeger.thrift over Thrift-binary protocol (used by Node.js SDK) |
5775 | UDP | agent | (deprecated) accept zipkin.thrift over compact Thrift protocol (used by legacy clients only) |
5778 | HTTP | agent | serve configs (sampling, etc.) |
16686 | HTTP | query | serve frontend |
4317 | HTTP | collector | accept OpenTelemetry Protocol (OTLP) over gRPC |
4318 | HTTP | collector | accept OpenTelemetry Protocol (OTLP) over HTTP |
14268 | HTTP | collector | accept jaeger.thrift directly from clients |
14250 | HTTP | collector | accept model.proto |
9411 | HTTP | collector | Zipkin compatible endpoint (optional) |
容器启动后,使用浏览器打开 http://localhost:16686 即可访问 Jaeger UI。
Jaeger Tracing Go示例
Jaeger Go示例
下面是一个批量创建 span 并将 trace 数据发送到 Jaeger 的 Go 语言示例。
package main
import (
"context"
"fmt"
"math/rand"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
traceSDK "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
)
// jaeger trace Go demo by Q1mi
const (
serviceName = "Go-Jaeger-Demo"
jaegerEndpoint = "127.0.0.1:4318"
)
// setupTracer 设置Tracer
func setupTracer(ctx context.Context) (func(context.Context) error, error) {
tracerProvider, err := newJaegerTraceProvider(ctx)
if err != nil {
return nil, err
}
otel.SetTracerProvider(tracerProvider)
return tracerProvider.Shutdown, nil
}
// newJaegerTraceProvider 创建一个 Jaeger Trace Provider
func newJaegerTraceProvider(ctx context.Context) (*traceSDK.TracerProvider, error) {
// 创建一个使用 HTTP 协议连接本机Jaeger的 Exporter
exp, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint(jaegerEndpoint),
otlptracehttp.WithInsecure())
if err != nil {
return nil, err
}
res, err := resource.New(ctx, resource.WithAttributes(semconv.ServiceName(serviceName)))
if err != nil {
return nil, err
}
traceProvider := traceSDK.NewTracerProvider(
traceSDK.WithResource(res),
traceSDK.WithSampler(traceSDK.AlwaysSample()), // 采样
traceSDK.WithBatcher(exp, traceSDK.WithBatchTimeout(time.Second)),
)
return traceProvider, nil
}
// testTracer 编写一个批量创建span的tracer
// trace 和 span
func testTracer(ctx context.Context) {
tracer := otel.Tracer("test-tracer")
baseAttrs := []attribute.KeyValue{
attribute.String("domain", "liwenzhou.com"),
attribute.Bool("plagiarize", false),
attribute.Int("code", 7),
}
// 开启span
ctx, span := tracer.Start(ctx, "parent-span", trace.WithAttributes(baseAttrs...))
// 结束span
defer span.End()
// 使用for循环创建多个子span,方便查看效果
for i := range 10 { // Go1.22+
// 传入父ctx,开启子span
_, iSpan := tracer.Start(ctx, fmt.Sprintf("span-%d", i))
// 随机sleep,模拟子span中耗时的操作
time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
// 子span结束
iSpan.End()
}
fmt.Println("done!")
}
func main() {
ctx := context.Background()
shutdown, err := setupTracer(ctx)
if err != nil {
panic(err)
}
defer func() {
_ = shutdown(ctx)
}()
// 批量创建span并上报至Jaeger
testTracer(ctx)
}
将上述代码运行。
$ go run .
done!
Jaeger UI 使用
你可以使用Jaeger UI 左侧的搜索面板来搜索具有特定属性的 trace: 它们来自哪个服务、进行了什么操作、包含在跟踪中的特定标记(例如,HTTP状态码)、过去需要查找多长时间以及结果数量限制。
当你输入了搜索条件后,点击"Find Traces"按钮后,即可看到类似下面的搜索结果。
由于我运行了两次程序,所以页面上会有两条 trace 记录,可以点击其中一条记录查看详情。
在这个页面,你可以找到关于执行时间、都发出了哪些调用、调用的持续时间以及携带的特定属性等信息。
总结
随着 OpenTelemetry 的稳步发展,Jaeger 更倾向于遵循 OpenTelemetry 规范,并把精力集中在构建跟踪后端、可视化工具和数据挖掘技术上。作为普通开发者,使用 OpenTelemetry SDK + Jaeger 将是分布式追踪的理想方案。
参考链接
https://medium.com/jaegertracing/jaeger-tracing-a-friendly-guide-for-beginners-7b53a4a568ca
其他常用库的OTel相关内容
- OpenTelemetry 介绍
- OpenTelemetry Go快速指南
- 基于OTel的HTTP链路追踪
- gRPC的链路追踪
- zap记录链路追踪traceID
- GORM配置链路追踪
- Redis配置链路追踪