Uber jaeger--一個基於Go的分佈式追蹤系統

Jaeger-Uber開源的一個基於Go的分佈式追蹤系統

最近因工做須要在研究traing系統,最後選了jaeger,下面是一些總結,同時摘抄了網上的一些資料,並結合本身實踐過程當中遇到的一些什麼問題,歡迎指正,如你也在使用jaeger,或者想使用jaeger,途中遇到什麼困難,可發郵件交流:honglouleiyan@163.comhtml

前言

​ 隨着公司的發展,業務不斷的增長,模塊的不斷拆分,系統間業務調用就變得越複雜,對定位線上故障帶來很大困難。整個調用鏈不透明,猶如系統被蒙上一塊黑紗,當線上遇到故障時,整個技術部就陷入痛苦的漩渦。這時候分佈式追蹤系統應運而生,如揭開了黑紗,讓陽光照進黑暗。java

Opentracing相關文檔

 

分佈式追蹤系統Jaeger

 

Jaeger是Uber開發的一套分佈式追蹤系統,已在Uber大規模使用。並在2017-9-13 加入CNCF 開源組織。使用Jaeger能夠很是直觀的展現整個分佈式系統的調用鏈,由此能夠很好發現和解決問題:github

 

 

jaeger架構

 

jaeger官網網址

https://www.jaegertracing.io/docs/web

jaeger github地址

https://github.com/jaegertracing/jaegerspring

jaeger 部署

使用docker部署官網資料和網站資料比較多,不過都是使用cassandra做爲存儲引擎,而且通常文章並無給出具體的建表語句,官網文檔也沒有找到建表語句!sql

若是你須要使用cassandra做爲存儲引擎的話,這篇文章僅供參考:https://blog.csdn.net/niyuelin1990/article/details/80225305

文章中有對應的導入jaeger表結構部門:

導入Jaeger表結構,這裏不得不吐槽一下Jaeger!Jaeger二進制安裝包里根本沒有數據sql文件,並且沒有任何文檔告訴你SQL文件在哪裏找,沒安裝文檔!我表示是崩潰的,最終在源碼目錄的一個角落中找到SQL文件,路徑展現一下:

$GOPATH/src/github.com/jaegertracing/jaeger/plugin/storage/cassandra/schema/v001.cql.tmpl
12
格式:
cqlsh -h HOST -p PORT -f fileName
cqlsh 10.100.7.46 -f $GOPATH/src/github.com/jaegertracing/jaeger/plugin/storage/cassandra/schema/v001.cql.tmpl 123

上面的命令是我搜索來的,由於在導入以前我已經手動一條一條加進去了(>﹏<)!若是很差用的話,讀者能夠直接cqlsh一條一條黏上去!!!!

 

下面本文將介紹如何使用elasticsearch存儲引擎部署jaeger!

Jaeger組件

Agent

Agent是一個網絡守護進程,監聽經過UDP發送過來的Span,它會將其批量發送給collector。按照設計,Agent要被部署到全部主機上,做爲基礎設施。Agent將collector和客戶端之間的路由與發現機制抽象了出來。

Collector

Collector從Jaeger Agent接收Trace,並經過一個處理管道對其進行處理。目前的管道會校驗Trace、創建索引、執行轉換並最終進行存儲。存儲是一個可插入的組件,如今支持Cassandra和elasticsearch。

Query

Query服務會從存儲中檢索Trace並經過UI界面進行展示,該UI界面經過React技術實現,其頁面UI以下圖所示,展示了一條Trace的詳細信息。

存儲

jaeger採集到的數據必須存儲到某個存儲引擎,目前支持Cassandra和elasticsearch

 

docker + elasticsearch安裝

首先,你安裝jaeger時,須要使用docker環境,

而後使用docker安裝一個elasticsearch

docker run -d  --name elasticsearch --restart=always -p 9200:9200 -p 9300:9300 -e ES_JAVA_OPTS="-Xms512m -Xmx512m" elasticsearch:latest

注意:

一、此elasticsearch爲單機版且數據存內存,若生產環境,請自行解決如何使用docker安裝elasticsearch集羣而且數據寫入磁盤,elasticsearch版本請選擇5.X,緣由以下(github issues:https://github.com/jaegertracing/jaeger/issues/665

jaeger elasticsearch版本請使用5.X的版本,官網上雖說明使用5.X和6.X都行,可是親測使用6.2.4的es,會出現數據丟失:collector將數據寫入elsasticsearch時會出現索引已存在的報錯:

error:"trace_id":"89c90e9c1bc48622","span_id":"89c90e9c1bc48622","error":"elastic: Error 400 (Bad Request): index [jaeger-span-2018-05-27/5JHNIPoLRBe3c560r7FJlQ] already exists [type=resource_already_exists_exception]

 

 

​ 若你須要把elsticsearc 9200暴露到公網上,你注意Elasticsearch服務安全加固,可參考:https://www.sojson.com/blog/213.html

二、請使用docker安裝elasticsearch,若未使用docker安裝,下一步安裝collector時會出現報錯:

docker: Error response from daemon: could not get container for elasticsearch: No such container: elasticsearch.

容器中找不到對應的elasticsearch

 

docker + collector安裝

若你安裝的collector和elasticsearch是在同一臺機器上,使用docker容易的--link命令就能夠將collector和elasticsearch關聯上,安裝命令以下:

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

注意:

​ --link elasticsearch:elasticsearch,表明docker容易關聯,該名字必須和你安裝elasticsearch —name的名字相同

​ --SPAN_STORAGE_TYPE=elasticsearch 表明安裝jaeger選擇elasticsearch做爲存儲

-e ES_SERVER_URLS=http://elasticsearch:9200次條目表明你選擇容器安裝的elasticsearch的9200端口

-e ES_USERNAME elasticsearch的用戶名:默認elastic,下同

-e ES_PASSWORD elasticsearch的密碼

-e 其實就是表明的環境變量,其餘變量你可使用如下語句查看:

docker run -e SPAN_STORAGE_TYPE=elasticsearch jaegertracing/jaeger-collector /go/bin/collector-linux --help

 

固然,通常生產環境你確定不會將collector和elasticsearch安裝到同一臺機器,至少你可能會安裝多個collector,因此,如何跨機器的用collector鏈接此elasticsearch呢?

你能夠用用如下命令:

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

區別在於,你無需使用—link來進行容器互連,只需ES_SERVER_URLS填寫對應的ip和port便可;

若是你想看啓動是否成功,你可將命令中的「-d」改成「--rm」並刪除「--restart=always」 ,這樣啓動日誌會即時打印到控制檯

--rm命令選項,等價於在容器退出後,執行docker rm -v

--restart=always,一直執行,異常退出嘗試重啓

啓動成功日誌:

{"level":"info","ts":1527673610.349252,"caller":"healthcheck/handler.go:99","msg":"Health Check server started","http-port":14269,"status":"unavailable"}

{"level":"info","ts":1527673610.7525811,"caller":"static/strategy_store.go:76","msg":"No sampling strategies provided, using defaults"}

{"level":"info","ts":1527673610.752815,"caller":"collector/main.go:142","msg":"Registering metrics handler with HTTP server","route":"/metrics"}

{"level":"info","ts":1527673610.7528777,"caller":"collector/main.go:150","msg":"Starting Jaeger Collector HTTP server","http-port":14268}

{"level":"info","ts":1527673610.7529178,"caller":"healthcheck/handler.go:133","msg":"Health Check state change","status":"ready"}

 

如你出現如下錯誤:

"caller":"collector/main.go:102","msg":"Failed to init storage factory","error":"health check timeout: no Elasticsearch node available","errorVerbose":"no Elasticsearch node available

請檢查你的elasticsearch地址,

 

 

docker + query安裝

同collector同樣,若你安裝的collector和elasticsearch是在同一臺機器上,使用docker容易的--link命令就能夠將query和elasticsearch關聯上,安裝命令以下:

docker run -d --name jaeger-query --restart=always --link elasticsearch:elasticsearch -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://elasticsearch:9200 -e ES_USERNAME=elastic -e ES_PASSWORD=你的密碼 -p 16686:16686/tcp jaegertracing/jaeger-query

其餘對應的操做,你參考collector便可,到了這一步,若是你能將collector部署好,那麼部署query也是同樣的;

注意,ES_USERNAME、ES_PASSWORD這兩個環境變量,當你的elasticsearch未設置帳號密碼時,你能夠不填,也能夠填上默認值,elasticsearch的默認ES_USERNAME=elastic,ES_PASSWORD=changeme

部署完成query以後,根據你暴露的端口號(-p 16686:16686/tcp),瀏覽器輸入如下地址(將localhost換成你部署query的地址):

http://localhost:16686

你就會看到開篇的UI界面了,固然數據確定是空空如也。

 

 

docker + agent安裝

根據uber jaeger官網的架構,agent通常是和jaeger-client部署在一塊兒,agent做爲一個基礎架構,每一臺應用(接入jaeger-client的應用)所在的機器都須要部署一個agent;

根據數據採集原理,jaeger-client採集到數據以後,是經過UDP端口發送到agent的,jaeger-client和agent部署在一塊兒的好處是UDP傳輸數據都在應用所在的機器,可避免UDP的跨網絡傳輸,多一層安全保障。

固然,架構多是多變的,你的agent可能不和jaeger-client所在的應用在一臺機器,這個時候,jaeger-client就必須顯示的指定其鏈接的agent的IP及port,具體作法後文jaeger-client對應模塊會講到。

前文提到,jaeger-client採集到數據以後,是經過UDP端口發送到agent的,agent接收到數據以後,使用Uber的Tchannel協議,將數據發送到collector,因此,agent是必須和collector相連的;

docker安裝agent命令以下:

docker run   -d  --name jaeger-agent --restart=always -p 5775:5775/udp   -p 6831:6831/udp   -p 6832:6832/udp   -p 5778:5778/tcp   jaegertracing/jaeger-agent   /go/bin/agent-linux --collector.host-port=collector ip:14267

 

如前文所述,你可能不止一個collector,你可能須要這樣:

docker run   -d  --name jaeger-agent --restart=always -p 5775:5775/udp   -p 6831:6831/udp   -p 6832:6832/udp   -p 5778:5778/tcp   jaegertracing/jaeger-agent   /go/bin/agent-linux --collector.host-port=collector ip1:14267,collector ip2:14267,collector ip3:14267

--collector.host-port=collector ip1:14267,collector ip2:14267,collector ip3:14267,用逗號分開,鏈接三個collector,這樣的話,這三個collector只要一個存活,agent就能夠吧數據傳輸完成,以免單點故障

 

二進制安裝jaeger

以上,使用docker容器化的安裝jaeger是很是方便的,而後加上Kubernetes,能夠很好的作好監控管理;

具體使用Kubernetes安裝jaeger,你可自行研究,官方github地址:https://github.com/jaegertracing/jaeger-kubernetes

固然你也能夠不使用docker,linux安裝jaeger網上資料不少,如:https://blog.csdn.net/niyuelin1990/article/details/80225305

二進制安裝包地址:

https://github.com/jaegertracing/jaeger/releases

如安裝agent,如咱們通常應用文件同樣:

nohup ./jaeger-agent --collector.host-port=10.100.7.46:14267 1>1.log 2>2.log &

 

jaeger-client

目前jaeger官方支持如下客戶端:

Language GitHub Repo
Go jaegertracing/jaeger-client-go
Java jaegertracing/jaeger-client-java
Node.js jaegertracing/jaeger-client-node
Python jaegertracing/jaeger-client-python
C++ jaegertracing/jaeger-client-cpp
C# jaegertracing/jaeger-client-csharp

請他語言也在開發中,具體請看: issue #366.

因爲做者只會java開發,僅僅只能寫點java client的東西;

Java-client

Jaeger tracing收集數據原理是第一個應用被調用的時候生成一個traceId,而後這個traceId會放到HTTP請求頭裏面將其傳給下一個鏈路,而後每個鏈路裏面登陸帶有這個traceId,最後在elasticsearch/Cassandra裏面講採集到數據聚合成一個調用鏈路;

因此,jaeger應用場景爲HTTP調用鏈相關的場景,對於dubbo這種RPC調用我的認爲是不適用的。

以現有技術體系,目前成熟的框架有springmvc、springboot、springcloud,其中springboot、springcloud基本相同,本文只講springmvc、springboot,由於兩者有一些差異,須要特別處理;

 

springboot接入jaeger client

springboot 接入jaeger github地址以下:

http://planet.jboss.org/post/opentracing_spring_boot_instrumentation

一、在spring boot的項目pom.xml添加依賴

<dependency>

​ <groupId>io.opentracing.contrib</groupId>

​ <artifactId>opentracing-spring-web-autoconfigure</artifactId>

​ <version>0.3.0</version>

</dependency>

<!--添加jaeger-->

<dependency>

​ <groupId>com.uber.jaeger</groupId>

​ <artifactId>jaeger-core</artifactId>

​ <version>0.26.0</version>

</dependency>

二、注入jaeger bean

@Bean
    public Tracer jaegerTracer() {
        com.uber.jaeger.Configuration.SenderConfiguration senderConfiguration = new com.uber.jaeger.Configuration.SenderConfiguration();
        com.uber.jaeger.Configuration.ReporterConfiguration reporterConfiguration = new com.uber.jaeger.Configuration.ReporterConfiguration().withSender(senderConfiguration).withLogSpans(false).withMaxQueueSize(1000).withFlushInterval(100);
        com.uber.jaeger.Configuration.SamplerConfiguration samplerConfiguration = new com.uber.jaeger.Configuration.SamplerConfiguration().withType(ConstSampler.TYPE).withParam(1);
        com.uber.jaeger.Configuration configuration = new com.uber.jaeger.Configuration(traceAppName).withReporter(reporterConfiguration).withSampler(samplerConfiguration);
        return configuration.getTracer();
    }

請注意,此bean所屬的類必須隨着spring容器啓動,已確保spring啓動是此bean被注入:

即加上@Configuration 註解便可;

 

SenderConfiguration可供你選擇數據上報方式,使用with*方法選擇對應的參數:

senderConfiguration.withAgentHost(agent ip) —— 默認值爲本機

senderConfiguration.withAgentPort(6831) —— 默認值6831

如上例:SenderConfiguration什麼參數都沒有,即默認選擇本機agent,6831 UDP端口上報採集到的數據

 

HTTP直接上報

你也能夠選擇繞過agent,直接使用HTTP協議將數據上報給collector,這樣,你上文中就能夠沒必要安裝agent;

這是,你的SenderConfiguration設置如下參數:

senderConfiguration.withEndpoint("http://localhost:14268/api/traces");

localhost:14268 爲你的collector的ip和端口號,這樣你就能夠把數據直接上報到collector

固然,你可能會有一些安全方面的考慮,你可使用下面的方式設置你的用戶名和密碼,或者你的token

senderConfiguration.withAuthPassword(password);

senderConfiguration.withAuthUsername(username);

senderConfiguration.withAuthToken(authToken);

 

ReporterConfiguration參數:

withSender -------選擇發送方式

withLogSpans -------是否日誌上報

withMaxQueueSize -------數據最大累計量

withFlushInterval -------報告間隔的刷新( ms )

你能夠根據大家業務系統給的數據量選擇合適的參數;

根據uber jaeger"不憐憫"數據原則,若你選擇withMaxQueueSize爲1000(條),withFlushInterval爲1000(ms),即1000毫秒之內只會有1000條數據上報,其餘數據會丟掉

 

SamplerConfiguration 參數:

SamplerConfiguration可設置你的採樣策略:

withType 採樣策略:

  1. ConstSampler,全量採集

  2. ProbabilisticSampler ,機率採集,默認萬份之一

  3. RateLimitingSampler ,限速採集,每秒只能採集必定量的數據

  4. RemotelyControlledSampler ,一種動態採集策略,根據當前系統的訪問量調節採集策略

withParam 採樣率

withManagerHostPort 採樣策略配置 默認爲:localhost:5778

 

當使用uber jaeger時,若是你要在嵌入tracing的應用裏面發送HTTP請求,你可能須要用到RestTemplate,不然你用的HTTP client會致使trace id丟失,從而致使調用鏈斷裂;

因此你還須要注入RestTemplate bean,方式和jaeger bean同樣

@Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

說的再多,不過給你一個例子:

https://github.com/pavolloffay/opentracing-java-examples

 

Springmvc接入jaeger client

Springmvc 接入jaeger github地址以下:

https://github.com/opentracing-contrib/java-spring-web

一、在spring mvc的項目pom.xml添加依賴

<dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-web</artifactId>
            <version>0.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.uber.jaeger</groupId>
            <artifactId>jaeger-core</artifactId>
            <version>0.26.0</version>
        </dependency>

注意和springboot的區別

 

想在spring mvc引入tracing功能,配置中是必須添加TracingFilter and TracingHandlerInterceptor,這兩個類 是必須的,你能夠經過手動注入或者CDI的方式注入

具體代碼示例以下:

@EnableWebMvc
@Configuration
@Import({TracingBeansConfiguration.class})
public class SpringMVCConfiguration extends WebMvcConfigurerAdapter implements ServletContextListener {
​
    @Autowired
    private List<HandlerInterceptorSpanDecorator> spanDecorators;
​
    @Autowired
    private Tracer tracer;
    
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        GlobalTracer.register(tracer);
        registry.addInterceptor(new TracingHandlerInterceptor(tracer, spanDecorators));
    }
​
​
    @Bean
    public RestTemplate restTemplate(Tracer tracer) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Collections.<ClientHttpRequestInterceptor>singletonList(
                new TracingRestTemplateInterceptor(tracer)));
        return restTemplate;
    }
​
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        sce.getServletContext().setAttribute(TracingFilter.SPAN_DECORATORS,
                Collections.singletonList(ServletFilterSpanDecorator.STANDARD_TAGS));
    }
​
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        
    }
}

這段代碼:implements ServletContextListener

因此咱們須要在web.xml裏面講這個listener 配置進去

 <listener>
        <listener-class>com.xxx.ecm.platform.gw.server.trace.SpringMVCConfiguration</listener-class>
      </listener>

同事,咱們看到裏面 引入了這個類:@Import({TracingBeansConfiguration.class})

TracingBeansConfiguration代碼以下:

@org.springframework.context.annotation.Configuration
public class TracingBeansConfiguration {
​
    @Value("${trace.app.name}")
    private String traceAppName;
​
    @Bean
    public Tracer jaegerTracer() {
        com.uber.jaeger.Configuration.SenderConfiguration senderConfiguration = new com.uber.jaeger.Configuration.SenderConfiguration();
        com.uber.jaeger.Configuration.ReporterConfiguration reporterConfiguration = new com.uber.jaeger.Configuration.ReporterConfiguration().withSender(senderConfiguration).withLogSpans(false).withMaxQueueSize(1000).withFlushInterval(100);
        com.uber.jaeger.Configuration.SamplerConfiguration samplerConfiguration = new com.uber.jaeger.Configuration.SamplerConfiguration().withType(ConstSampler.TYPE).withParam(1);
        com.uber.jaeger.Configuration configuration = new com.uber.jaeger.Configuration(traceAppName).withReporter(reporterConfiguration).withSampler(samplerConfiguration);
        return configuration.getTracer();
    }
​
    @Bean
    public List<HandlerInterceptorSpanDecorator> spanDecorators() {
        return Arrays.asList(HandlerInterceptorSpanDecorator.STANDARD_LOGS,
                HandlerInterceptorSpanDecorator.HANDLER_METHOD_OPERATION_NAME);
    }

此class的做用就是初始化兩個bean,Tracer bean和HandlerInterceptorSpanDecorator bean,以供SpringMVCConfiguration使用,

其中Tracer bean做用和配置和咱們使用spring boot相同,詳細配置請參考前文。

另外還須要把tracing filter 配置到配置文件:

<!-- tracing filter -->
      <filter>
        <filter-name>tracingFilter</filter-name>
        <filter-class>io.opentracing.contrib.web.servlet.filter.TracingFilter</filter-class>
        <async-supported>true</async-supported>
      </filter>
      <filter-mapping>
        <filter-name>tracingFilter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>

 

OK!你已經在springmvc配置好你的系統了!(spring-web版本要4.3.8.RELEASE以上)

 

 

端口號說明

咱們從前文中能夠看到,咱們安裝jaeger各個組件的時候使用了不少端口號,具體這些端口號都是些什麼做用呢?

下面將一一列舉其做用:

elasticsearch暴露以下端口

端口號 協議 功能
9200 HTTP 經過http協議鏈接es使用的端口
9300 TCP 經過tcp協議鏈接es使用的端口

 

agent 暴露以下端口

端口號 協議 功能
5775 UDP 經過兼容性 thrift 協議,接收 zipkin thrift 類型的數據
6831 UDP 經過二進制 thrift 協議,接收 jaeger thrift 類型的數據
6832 UDP 經過二進制 thrift 協議,接收 jaeger thrift 類型的數據
5778 HTTP 可用於配置採樣策略

collector 暴露以下端口

端口號 協議 功能
14267 TChannel 用於接收 jaeger-agent 發送來的 jaeger.thrift 格式的 span
14268 HTTP 能直接接收來自客戶端的 jaeger.thrift 格式的 span
9411 HTTP 能經過 JSON 或 Thrift 接收 Zipkin spans,默認關閉

query 暴露以下端口

端口號 協議 功能
16686 HTTP 1. /api/* - API 端口路徑 2. / - Jaeger UI 路徑

 

 

jaeger dependencies

完成安裝jaeger之後,你應該能夠在jaeger ui上看到效果了,你能夠採集到對應的數據,而且可以查詢到調用鏈路。可是你會發現search按鈕旁邊,還有一個dependencies選項,你點開確什麼也沒有。

此時你還須要安裝jaeger dependencies了,並且他須要定時執行,由於jaeger dependencies是在執行時去撈取對應的數據。

你能夠定時執行如下代碼:

STORAGE=elasticsearch ES_NODES=http://localhost:9200 java -jar jaeger-spark-dependencies.jar

ES_NODES爲前面安裝的es地址

jaeger-spark-dependencies.jar 怎麼來的?

你能夠搜索對應的資料下載,可是建議你下載官方源碼,本身打包,github地址以下:

https://github.com/jaegertracing/spark-dependencies

下載源碼執行mvn clean install -DskipTests打包,或許你能夠crontab定時執行腳本,來跑天天的數據

 

另外,你也可使用docker執行:

 docker run  --rm  --name  spark-dependencies  --env STORAGE=elasticsearch --env ES_NODES=http://localhost:9200 jaegertracing/spark-dependencies

ES_NODES爲前面安裝的es地址

固然,至於docker怎麼執行定時任務,或者Kubernetes怎麼執行CronJob,你能夠自行研究dokcer或Kubernetes相關的知識。固然,你能夠crontab定時執行腳本。

相關文章
相關標籤/搜索