Jaeger
简介
Jaeger 是一个开源的端到端分布式追踪系统,可以追踪分布式系统中的请求流程。它可以帮助识别性能瓶颈,从而优化系统性能,并且有助于快速诊断故障和问题。它是由 Uber 开发,后来成为了 Cloud Native Computing Foundation (CNCF) 的一部分。
原理图
可以用物理机、虚拟机或容器部署应用程序和Jaeger客户端。
- 在宿主环境内:
Application:通过使用OpenTracing API进行代码层面的插桩(Instrumentation),来启用追踪功能。
OpenTracing API:这是一个提供了一组标准API的库,用于在服务之间传播追踪信息,并记录时间和元数据。
jaeger-client:这是一个OpenTracing兼容的追踪客户端,它负责将追踪数据发送到jaeger-agent。追踪数据通常通过UDP发送,以减少对应用程序性能的影响。
jaeger-agent:这是一个网络守护进程,接收由jaeger-client发送的追踪数据,并将其转发到jaeger-collector。它可能还会对数据进行一些初步的处理,如采样。
- jaeger-collector内:
jaeger-collector:收集器接收由代理转发的追踪数据,并将其存储在后端存储系统中。它还有一个内存队列来缓冲追踪数据,并支持自适应采样策略,以动态调整采样率。
Adaptive Sampling:自适应采样可以根据当前的追踪量和配置动态调整数据采集的粒度。
- 存储和查询:
Data Store (Cassandra):追踪数据被存储在这个数据存储中,Cassandra是一种高度可扩展的NoSQL数据库,也可以使用其他数据库如Elasticsearch。
jaeger-ui (React):这是Jaeger的前端用户界面,使用React编写,允许用户查询和可视化追踪信息。
jaeger-query (Go):这个组件处理来自UI的查询请求,从数据存储中检索追踪数据并返回给UI。
此外,可以使用Spark对收集到的追踪数据进行复杂的分析和处理。
使用流程
- 安装Jaeger:
可以通二进制文件安装、Docker 容器启动或在 Kubernetes 上部署。
Docker快速启动命令:
docker run -d --name jaeger -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest
这条命令将启动 Jaeger 并暴露其 UI 端口(16686)。
Docker compose启动:
version: '3.1'
services:
db:
image: jaegertracing/all-in-one
restart: always
environment:
COLLECTOR_ZIPKIN_HTTP_PORT: 9411
ports:
6831:6831/udp
16686:16686
- 集成应用程序:
选择合适的客户端库。Jaeger 支持多种语言,如 Java, Go, Python, Node.js 等。
在应用程序中集成 Jaeger 客户端。这通常涉及到配置 tracer 实例,并在应用程序中创建 spans 来追踪事务。
- 配置和启动 Tracer:
Tracer 负责将 span 数据发送给 Jaeger Agent 或直接到 Jaeger Collector。
Tracer 的配置取决于所用的客户端库,通常包括设置服务名称、采样策略和 Jaeger Agent 的地址。
- 创建 Spans:
Spans 是追踪中的基本单元,代表单个操作或事务。
在应用程序的关键部分创建 spans,例如请求的开始和结束,或是在执行重要任务时。
- 观察和分析数据:
一旦应用程序开始发送追踪数据到 Jaeger,可以通过访问 Jaeger UI(通常是 http://localhost:16686)来观察和分析这些数据。
在 Jaeger UI 中,可以查看事务流程图、检查单个 spans、查看服务之间的调用关系等。
- 调整和优化:
根据 Jaeger 提供的数据,你可以识别性能瓶颈、追踪错误和异常、优化服务间的交互。
- 部署和维护:
在开发和测试完成后,可以将 Jaeger 部署到生产环境。
跟踪数据的量可能会很大,所以要合理配置存储和保留策略。
使用示例
Go
首先安装Jaeger客户端库:
go get github.com/uber/jaeger-client-go
go get github.com/uber/jaeger-lib
go get github.com/opentracing/opentracing-go
示例代码:
package main
import (
"fmt"
"io"
"time"
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)
// 初始化Jaeger追踪器
func initJaeger(service string) (opentracing.Tracer, io.Closer) {
cfg := &config.Configuration{
ServiceName: service,
Sampler: &config.SamplerConfig{
Type: jaeger.SamplerTypeConst, // 使用常量采样器,采样所有请求
Param: 1,
},
Reporter: &config.ReporterConfig{
LogSpans: true, // 开启日志记录
BufferFlushInterval: 1 * time.Second, // 设置日志刷新间隔
},
}
// 创建新的追踪器
tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) // 初始化失败,抛出异常
}
return tracer, closer // 返回追踪器和关闭函数
}
func main() {
// 1.初始化 tracer
tracer, closer := initJaeger("hello-world")
defer closer.Close() // 确保在程序结束前关闭追踪器
opentracing.SetGlobalTracer(tracer) // 设置全局追踪器
// 2.开始一个新的span(注意:必须要调用 Finish()方法span才会上传到后端)
span := tracer.StartSpan("say-hello")
defer span.Finish()
// 3.模拟一些操作并记录
helloTo := "Jaeger"
helloStr := fmt.Sprintf("Hello, %s!", helloTo) // 格式化一个字符串
println(helloStr) // 在控制台打印字符串
// 4.设置标签和记录日志
span.SetTag("hello-to", helloTo) // 设置span的标签
span.LogKV("event", "string-format", "value", helloStr) // 记录事件
time.Sleep(1 * time.Second) // 模拟一些工作
span.LogKV("event", "println") // 记录打印事件
}
Python
from jaeger_client import Config
import time
def init_tracer(service_name):
config = Config(
config={ # Usually read from some YAML configuration
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
'reporter_batch_size': 1,
},
service_name=service_name,
validate=True,
)
return config.initialize_tracer()
def say_hello(hello_to):
with tracer.start_span('say-hello') as span:
span.set_tag('hello-to', hello_to)
hello_str = f'Hello, {hello_to}!'
print(hello_str)
span.log_kv({'event': 'string-format', 'value': hello_str})
time.sleep(1) # Simulate some work
span.log_kv({'event': 'print-string', 'value': hello_str})
if __name__ == "__main__":
tracer = init_tracer('hello-world')
say_hello('Jaeger')
# Close the tracer to flush the spans - important in a short-lived script
tracer.close()
Ref links
https://www.jaegertracing.io/docs/1.51/
https://www.lixueduan.com/posts/tracing/03-jaeger-quick-start/