OpenTracing数据模型 OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。 特别说明,一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), Span与Span的关系被命名为References。 ps: Span,可以被翻译为跨度,可以被理解为一次方法调用, 一个程序块的调用, 或者一次RPC/数据库访问.只要是一个具有完整时间周期的程序访问,都可以被认为是一个span.在此译本中,为了便于理解,Span和其他标准内声明的词汇,全部不做名词翻译。
trace_id 全局链路id span_id 当前服务的链路id pspan_id 上游服务的链路id
http://localhost:16686/
http://localhost:5601/
https://www.jaegertracing.io/
https://www.jaegertracing.io/docs/1.35/deployment/#discovery-system-integration
https://pkg.go.dev/github.com/jtolds/gls#section-readme
https://github.com/jaegertracing/jaeger
https://github.com/jaegertracing/jaeger-client-go
https://github.com/jaegertracing/jaeger/blob/main/ports/ports.go
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/pkg/errors"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
jaegerlog "github.com/uber/jaeger-client-go/log"
"github.com/uber/jaeger-lib/metrics"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/jtolds/gls"
由于logstash比较笨重,不采用logstash直接采集日志,采用各个服务器搭建filebeat采集tracelog到kafka, logstash集中消费kafka到es
func Init(service string, options ...config.Option) (closer io.Closer) {
host := util.GetHostName()
queSize := jaegerQueueSizeTest
isLocal := false
cfg := &config.Configuration{
ServiceName: "test",
Sampler: &config.SamplerConfig{
//Type: jaeger.SamplerTypeProbabilistic,
//Param: jaegerProbabilityValue.JaegerProbabilityValue,
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &config.ReporterConfig{
//QueueSize: queSize,
LogSpans: true,
//LocalAgentHostPort: jaegerAgentUrl.JaegerAgentUrl,
//BufferFlushInterval: 1 * time.Second,
//CollectorEndpoint: jaegerCollectorUrl.JaegerCollectorUrl,
},
}
if isLocal {
cfg.Reporter = &config.ReporterConfig{
QueueSize: queSize,
LogSpans: true,
LocalAgentHostPort: jaegerAgentUrl.JaegerAgentUrl,
BufferFlushInterval: 1 * time.Second,
}
} else {
cfg.Reporter = &config.ReporterConfig{
LogSpans: true,
CollectorEndpoint: jaegerCollectorUrl.JaegerCollectorUrl,
}
}
tlog.Info("jaegerInit", host, queSize, isLocal, fmt.Sprintf("%+v", cfg.Reporter))
//options = append(options, config.Logger(&traceLog{}), config.Metrics(newMetricRecord()))
options = append(options, config.Logger(jaegerlog.StdLogger), config.Metrics(newMetricRecord()))
closer, err := cfg.InitGlobalTracer(service, options...)
// tracer, closer, err := cfg.New(service, options...)
if err != nil {
//panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
tlog.Error("ERROR: cannot init Jaeger: ", err)
return
}
// opentracing.SetGlobalTracer(tracer)
return
}
package ot
import (
"context"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/pkg/errors"
"github.com/uber/jaeger-client-go"
)
func containStr(strs []string, needle string) bool {
for _, s := range strs {
if s == needle {
return true
}
}
return false
}
func GinHeaderSwitchOn(c *gin.Context) bool {
jaeger := c.GetHeader("Jaeger")
if jaeger == "jaeger" {
return true
}
return false
}
// JaegerTrace gin jaeger中间件,用于对每个请求生成一个span,并使用opentracing生成的spanID作为日志的spanID
func JaegerTrace() gin.HandlerFunc {
return func(c *gin.Context) {
// 网关层是否开启链路
if !(GinHeaderSwitchOn(c)) {
c.Next()
return
}
var err error
// find spanContext in http header
parentSpanCtx, _ := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(c.Request.Header))
optName := c.Request.RequestURI
if c.Request.Method == http.MethodGet {
pos := strings.Index(optName, "?")
if pos != -1 {
optName = optName[:pos]
}
}
span := opentracing.StartSpan(optName,
ext.RPCServerOption(parentSpanCtx),
ext.SpanKindRPCServer,
opentracing.Tag{Key: string(ext.PeerAddress), Value: c.ClientIP()},
opentracing.Tag{Key: string(ext.HTTPUrl), Value: c.Request.RequestURI},
)
defer func() {
if err != nil {
span.SetTag("error", err.Error())
}
span.Finish()
}()
// 这里context关联span
ctx := context.Background()
ctx = opentracing.ContextWithSpan(ctx, span)
// 在 header 中加上当前进程的上下文信息
c.Request = c.Request.WithContext(ctx)
// 获取tracerID、spanID和pSpanID
traceID := getTracerIDGin(c, span.Context())
pSpanID := getParentSpanIDGin(c, parentSpanCtx, span.Context())
spanID := getSpanIDGin(c, span.Context())
// 上报jaeger
GinSpanExtLog(ctx, c.Request, span)
// gls设置全局span_id, trace_id
gls.SetGlsWithCtx(traceID, pSpanID, spanID, ctx, func() {
c.Next()
})
// span设置 tag
ext.HTTPStatusCode.Set(span, uint16(c.Writer.Status()))
}
}
func getTracerIDGin(c *gin.Context, spanCtx opentracing.SpanContext) (tracerID string) {
tracerIDTmp, ok := spanCtx.(jaeger.SpanContext)
if !ok {
return
}
tracerID = c.Request.Header.Get("sm-trace-id")
if len(tracerID) > 0 {
return
}
return tracerIDTmp.TraceID().String()
}
func getParentSpanIDGin(c *gin.Context, parentSpanCtx, spanCtx opentracing.SpanContext) (pSpanID string) {
// 先从opentracing中取parentSpan的spanid,作为本次请求的 pSpanID
_, pSpanID, _ = GetSpanIDFromSpanCtx(parentSpanCtx)
if pSpanID != "" && pSpanID != "0" {
return
}
// 没有的话,取Header中的spanID
pSpanID = c.Request.Header.Get("sm-span-id")
if pSpanID != "" {
return
}
// 还没有的话,pSpanID == spanID
_, pSpanID, _ = GetSpanIDFromSpanCtx(spanCtx)
return
}
func getSpanIDGin(c *gin.Context, spanCtx opentracing.SpanContext) (spanID string) {
_, spanID, _ = GetSpanIDFromSpanCtx(spanCtx)
if spanID != "" {
return
}
spanID = util.GenerateSpanID(c.Request.RemoteAddr)
return
}
// GetSpanIDFromSpanCtx 从spanContext中获取父spanid和spanid,目前仅支持jaeger的
func GetSpanIDFromSpanCtx(spanCtx opentracing.SpanContext) (pSpanID, spanID string, err error) {
switch t := spanCtx.(type) {
case jaeger.SpanContext:
spanID = spanCtx.(jaeger.SpanContext).SpanID().String()
pSpanID = spanCtx.(jaeger.SpanContext).ParentID().String()
default:
err = errors.Wrapf(errors.New("sorry, unsupported spanCtx type."), "type", t)
}
return
}
package ot
import (
"context"
"fmt"
"strings"
"sm-goapi/src/golib/gls"
"sm-goapi/src/golib/tlog"
"github.com/opentracing/opentracing-go"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
type TextMapWriter struct {
metadata.MD
}
// 重写TextMapWriter的Set方法,我们需要将carrier中的数据写入到metadata中,这样grpc才会携带。
func (t TextMapWriter) Set(key, val string) {
//key = strings.ToLower(key)
t.MD[key] = append(t.MD[key], val)
}
func JaegerGrpcClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {
if strings.Contains(method, "/Ping") {
return invoker(ctx, method, req, reply, cc, opts...)
}
// 先从context中获取原始的span, 没有则直接过滤
parentSpan := opentracing.SpanFromContext(ctx)
if parentSpan == nil {
return invoker(ctx, method, req, reply, cc, opts...)
}
parentContext := parentSpan.Context()
tracer := opentracing.GlobalTracer()
span := tracer.StartSpan(method, opentracing.ChildOf(parentContext))
defer span.Finish()
// grpc client端连接log
GrpcSpanLog(ctx, "JaegerGrpcClientInterceptor", method, span, req)
// 从context中获取metadata。md.(type) == map[string][]string
mdOld, ok := metadata.FromIncomingContext(ctx)
traceID, _, spanID := gls.GetTraceInfo()
if !ok {
mdOld = metadata.Pairs("trace_id", traceID, "span_id", spanID)
} else {
// 如果对metadata进行修改,那么需要用拷贝的副本进行修改。(FromIncomingContext的注释)
mdOld = mdOld.Copy()
}
md := mdOld
md.Set("trace_id", traceID)
md.Set("span_id", spanID)
ctx = metadata.NewOutgoingContext(ctx, md)
// 定义一个carrier,下面的Inject注入数据需要用到。carrier.(type) == map[string]string
// carrier := opentracing.TextMapCarrier{}
carrier := TextMapWriter{md}
// 将span的context信息注入到carrier中
e := tracer.Inject(span.Context(), opentracing.TextMap, carrier)
if e != nil {
fmt.Println("tracer Inject err,", e)
}
// 创建一个新的context,把metadata附带上
ctx = metadata.NewOutgoingContext(ctx, md)
return invoker(ctx, method, req, reply, cc, opts...)
}
package ot
import (
"context"
"strings"
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
type TextMapReader struct {
metadata.MD
}
//读取metadata中的span信息
func (t TextMapReader) ForeachKey(handler func(key, val string) error) error { //不能是指针
for key, val := range t.MD {
for _, v := range val {
if err := handler(key, v); err != nil {
return err
}
}
}
return nil
}
func JaegerGrpcServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
if strings.HasSuffix(info.FullMethod, "/Ping") {
return handler(ctx, req)
}
// 从context中获取metadata。md.(type) == map[string][]string
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(nil)
}
// 非网关起始链路,则过滤
traceID, pSpanID := "", ""
if arr := md["trace_id"]; len(arr) > 0 {
traceID = arr[0]
}
if len(traceID) == 0 {
return handler(ctx, req)
}
if arr := md["span_id"]; len(arr) > 0 {
pSpanID = arr[0]
}
// 非网关起始链路,则过滤
if len(pSpanID) == 0 {
return handler(ctx, req)
}
carrier := TextMapReader{md}
tracer := opentracing.GlobalTracer()
spanContext, e := tracer.Extract(opentracing.TextMap, carrier)
if e != nil {
}
span := tracer.StartSpan(info.FullMethod, opentracing.ChildOf(spanContext))
defer span.Finish()
ctx = opentracing.ContextWithSpan(ctx, span)
return handler(ctx, req)
}
package middleware
import (
"context"
"strings"
"github.com/opentracing/opentracing-go"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
traceID, pSpanID := "", ""
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
// 如果对metadata进行修改,那么需要用拷贝的副本进行修改。(FromIncomingContext的注释)
md = md.Copy()
}
if arr := md["trace_id"]; len(arr) > 0 {
traceID = arr[0]
}
// 非网关起始链路,则过滤
if len(traceID) == 0 {
return LogInterceptor(ctx, req, info, handler)
}
if arr := md["span_id"]; len(arr) > 0 {
pSpanID = arr[0]
}
// 非网关起始链路,则过滤
if len(pSpanID) == 0 {
return LogInterceptor(ctx, req, info, handler)
}
// 获取tracerID、spanID和pSpanID
traceID1, pSpanID1, spanID := ot.TraceInfo(ctx)
if len(traceID) == 0 {
traceID = traceID1
}
if len(pSpanID1) == 0 {
pSpanID = pSpanID1
}
//gls.SetGls(traceID, pSpanID, spanID, func() {
// resp, err = _UnaryServerInterceptor(ctx, req, info, handler)
//})
gls.SetGlsWithCtx(traceID, pSpanID, spanID, ctx, func() {
if !strings.HasSuffix(info.FullMethod, "/Ping") {
span := opentracing.SpanFromContext(ctx)
defer span.Finish()
// grpc server端log
ot.GrpcSpanLog(ctx, "UnaryServerInterceptor", info.FullMethod, span, req)
tlog.Debug("UnaryServerInterceptor", "traceID", traceID, "pSpanID", pSpanID, "spanID", spanID, "4", traceID1, "pSpanID1", pSpanID1, md)
}
resp, err = LogInterceptor(ctx, req, info, handler)
})
return
}
closer := ot.Init("gateway")
defer func() {
if closer != nil {
closer.Close()
}
}()
// ot.JaegerTrace()
group.Use(usersLimited, ot.JaegerTrace())
qgrpc包中grpc.DialContext连接中引入
grpc.WithUnaryInterceptor(ot.JaegerGrpcClientInterceptor)
方法开始处调用 ot.JaegerGinTrace()
jaegerLog := ot.NewJaegerFuncLog()
……
……
……
// log写入
jaegerLog.Info("listProfile", "1", "type", "mutiProfileUserReq")
……
……
……
// log写入
jaegerLog.Info("formatProfile", "2", "res", fmt.Sprintf("%+v", resp))
// 传入span,上传给jaeger
jaegerLog.Write()
ot.Init(“user_server”)
closer := ot.Init("user_server")
defer func() {
if closer != nil {
closer.Close()
}
}()
s := grpc.NewServer(grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
ot.JaegerGrpcServerInterceptor, // 引入链路
middleware.UnaryServerInterceptor, // 上报及log传递trace_id, span_id, pspan_id
)))
方法开始处调用 ot.JaegerGrpcTrace()
ps: 各个节点需要传递context时,尽量使用gls.TraceCtx()去衔接初始化设置的context
Jaeger 是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API。UI相较于Zipkin的更加直观和丰富,还有一个则是sdk比较丰富,go语言编写,上传采用的是udp传输,效率高速度快。相比Pinpoint的缺点,当然是UI差距了,基本上现在流行的追踪系统UI上都远远逊于它。
jaeger的开发语言是golang
jaeger支持OpenTracing协议,同属于CNCF基金会
jaeger支持各种各样的客户端,包括Go、Java、Node、Python、C++等
jaeger支持udp协议传输,当然也支持http
从以上架构图可以看出,jaeger将jaeger-agent从业务应用中抽出,部署在宿主机或容器中,专门负责向collector异步上报调用链跟踪数据,这样做将业务应用与collector解耦了,同时也减少了业务应用的第三方依赖。另外jaeger整体是用go语言编写的,在并发性能、对系统资源的消耗上也对基于java的openzipkin好不少。
其中jaeger-collector和jaeger-query是必须的,其余的都是可选的,我们没有采用agent上报的方式,而是让客户端直接通过endpoint上报到collector。
是Jaeger客户端代码库,便于不同语言的项目来介入到Jaeger中,当我们的应用程序装载上之后,client会负责收集并发送数据到Agent。
· 实现了OpenTracing API接口
· 当应用建立Span并发出请求到下游的服务时,它会附带跟踪信息(Trace ID, Span ID, baggage)。其他信息比如请求的名字,请求的参数,日志不会发给下游服务,而会被取样并异步的通过Jaeger-client发送到Jaeger-agent。
· 因为创建跟踪信息的代价很小,跟踪功能是默认开启的。
· 虽然跟踪功能是默认开启,但只有一部分的跟踪会被记录下来。默认比例是0.1%的取样
下图为具体实现原理:
是Jaeger客户端代理,jaeger的agent,是一个监听在 UDP 端口上接收 span 数据的网络守护进程。 如同大多数分布式系统都拥有一个Agent一样,Jaeger的Agent有以下几类特点:
· agent收集并汇聚这些span信息到collector;
· agent的被设计成一个基础组件,旨在作为基础架构组件部署到所有宿主机;
· agent将client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节;
总结如下:
· 与应用运行在同一个机器里
· 负责接受从客户端通过UDP发来的Trace/Span信息
· 批量上传到Jaeger收集器
collector,顾名思义,从agent收集traces信息,并通过处理管道处理他们,再写入后端存储(backends)。
当前的collector工作主要是管理trace,建立索引,执行相关转换,并最终存储它们。
Collector中运行着sampling逻辑,根据我们设定的sampling方式对数据进行收集和处理。
总结如下:
· 接受从agent发来的Trace/Span信息
· 进行信息校验,索引和存储到后台的数据库
即数据存储。Jaeger的存储是一个可插拔的组件,目前支持Cassandra,Elasticsearch和Kafka(当然也支持纯内存方式,但是不适用于生产环境),当前我们生产环境使用的ES。
数据查询与前端界面展示。Query查询是一种从存储中检索trace,并提供UI以显示它们的服务。Jaeger 原生UI可以展示了一次Trace的数据流向,作为一次系统作用的数据传播/执行图。
生产环境也可以和Kibana配合查询使用,增加其统计能力。
ps:
数据存本地内存
docker run -d -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 -p 14250:14250
–name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 jaegertracing/all-in-one:1.35
ps: jaeger支持ES和Canssandra两种后端DB,本例就使用ES存储。
wget -c https://github.com/jaegertracing/jaeger/releases/download/v1.35.1/jaeger-1.35.1-linux-amd64.tar.gz
增加执行权限 chmod a+x jaeger-*
由于启动命令参数较多,先创建几个执行脚本,通过二进制文件来启动jaeger
#start-collector.sh #####直接连接es#####
nohup ./jaeger-collector --span-storage.type=elasticsearch --es.server-urls http://127.0.0.1:9200/ --log-level=debug > collector.log 2>&1 &
#start-agent.sh
###### agent通过http方式传给collector
nohup ./jaeger-agent --collector.host-port=127.0.0.1:14267 --discovery.min-peers=1 --log-level=debug > agent.log 2>&1 &
###### agent通过gprc方式上传给collector,collector传给kafka
nohup ./jaeger-agent --reporter.grpc.host-port=127.0.0.1:14350,127.0.0.1:14150,127.0.0.1:14050 --log-level=debug
#start-query.sh
nohup ./jaeger-query --span-storage.type=elasticsearch --es.server-urls=http://127.0.0.1:9200/ > query.log 2>&1 &
sudo yum install docker
#修改docker默认网段,防止与sg环境冲突
[worker()@sg-prod-cv-worker-10 ~]$ cat /etc/docker/daemon.json
{
"bip": "172.100.0.1/16"
}
docker network create jaeger-net --subnet 172.21.0.1/16
#kafka 127.0.0.1:9092
#topic jaeger-spans
#group jaeger-ingester
#删除索引 curl -v -X DELETE 'http://127.0.0.1:9200/jaeger-span-2022-06-14'
#docker run -e SPAN_STORAGE_TYPE=kafka jaegertracing/jaeger-collector:1.35 help
#docker run -e SPAN_STORAGE_TYPE=elasticsearch jaegertracing/jaeger-collector:1.35 help
#docker run -e SPAN_STORAGE_TYPE=elasticsearch jaegertracing/jaeger-ingester:1.35 help
#docker run -e SPAN_STORAGE_TYPE=elasticsearch jaegertracing/jaeger-query:1.35 help
#############collector ->es
docker run \
-d \
--name collector \
--network=jaeger-net \
--restart=always \
--hostname=collector \
-e SPAN_STORAGE_TYPE=elasticsearch \
-e ES_ARCHIVE_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=N0PXMmvN \
-e ES_ARCHIVE_USERNAME=elastic \
-e ES_ARCHIVE_PASSWORD=N0PXMmvN \
-e LOG_LEVEL="debug" \
-p 14267:14267 \
-p 14268:14268 \
-p 14269:14269 \
-p 14250:14250 \
-d \
jaegertracing/jaeger-collector:1.35
##################collector ->kafka
docker run \
-d \
--name collector-kafka-1 \
--network=jaeger-net \
--restart=always \
--hostname=collector-kafka-1 \
-e SPAN_STORAGE_TYPE=kafka \
-e KAFKA_PRODUCER_BROKERS="127.0.0.1:9092" \
-e KAFKA_TOPIC="jaeger-spans" \
-e LOG_LEVEL="debug" \
-p 14367:14267 \
-p 14368:14268 \
-p 14369:14269 \
-p 14350:14250 \
-d \
jaegertracing/jaeger-collector:1.35
docker run \
-d \
--name collector-kafka-2 \
--network=jaeger-net \
--restart=always \
--hostname=collector-kafka-2 \
-e SPAN_STORAGE_TYPE=kafka \
-e KAFKA_PRODUCER_BROKERS="127.0.0.1:9092" \
-e KAFKA_TOPIC="jaeger-spans" \
-e LOG_LEVEL="debug" \
-p 14167:14267 \
-p 14168:14268 \
-p 14169:14269 \
-p 14150:14250 \
-d \
jaegertracing/jaeger-collector:1.35
docker run \
-d \
--name collector-kafka-3 \
--network=jaeger-net \
--restart=always \
--hostname=collector-kafka-3 \
-e SPAN_STORAGE_TYPE=kafka \
-e KAFKA_PRODUCER_BROKERS="127.0.0.1:9092" \
-e KAFKA_TOPIC="jaeger-spans" \
-e LOG_LEVEL="debug" \
-p 14067:14267 \
-p 14068:14268 \
-p 14069:14269 \
-p 14050:14250 \
-d \
jaegertracing/jaeger-collector:1.35
###################### ingester
docker run \
-d \
--name ingester-1 \
--network=jaeger-net \
--restart=always \
--hostname=ingester-1 \
-e SPAN_STORAGE_TYPE=elasticsearch \
-e ES_ARCHIVE_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=N0PXMmvN \
-e ES_ARCHIVE_USERNAME=elastic \
-e ES_ARCHIVE_PASSWORD=N0PXMmvN \
-e KAFKA_CONSUMER_BROKERS="127.0.0.1:9092" \
-e KAFKA_CONSUMER_TOPIC="jaeger-spans" \
-e LOG_LEVEL="debug" \
jaegertracing/jaeger-ingester:1.35
docker run \
-d \
--name ingester-2 \
--network=jaeger-net \
--restart=always \
--hostname=ingester-2 \
-e SPAN_STORAGE_TYPE=elasticsearch \
-e ES_ARCHIVE_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=N0PXMmvN \
-e ES_ARCHIVE_USERNAME=elastic \
-e ES_ARCHIVE_PASSWORD=N0PXMmvN \
-e KAFKA_CONSUMER_BROKERS="127.0.0.1:9092" \
-e KAFKA_CONSUMER_TOPIC="jaeger-spans" \
jaegertracing/jaeger-ingester:1.35
docker run \
-d \
--name ingester-3 \
--network=jaeger-net \
--restart=always \
--hostname=ingester-3 \
-e SPAN_STORAGE_TYPE=elasticsearch \
-e ES_ARCHIVE_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=N0PXMmvN \
-e ES_ARCHIVE_USERNAME=elastic \
-e ES_ARCHIVE_PASSWORD=N0PXMmvN \
-e KAFKA_CONSUMER_BROKERS="127.0.0.1:9092" \
-e KAFKA_CONSUMER_TOPIC="jaeger-spans" \
jaegertracing/jaeger-ingester:1.35
################## query
docker run -d \
--name query \
--network=jaeger-net \
--restart=always \
--hostname=query \
-d \
-p 16685:16685 \
-p 16686:16686 \
-p 16687:16687 \
-e SPAN_STORAGE_TYPE=elasticsearch \
-e ES_ARCHIVE_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_SERVER_URLS="http://127.0.0.1:9200/" \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=N0PXMmvN \
-e ES_ARCHIVE_USERNAME=elastic \
-e ES_ARCHIVE_PASSWORD=N0PXMmvN \
-e LOG_LEVEL="debug" \
jaegertracing/jaeger-query:1.35
########### agent:docker
docker run \
--name agent \
--rm \
-p5775:5775/udp \
-p6831:6831/udp \
-p6832:6832/udp \
-p5778:5778/tcp \
-d \
jaegertracing/jaeger-agent:1.35 \
--reporter.grpc.host-port="127.0.0.1:14350,127.0.0.1:14150,127.0.0.1:14050"
######### agent二进制
nohup ./jaeger-agent --reporter.grpc.host-port=127.0.0.1:14350,127.0.0.1:14150,127.0.0.1:14050 --log-level=debug &