- 介绍
- architeture
- 安装
- 配置
- 通过单个请求来Tracing
- 是不是必须经过Ingress才能有Tracing功能?
- RequestID格式不对造成无法Tracing
- ingress对requestID重写的解决办法
介绍
jaeger 是uber 的一个分布式链路追踪系统,根据 dapper 和 zipkin 演化过来的。
随着向微服务的架构迁移,问题主要集中在网络和可观察性上面。
jaeger 有以下功能:
分布式事务监控
性能和延迟最优化
根本原因分析
服务依赖分析
distributed context propagation
jaeger 的部署方式有很多种,可以单独二进制方式部署,也可以以 docker 的方式单独部署。下面看下与 k8s 部署的方式。
architeture
span: 一个 span 代表一个在 jaeger 中工作的一个逻辑单元,有一个 operation name,开始时间,和持续时间。
trace
这里只提供了直接存储到后端的图
官网还提供了一个用 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的格式不对造成的:
- ingress 注入sidecar时,requestID是 xxx-xxx-xxx 这种形式 rfc4122
如下是通过ingress访问productpage的包:
可以看到它的requestID 是 xxx-xxx-xxx这种形式,同时它有 x-b3-traceid的头,spanid这些。
spanID是productpage这个pod注入sidecar后,流量先经过sidecar转发,因为envoy集成了jaeger,在安装istio的时候启用了zipkin,因此会充当jaeger client的角色,生成一个traceID。
- ingress没有注入sidecar的包,此时requestID是 xxxxxxx这种形式,非标准
通过这个对比就明白了,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
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