Think Deep,Work Lean

jaeger讲解

Posted on By zack

介绍

官网

jaeger 是uber 的一个分布式链路追踪系统,根据 dapper 和 zipkin 演化过来的。
随着向微服务的架构迁移,问题主要集中在网络和可观察性上面。
jaeger 有以下功能:

分布式事务监控

性能和延迟最优化

根本原因分析

服务依赖分析

distributed context propagation

jaeger 的部署方式有很多种,可以单独二进制方式部署,也可以以 docker 的方式单独部署。下面看下与 k8s 部署的方式。

architeture

span: 一个 span 代表一个在 jaeger 中工作的一个逻辑单元,有一个 operation name,开始时间,和持续时间。

trace

这里只提供了直接存储到后端的图 jaeger-architecture-v1

官网还提供了一个用 kafka 作为缓存的架构图,这里省略。

agent: jaeger agent 是个通过 udp 通信的、监听 spans 的网络 daemon,按批次发给 collector。作为一个基础设施的组件,需要在所有的 Host 上安装。将 client 的路由发现上报给 collector。

collector: 从 jaeger agent 接收 traces,并且以流水线的形式来运行。pipeline 验证 traces,并给它们建索引,转换并最终存储。

Query: 是一个查询traces 的服务

Ingress: 它是从 kafa topic 接收消息,并将它写入到其他的存储后端。

安装

配置

采样

采样配置有几种策略可以配置,可以设置Tracing全部/部分/随机,sampling 配置

kubectl -n istio-system get cm jaeger-sampling-configuration -oyaml 
  ...
  sampling: '{"default_strategy":{"param":1,"type":"probabilistic"}}'  # 表示随机采样,但是比例是100%

使用内存来存储Tracing数据

Jaeger默认依赖eleasticSearch,通过修改Jaeger的配置,让它解决依赖,使用内存存储。

 kubectl -n istio-system patch jaeger jaeger -p '{"spec": {"strategy":"allInOne"}}' --type=merge

需要删除Jaeger这个cr中关于elasticSearch的所有配置,否则健康检查失败会导致失败。

通过单个请求来Tracing

如果一个请求需要被Jaeger Traing,那么这个请求一定需要一个X-Request-ID Header。且在该请求访问链中所有的工作负载都需要注入sidecar,将该ID记录到Jager。如下:通过curl指定Header中的requestID,页面上就能看到这个request的Tracing:

curl -H 'X-Request-ID: 56955ffa-4000-9e1e-bd53-50a41b825ea0' 'ssa2:31234/productpage?u=test'

如果这个服务注入了sidecar,流量先经过istio-proxy会自动产生一个X-Request-ID,不用手动指定。

是不是必须经过Ingress才能有Tracing功能?

Tracing功能并非一定需要一个Ingress,只不过kubernetes ingress controller默认会给一个请求生成requestID, 配置参数,如果需要另外一些参数,如增加X-Forward-For等,可以在ingress controller configmap的data中设置。

kubectl -n kubesphere-controls-system edit cm kubesphere-router-xxx
...
data:
use-forwarded-headers: "true"

但是请求经过nginx后,默认给它加上的X-Request-ID非标准形式,因此不能被Tracing;解决方法是给该网关注入sidecar。因为流量先经过istio-proxy容器,自动加上标准形式的requestID,再经过nginx容器时,因为请求头中已经有了requestID,因此就不会使用nginx生成的非标准requestID。

总结:如果要被Tracing,要么不用nginx ingress网关,直接访问第一个服务URL;如果要用网关,网关一定要注入sidecar

RequestID格式不对造成无法Tracing

在使用istio sidecar集成Jaeger的时候,留下一个疑问:

注入sidecar ingress controller Tracing是整个链路的,但是如果只是通过入口服务来访问,带上X-Request-ID,只能是这一个服务有Tracing;

不注入sidecar ingress controller访问productpage时,Header中也带了request-id,但是Productpage却没有Tracing,为什么?

通过抓包分析能看出来原因为request-id的格式不对造成的:

  1. ingress 注入sidecar时,requestID是 xxx-xxx-xxx 这种形式 rfc4122

如下是通过ingress访问productpage的包:

jaeger2

可以看到它的requestID 是 xxx-xxx-xxx这种形式,同时它有 x-b3-traceid的头,spanid这些。

spanID是productpage这个pod注入sidecar后,流量先经过sidecar转发,因为envoy集成了jaeger,在安装istio的时候启用了zipkin,因此会充当jaeger client的角色,生成一个traceID。

  1. ingress没有注入sidecar的包,此时requestID是 xxxxxxx这种形式,非标准

jaeger3

通过这个对比就明白了,requestID的形式决定的。request id 是 nginx 默认的 $req_id 不符合 rfc4122 的规范所以不会被 jaeger 收集。对应的issue

skywalking是sw8的头(XXXXX-XXXXX-XXXX-XXXX),标准请求头,可以被Tracing

问题:为什么ingress注入sidecar跟不注入sidecar x-request-id形式不一样呢?

当请求访问Ingress时,会先经过一个sidecar istio-proxy container后,产生一个标准ID(xxx-xxx-xxx)。请求转发至ingress controller的container,此时请求已经有了RequestID,因此不会被nginx覆盖。配置文件如下:

nginx配置文件

# kubectl -n kubesphere-controls-system exec kubesphere-router-xxx -- cat /etc/nginx/nginx.conf

# Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server.
# If no such header is provided, it can provide a random value.
map $http_x_request_id $req_id {  # http_x_request_id 是请求进来的requestId
  default   $http_x_request_id; # 默认使用请求头中的requestID,即将http_x_request_id的值赋给req_id

  ""        $request_id; # http_x_request_id是空,那么使用 request_id,request_id是Nginx默认的自带的变量,一个16位比特的随机数,用32位的16进制数表示。

...
}
    proxy_set_header X-Request-ID           $req_id;

问题2:为什么单独访问productpage服务,只有一个trace?

原因是没有加后缀,单独请求productpage的pod IP加端口只有一个Trace,原因是只访问了这一个应用。如果要访问完整链路,需要访问完整PATH。

# kubectl -n test2 get po -owide | grep product
productpage-v1-844495cb4b-7ftgk   2/2     Running   0          143m   10.233.64.219   ssa3   <none>           <none>
# watch -n0.1 -d 'curl 10.233.64.219:9080/productpage?u=test'  # 完整PATH: /productpage?u=test

jaeger4

ingress对requestID重写的解决办法

社区碰到一个问题: openresty -> nginx/istio -> pod, 如果遇到 openresty 传入 X-Request-ID ,之后 tracing 无数据 或者 openresty 传入的 uuid 在 tracing 里 搜索不到的情况,作者通过加上envoyfilter解决:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: request-id-settings
  namespace: kubesphere-controls-system
spec:
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      context: ANY
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
    patch:
      operation: MERGE
      value:
        typed_config:
          '@type': type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          preserve_external_request_id: true
  workloadSelector:
    labels:
      app: kubesphere
      component: ks-router
      project: system
      tier: backend