四 部署

因为不是所有机器都安装有docker,所以会有docker和二进制两种方式部署

1. 部署elasticsearch

docker pull es镜像时必须指定版本号

docker pull elasticsearch:6.4.2

启动

docker run -d -p 9200:9200 -p 9300:9300 -v /data1/elasticsearch/data:/usr/share/elasticsearch/data -v /data1/elasticsearch/logs:/usr/share/elasticsearch/logs -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" --restart=always --name es elasticsearch:6.4.2

启动后如果遇到报错说没权限Permission denied,则把-v指定的目录赋予权限执行

chmod +777 /data1/elasticsearch/data

重新启动es

docker start es
  • ES_JAVA_OPTS 指定jvm虚拟机内存
  • restart=always 程序异常退出后自动重启

    2. 部署collector

    1. 使用supervisor部署

    官网上并没有详细介绍collector的部署,不过一般默认参数然后根据存储引擎做相应配置就够用,想看详细参数可以执行

    二进制程序生成collector文档
    ./jaeger-collector docs
    

    使用supervisor管理jaeger-collector程序

    [program:jaeger-collector]
    command=/data1/jaeger/jaeger-collector  --log-level=debug
    autostart=true
    autorestart=true
    directory=/data1/jaeger
    startsecs=10
    environment=SPAN_STORAGE_TYPE=elasticsearch,ES_SERVER_URLS=http://你的esip:9200
    stdout_logfile=/data1/jaeger/logs/collector-info.log
    stdout_logfile_maxbytes=100MB
    stdout_logfile_backups=10
    stdout_capture_maxbytes=1MB
    stderr_logfile=/data1/jaeger/logs/collector-err.log
    stderr_logfile_maxbytes=100MB
    stderr_logfile_backups=10
    stderr_capture_maxbytes=1MB
    
  • environment,因为使用二进制启动需要使用环境变量指定存储引擎elasticsearch和存储引擎地址=http://你的esip:9200,所以supervisor配置文件需指定环境变量,放入supervosr配置文件后,执行supervisorctl update程序会自动启动

    2. 使用docker部署

    如果和es在一台机器则可以使用docker的--link参数

    docker run -d --name jaeger-collector --restart=always --link es:es -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://es:9200 -p 14267:14267 -p 14268:14268 -p 9411:9411 jaegertracing/jaeger-collector
    

    不在的话可以这样

    docker run -d --name jaeger-collector  --restart=always -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://你的esip:9200 -e -p 14267:14267 -p 14268:14268 -p 9411:9411 jaegertracing/jaeger-collector
    
  • SPAN_STORAGE_TYPE,指定存储引擎

  • ES_SERVER_URLS,指定存储引擎地址

  • –restart=always,一直执行,异常退出尝试重启

3. 部署query

因为jaeger的query没有校验用户的权限,建议不要对公网放开,我这里是需要的时候把服务启动,不需要的时候就关闭

使用docker部署

docker run -d --name jaeger-query --restart=always --link es:es -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://es:9200 -p 8080:16686/tcp jaegertracing/jaeger-query

4. 部署agent

二进制部署

使用supervisor管理

[program:jaeger-agent]
command=/data1/jaeger/jaeger-agent --reporter.tchannel.host-port=127.0.0.1:14267 --log-level=debug
autostart=true
autorestart=true
directory=/data1/jaeger
startsecs=10
stdout_logfile=/data1/jaeger/logs/agent-info.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=10
stdout_capture_maxbytes=1MB
stderr_logfile=/data1/jaeger/logs/agent-err.log
stderr_logfile_maxbytes=100MB
stderr_logfile_backups=10
stderr_capture_maxbytes=1MB
./jaeger-agent --reporter.tchannel.host-port=127.0.0.1:14267 --log-level=debug
  • 14267端口是agent发送span给collector的通道

五 使用

跨进程使用时有两个概念需要注意 - SpanContext 传递给下级span的信息trace_idspan_idparentId等 - Baggage 存储在SpanContext的键值集合,在一个链路上全局传输

HTTP服务Gin框架

1. 建立链接
import (
	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegerCfg "github.com/uber/jaeger-client-go/config"
)


if config.DefaultConfig.JaegerInfo.Enable {
	cfg := &jaegerCfg.Configuration{
		ServiceName: config.DefaultConfig.JaegerInfo.ServiceName,
		Sampler: &jaegerCfg.SamplerConfig{
			Type:  jaeger.SamplerTypeRateLimiting, //每秒采样几个span
			Param: 3.0,
		},
		Reporter: &jaegerCfg.ReporterConfig{
			LocalAgentHostPort: config.DefaultConfig.JaegerInfo.Addr,
			LogSpans:           true,
		},
	}
	tracer, closer, err := cfg.NewTracer(jaegerCfg.Logger(jaeger.StdLogger))
	if err != nil {
		panic(fmt.Sprintf("Init failed: %v\n", err))
	}
	defer closer.Close()
	opentracing.SetGlobalTracer(tracer)
}
2. 中间件

因为是http服务可能会涉及跨请求传播span,因此此中间件实现了从header中抽取上游span上下文,并根据上下文创建新span

import (
    "github.com/opentracing-contrib/go-gin/ginhttp"
	"github.com/opentracing/opentracing-go"
)

func Jaeger() func(c *gin.Context) {
	if !config.DefaultConfig.JaegerInfo.Enable {
		return func(c *gin.Context) {}
	}
	return ginhttp.Middleware(
		opentracing.GlobalTracer(),
		ginhttp.OperationNameFunc(func(r *http.Request) string {
			var opName = "HTTP " + r.Method + " "
			path := r.URL.Path
			lastIndex := strings.LastIndex(path, "/")
			if lastIndex <= 0 {
				return opName + path
			}
			return opName + path
		}),
	)
}

grpc服务

1. 建立连接

和上方HTTP的一样

2. 启动服务时加入拦截器

因为grpc有限制,不能加入多个拦截器,可以使用go-grpc-middleware解决

import (
	"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegerCfg "github.com/uber/jaeger-client-go/config"

)

	opts := []grpc.ServerOption{
		grpc.MaxRecvMsgSize(1024 << 20),
		grpc.MaxSendMsgSize(1024 << 20),
	}
	if config.DefaultConfig.JaegerInfo.Enable {
		tracer := opentracing.GlobalTracer()
		opts = append(opts, []grpc.ServerOption{
			grpc.UnaryInterceptor(
				otgrpc.OpenTracingServerInterceptor(tracer)),
			grpc.StreamInterceptor(
				otgrpc.OpenTracingStreamServerInterceptor(tracer)),
		}...)
	}

	serv := grpc.NewServer(opts...)
	___.RegisterDataServer(serv, s)
	if err = serv.Serve(lis); err != nil {
		logger.Println("serve error: ", err.Error())
	}

Gin框架中传递span

传递至grpc服务

在gin框架的http服务中设置好中间件后,那每个请求都会自动创建一个span,只需要把gin.Context对象中的Context传递给下游服务就行

rpc.___Client.Get(c.Request.Context(), req)

传递至http服务

ext.SpanKindRPCClient.Set(span)
    ext.HTTPUrl.Set(span, url)
    ext.HTTPMethod.Set(span, "GET")
    span.Tracer().Inject(
        span.Context(),
        opentracing.HTTPHeaders,
        opentracing.HTTPHeadersCarrier(req.Header),
    )