Istio多集群链路追踪实践

2022-11-28 17:29:56 浏览数 (1)

为了实现多集群的流量治理,我们采用Istio官方提供的多主集群进行Istio的部署,这样就出现一个问题,对于多主集群的Istio治理,如何进行跨集群的流量监控,实现跨集群的服务链路追踪。

Istio作为服务网格在云原生下服务网格的事实标准,宣称有三大功能:流量管理、可观测、安全能力,可观测独占一席,可见可观测对于Istio本身的重要程度。可观测主要包含三个方面:指标、日志、全链路追踪。

作者:李运田, 中国移动云能力中心软件开发工程师,专注于云原生、Istio、微服务、Spring Cloud 等领域。

01

单集群链路追踪

在分布式链路跟踪中有两个重要的概念:Trace和Span。Trace是请求在分布式系统中的整个链路视图,Span则代表整个链路中不同服务内部的视图,Span组合在一起就是整个Trace的视图。在整个请求的调用链中,请求会一直携带TraceId往下游服务传递,每个服务内部也会生成自己的SpanId用于生成自己的内部调用视图,并和TraceId一起传递给下游服务。TraceId在请求的整个调用链中始终保持不变,所以在日志中可以通过TraceId查询到整个请求期间系统记录下来的所有日志。请求到达每个服务后,服务都会为请求生成SpanId,而随请求一起从上游传过来的上游服务的 SpanId会被记录成parent-SpanId。当前服务生成的SpanId随着请求一起再传到下游服务时,这个SpanId又会被下游服务当做 parent-SpanId记录。

Istio 通过Envoy完成全链路追踪所需的数据生成。也就是说Istio中实现链路追踪功能的是通过Envoy实现,Envoy 会为其所代理的程序自动地生成全链路追踪中的Span,但是要想自己的服务实现全链路追踪并不止是通过在服务中注入Sidecar就可以实现,也依赖于服务把生成的Header传递给流出的请求。在开始我们使用Springboot框架进行多个服务之间的调用,服务注入Sidecar后,通过观察发现Jaeger展示的链路追踪中每个服务都生成了一个TraceId,后来发现没有把生成的Header进行传递,通过在服务中添加一个拦截器实现Header的传递,这样在Jaeger的链路中即可看到整个链路信息。

02

多集群链路追踪

Istio中多集群的链路追踪是基于Istio的多集群能力实现,部署服务到多个集群,通过Istio的能力进行多集群统一服务治理,Istio只是在上层进行配置的修改,在部署的时候每个集群会单独部署Jaeger和ES,为实现多集群链路追踪,需要修改配置。

1、修改每个集群的Istio configmap

Istio的链路追踪通过zipkin直接传输到Jaeger-collector,Jaeger-collector将数据发送到自己现有的ES集群进行存储,Jaeger-query直接去现有ES集群查询。链路追踪发送到Jaeger-collector,如果不在同个namespace下,需要修改zipkin地址为Jaeger的zipkin收集地址。Envoy这个proxy会默认使用环境变量来设置zipkinAddress地址,默认地址是zipkin.istio-system:9411,可在yaml下查找zipkinAddress来修改。

2、修改链路追踪存储

Istio的链路追踪通过zipkin直接传输到Jaeger-collector,Jaeger-collector将数据发送到自己现有的ES集群进行存储,Jaeger-query直接去现有ES集群查询,修改每个集群的Jaeger-collector、Jaeger-query中的es.server-urls为同一个ES地址,使得所有集群中的链路追踪信息进行统一保存。

3、在每个集群中通过Jaeger-query查询到的多集群链路追踪信息即为全链路追踪信息

当client:8070调用provider:8090时,整个链路中会使用同一个TraceId,并把链路信息保存到同一个ES中,最后Jaeger从每个集群中都可以获取到整个服务链路的调用信息。

03

多集群链路追踪结果

在我们的测试中,使用client:8070调用provider:8090,通过上述配置后,可以在Jaeger中得到正常的多集群链路追踪信息,并且每个集群中的Jaeger得到的链路追踪信息一致。

打开每个Span可以看到具体的服务链路调用信息。

在我们探索之初,每个集群的Jaeger使用的ES都是各个集群中单独部署的,虽然Istio是多主模式部署,但是并不会在每个集群中汇总服务的链路追踪信息,这样每个集群中的Jaeger获取的链路追踪都是各个集群中的服务信息,如果服务出现了跨集群调用,那么服务的链路追踪就不完整。

有的链路有一个Span,有的链路有两个Span,可以打开链路追踪查看详细的内容

04

实现原理

服务网格就像微服务的通信层,服务之间的所有通信都是通过网格进行的。它可以实现负载均衡、服务发现、流量转移、速率限制、指标(metrics)收集等功能,Envoy就是这样的一个服务网格。在我们的例子中,Envoy将帮助我们生成唯一根请求id (x-request-id),生成子请求id,并将它们发送到Jaeger或Zipkin这样的追踪系统,这些系统存储、聚合追踪数据并为其提供可视化的能力。

Envoy主要用三个功能来支撑系统范围内的跟踪

1)生成Request ID: envoy会在需要的时候生成UUID,并操作名为[x-request-id]的HTTP Header。应用可以转发这个Header用于统一的记录和追踪。

2)集成外部追踪服务: envoy支持可插件的外部跟踪可视化服务。目前支持LightStep、zipkin或者Zipkin兼容的后端(例如:jaeger).另外可自己添加其它的追踪服务。

3)客户端跟踪ID连接:x-client-trace-id Header可以用来把不信任的请求ID连接到受信的x-request-id Header上。

当我们调用client:8070后,注入到client:8070的Envoy会根据请求生成TraceId和SpanId。

Envoy的全链路追踪的实现,主要是三部分,一部分是通过Tracing driver抽象类实现的各种对接Tracing系统的驱动,一部分是负责管理根据配置创建的各种Tracer的TracerManager,还有一部分则主要在HTTP Connection Manager实现上,用于在不同的阶段生成、补充、提交相关的Span。

1.在HTTPConnectionManager的配置对象初始化时,会根据配置从TracerManager中获取Tracer实例。

代码语言:javascript复制
if (config.has_tracing()) {
http_tracer_ = http_tracer_manager.getOrCreateHttpTracer(getPerFilterTracerConfig(config));
const auto& tracing_config = config.tracing();
Tracing::OperationName tracing_operation_name;
...
}

2.当HTTP Connection Manager解码请求的Header完成时,会通过Tracer实例的startSpan函数创建Span,这个Span将存在于当前Manager实例的active_span_字段上,随后HTTP Connection Manager的流程中,会根据active_span_的存在与否选择性地向 Span 上记录信息。

代码语言:javascript复制
if (connection_manager_.config_.tracingConfig()) {
traceRequest();
}
filter_manager_.decodeHeaders(*request_headers_, end_stream);
resetIdleTimer();
}
void ConnectionManagerImpl::ActiveStream::traceRequest() {
const Tracing::Decision tracing_decision =
Tracing::HttpTracerUtility::shouldTraceRequest(filter_manager_.streamInfo());
ConnectionManagerImpl::chargeTracingStats(tracing_decision.reason, connection_manager_.config_.tracingStats());
active_span_ = connection_manager_.tracer().startSpan(
*this, *request_headers_, filter_manager_.streamInfo(), tracing_decision);
if (!active_span_) {
return;
}
...
}

3.Envoy生成完相关TraceId和SpanId后,能够自动发送Span,但是用户自己的业务代码需要处理如下HTTP请求头信息,这样才能把多个Span正确的关联到同一个追踪上,我们使用的是Zipkin,Envoy 要传播的是如下B3 Header。

代码语言:javascript复制
• x-request-id
• x-b3-traceid
• x-b3-spanid
• x-b3-parentspanid
• x-b3-sampled
• x-b3-flags
• x-ot-span-context

比如我们的代码里通过Springboot拦截器进行Header的传递,Bookinfo中review在调用rating时通过如下方式处理Header。

05

总结

Istio服务网格在入口流量和出口流量上都做了代理,Istio中实现链路追踪功能的是通过Envoy实现,Envoy 会为其所代理的程序自动地生成全链路追踪中的Span,但是要想自己的服务实现全链路追踪并不止是通过在服务中注入Sidecar就可以实现,也依赖于服务把生成的Header传递给流出的请求,通过在多集群中进行配置,即可实现多集群链路追踪。

参考链接:

[1]https://istio.io/latest/docs/setup/install/multicluster/multi-primary/

0 人点赞