作者:Alex Leong
我们很高兴地宣布Linkerd 2.6增加了对分布式跟踪的支持!这意味着Linkerd数据平面代理,现在可以发出跟踪跨度(span),允许你查看请求在跟踪请求的Linkerd代理中花费的确切时间。由于在实践中使用分布式跟踪是相当困难的,在这篇文章中,我们收集了一个参考架构,并推荐了使用Linkerd进行分布式跟踪的最佳方法。
概述
跟踪可以成为调试分布式系统性能的宝贵工具,特别是在确定瓶颈和了解系统中每个组件的延迟成本方面。如果你还不熟悉分布式跟踪背后的思想,那么“Distributed Tracing for Polyglot Microservices”可以很好地概述这些概念。分布式跟踪的承诺是令人兴奋的,但是根据我们的经验,在实践中实现这些承诺有很大的障碍。
https://linkerd.io/2016/05/17/distributed-tracing-for-polyglot-microservices/
首先,分布式跟踪生态系统非常复杂。它包括一系列令人眼花缭乱的项目,如Zipkin、Jaeger、OpenTracing、OpenCensus、OpenTelemetry,以及许多许多其他项目,每个项目都有部分重叠的功能集。有一些复杂的矩阵,其中项目可以与其他项目以何种方式进行互操作。
https://opencensus.io/feature_matrix/
更糟糕的是,服务网格的添加增加了决策的另一层复杂性。使用服务网格和使用分布式跟踪在它们提供的功能上有重叠,例如在绘制应用程序拓扑的能力上。另外,虽然服务网格的大多数特性不需要更改代码,但对于分布式跟踪来说则不是这样。我的Linkerd维护者同事,William Morgan,早前写过一篇关于这个问题的博客文章,题为“Distributed tracing in the service mesh: four myths”。
https://linkerd.io/2019/08/09/service-mesh-distributed-tracing-myths/
我们看到在Linkerd社区的困惑,这一点对我们来说很重要,不仅“添加分布式跟踪”到Linkerd 2.6就可以收工了,但也提供具体建议如何Linkerd赋能应用程序可以使用这个特性。因此,参考架构如下。
Linkerd 2.6中的分布式跟踪
首先,让我们了解一下Linkerd中的“分布式跟踪支持”到底是什么样子。它实际上非常简单:当Linkerd数据平面代理在代理的HTTP请求中,看到b3格式的跟踪头时(请参阅下文了解为什么使用这种特殊格式),Linkerd将为该请求发出一个跟踪跨度。这个跨度将包括关于在Linkerd代理上花费的确切时间量的信息,以及将来可能包含的其他信息。
就是这样。如你所见,Linkerd在分布式跟踪中的角色实际上非常简单。复杂性在于,为了使Linkerd的这个特性有用而必须准备的所有其他东西。
还需要什么?要使用Linkerd的新分布式跟踪功能,你需要在你的系统中添加几个额外的组件:
演示!
让我们看看分布式跟踪在我们的参考架构中是如何工作的。然后,我们将更详细地描述每个组件,并解释如何在自己的应用程序中使用这些组件。
确保Linkerd 2.6 CLI可用,并在集群中安装Linkerd 2.6。如果没有,可以按照安装或升级说明操作。
$ linkerd version
Client version: stable-2.6
Server version: stable-2.6
首先克隆参考架构库:
git clone git@github.com:adleong/emojivoto.git && \
cd emojivoto
接下来我们安装Jaeger和OpenCensus收集器。向这些组件注入Linkerd非常重要,这样它们就可以通过安全连接,接收来自Linkerd代理的跨度。
linkerd inject tracing.yml | kubectl apply -f -
最后,我们安装Nginx ingress控制器和Emojivoto应用程序本身。因为我们将这些组件注入了Linkerd,所以我们将能够在结果跟踪中看到Linkerd代理本身。
linkerd inject emojivoto.yml | kubectl apply -f - && \
linkerd inject ingress.yml | kubectl apply -f -
有了所有这些,我们可以使用Jaeger仪表板来探索流经系统的跟踪。
kubectl -n tracing port-forward deploy/jaeger 16686 &; \
open http://localhost:16686
完整的跟踪
Linkerd分布式跟踪参考架构
这个参考架构肯定不是为你的应用程序获得分布式跟踪的唯一方法,根据你的应用程序及其需求,它甚至可能不是最好的方法,但它是一个很好的起点,无论是否使用服务网格都可以很好地工作。
参考架构有四个组件:用于ingress的Nginx、用于客户端库的OpenCensus、用于跟踪收集器的OpenCensus和用于后端的Jaeger。我们将更详细地描述这些组件。当然,每个组件都是可替换的 — 我们在下面详细描述了为每个组件替换不同选项的要求。
入口:Nginx
对于分布式跟踪来说,入口(ingress)是一个特别重要的组件,因为它创建每个跟踪的根跨度,并负责决定是否对该跟踪进行采样。让入口做出所有的采样决定,可以确保要么对整个跟踪进行采样,要么不进行采样,并避免创建“部分跟踪”。
分布式跟踪系统都依赖于服务将关于当前跟踪的元数据,从它们接收的请求传播到它们发送的请求。这种元数据称为跟踪上下文,通常编码在一个或多个请求头中。有许多不同的跟踪上下文头格式,虽然我们希望生态系统最终会集中于开放标准,如W3C tracecontext,但我们今天只使用b3格式。作为最早广泛使用的格式之一,它拥有最广泛的支持,特别是在像Nginx这样的入口。
这个参考架构包括一个简单的Nginx配置,它对50%的跟踪进行采样并将跟踪数据发送给收集器(使用Zipkin协议)。任何入口控制器都可以在这里代替Nginx使用,只要它:
客户端库:OpenCensus
虽然服务可以手动传播跟踪传播头信息,但通常使用库要容易得多,库可以做三件事:
我们建议在你的服务中使用OpenCensus,并将其配置为:
OpenCensus代理导出程序将通过gRPC API,将跟踪数据导出到OpenCensus收集器。如何配置OpenCensus的详细信息将根据语言的不同而有所不同,但是有许多流行语言的指南。在我们的示例应用程序Emojivoto中,你可以看到一个端到端的示例。
https://github.com/adleong/emojivoto
你可能会注意到OpenCensus项目处于维护模式,将成为OpenTelemetry的一部分。不幸的是,OpenTelemetry方法还没有准备好,所以OpenCensus目前仍是我们的推荐方法。
收集器:OpenCensus
OpenCensus收集器从OpenCensus代理导出程序接收跟踪数据,并可能在将数据发送给Jaeger之前进行转换和过滤。让OpenCensus导出程序发送到OpenCensus收集器给我们带来了很大的灵活性:我们可以切换到OpenCensus支持的任何后端,而不需要中断应用程序。
后端:Jaeger
Jaeger是最广泛使用的跟踪后端之一,而且理由很充分:它易于使用,并且在可视化跟踪方面做得很好。但是,可以使用OpenCensus支持的任何后端。
Linkerd
如果你的应用程序被注入了Linkerd,Linkerd代理将参与跟踪,并将跟踪数据发送到OpenCensus收集器。这丰富了跟踪数据,并允许你准确地查看请求在代理和连接上花费的时间。使Linkerd能够参与:
虽然Linkerd只能积极参与使用b3传播格式的跟踪(如上面的参考架构),但Linkerd将始终透明地转发未知的请求头,这意味着它将永远不会干扰使用其他传播格式的跟踪。我们还希望扩展Linkerd对更多传播格式的支持。如果你对此感兴趣,请开启一个问题(或拉取请求!)。
https://github.com/linkerd/linkerd2/issues
https://github.com/linkerd/linkerd2/pulls
总结
希望这个参考架构使你更容易理解分布式跟踪的不同移动部分,并开始检测你的应用程序。虽然上面的参考架构并不是为你的应用程序获得分布式跟踪的唯一方法,但是我们希望它能够成为你探索的良好起点。如果没有,请让我们知道!