Uber 的分布式跟踪 已翻译 100%

itfanr 投递于 10/09 15:05 (共 16 段, 翻译完成于 10-30)
阅读 1112
收藏 25
1
加载中

分布式跟踪正迅速成为许多组织用于监视复杂的基于微服务的架构的工具中必不可少的组件。在 Uber 工程团队中,我们的开源分布式跟踪系统 Jaeger 在整个 2016 年都实现了大规模的内部采用,集成到数百个微服务中,现在每秒能记录数千条记录。随着新一年的开始,这篇文章讲述我们如何得到下面的内容,从调查像 Zipkin 这样的现成解决方案,到为什么我们从 pull 架构切换到 push 架构,以及分布式跟踪将如何在 2017 年继续发展。

从 Monolith 到 Microservices

随着 Uber 的业务成倍增长,我们的软件架构复杂性也在增长。一年多以前的2015年秋天,我们有大约 500 个微服务。截至2017年初,我们有超过两千个微服务。这部分是由于越来越多的业务功能 —— 面向用户的业务功能,如 UberEATSUberRUSH —— 以及像欺诈检测、数据挖掘和地图处理等这样的内部功能。复杂性增加的另一个原因是从大型单片应用程序转向分布式微服务架构。

硅谷课堂
硅谷课堂
翻译于 10/16 09:58
0

像往常一样,进入微服务生态系统也给其带来了挑战。其中之一是进入系统可视化的丧失,以及服务之间不断产生的复杂交互。Uber的工程师们知道我们的技术对人们的生活有直接影响。系统的可靠性是首要的,但是没有可观测性也是不可能的。传统监控工具例如Metrics和分布式日志仍然占有一席之地,但是他们常常无法提供跨服务监控。这就是分布式跟踪崛起的原因。

跟踪Uber的起源

首个在Uber上广泛运用的跟踪系统名为Merckx,以当时世界上最快的自行车命名。Merckx可以快速回应Uber的整个Python后台的复杂查询。他可能会处理这样的查询:“查找请求超过两秒,只使用了特定数据库,而且事务持续打开了超过500ms的请求用户所登录的位置”。产生的分析数据会组织成一个由块组成的树,每个块代表了一个特定的操作或者远程调用,类似于OpenTracing API中“span”的概念。用户可以使用命令行工具对Kafka中的数据流进行ad hoc查询。他们也可以使用web UI界面来查看预定义摘要,它概括了API终端和Celery任务的高级行为。

Merckx将调用图建模为一个由块组成的树,其中每个块代表一个应用中的操作,例如一个数据库调用,一个RPC,或者甚至像解析JSON一样的库函数。

琪花亿草
琪花亿草
翻译于 10/22 11:40
0

Merckx 自动应用于 Python 中的许多基础架构库,包括 HTTP 客户端和服务端、SQL 查询、Redis 调用,甚至 JSON 序列化。检测记录了每个操作的某些性能指标和元数据,例如 HTTP 调用的 URL 或数据库调用的 SQL 查询。它还捕获了诸如数据库事务保持开放多长时间、访问了哪些数据库碎片和副本等信息。

Merckx 架构是一个对接到 Kafka 中的数据流的拉模型。

Merckx 的主要缺点在于,它的设计是针对优步单一 API 的时代。Merckx 缺乏任何分布式上下文的概念。它记录了 SQL 查询、Redis 调用,甚至是对其他服务的调用,但无法深入到一层级别以上。另一个有趣的 Merckx 限制是,许多高级特性(如数据库事务跟踪)实际上只在 uWSGI 下工作,因为 Merckx 数据存储在全局的、线程本地的存储中。一旦 Uber 开始采用 Tornado (用于 Python 服务的异步应用程序框架),线程本地存储就无法表示在 Tornado 的 IOLoop 上同一线程中运行的许多并发请求。我们开始意识到,在不依赖全局变量或全局状态的情况下,有一个可靠的机制来保持请求状态并正确传播请求状态是多么重要。

雪落无痕xdj
翻译于 10/28 07:59
0

接下来,在 TChannel 中跟踪

2015年初,我们开始开发一种用于 RPC 的网络多路复用和帧结构协议 TChannel 。协议的设计目标之一是将 Dapper 风格的分布式跟踪作为一等公民构建到协议中。为了实现这个目标,TChannel 协议规范跟踪字段定义为二进制格式的一部分。

spanid:8 parentid:8 traceid:8 traceflags:1

fieldtypedescription

spanid

int64当前 span的ID

parentid

int64前一个 span的ID

traceid

int64原始请求者赋予的ID

traceflags

uint8标识位

作为二进制格式的一部分出现在 TChannel 协议规范中的跟踪字段。

除了协议规范之外,我们还发布了几个开源客户端库,它们用不同的语言实现了协议。这些库的设计原则之一是具有请求上下文的概念,应用程序希望通过请求上下文从服务器端点传递到下游调用站点。例如,在 tchannel-go 中,使用 JSON 编码进行出站调用的签名需要上下文作为第一个参数:

func (c *Client) Call(ctx Context, method string, arg, resp interface{}) error {..}

雪落无痕xdj
翻译于 10/28 08:05
0

TChannel 库鼓励应用程序开发人员在编写代码时考虑到分布式上下文传播。

客户端库通过在线表示和内存上下文对象之间编组跟踪上下文,以及通过围绕服务处理程序和出站调用创建跟踪跨度,为分布式跟踪提供了内置支持。在内部,span 以一种几乎与 Zipkin 跟踪系统相同的格式表示,包括使用 Zipkin 特有的注释,例如 “cs” (客户机发送)和 “cr” (客户机接收)。TChannel 使用跟踪报告器接口将收集到的跟踪跨越进程发送到跟踪系统的后端。这些库附带了一个默认的报告器实现,它使用 TChannel 本身和 Hyperbahn (发现和路由层)以 Thrift 格式将跨度发送到收集器集群。

TChannel 客户端库让我们更加接近 Uber 需要的工作分发跟踪系统,提供了以下构建模块:

  • 进程间传播跟踪上下文,在请求内部管理

  • 记录跟踪跨度的工具 API

  • 跟踪上下文的进程内传播

  • 用于将流程外的跟踪数据报告到跟踪后端的格式和机制

雪落无痕xdj
翻译于 10/28 08:10
0

唯一缺失的部分是跟踪后端本身。跟踪上下文的格式和报告器使用的默认 Thift 格式都被设计成将 TChannel 与 Zipkin 后端集成起来非常简单。然而,当时将 span 发送到 Zipkin 的唯一方法是通过 Scribe ,而 Zipkin 支持的唯一性能数据存储是 Cassandra 。当时,我们对这两种技术都没有直接的操作经验,因此我们构建了一个原型后端,将一些定制组件与 Zipkin UI 结合起来,形成了一个完整的跟踪系统。

Tchannel 生成跟踪的原型后端体系结构是一个带有自定义收集器、自定义存储和开源 Zipkin UI 的 push 模型。

其他大科技公司如谷歌和 Twitter 的分布式跟踪系统的成功取决于 RPC 框架的可用性,Stubby 和 Finagle 分别在这些公司广泛使用。

雪落无痕xdj
翻译于 10/28 08:13
0

同样,在 TChannel 中开箱即用的跟踪功能是向前迈出的一大步。部署的后端原型立即开始接收来自几十个服务的跟踪。正在使用 TChannel 构建更多的服务,但是全面的生产部署和广泛的采用仍然存在问题。原型后端和基于 Riak/Solr 的存储在 Uber 的流量上存在一些问题,并且缺少一些查询功能来与 Zipkin UI 进行适当的互操作。尽管新服务迅速采用了 TChannel ,但 Uber 仍有大量的服务未将 TChannel 用于 RPC ;事实上,大多数负责运行核心业务功能的服务在没有 TChannel 的情况下运行。这些服务是用四种主要的编程语言 (Node) 实现的。使用各种不同的框架进行进程间通信。这种技术领域的异质性使得在 Uber 部署分布式跟踪比在谷歌和 Twitter 这样的地方更难。

在纽约建造 Jaeger

Uber NYC 工程组织成立于2015年初,有两个主要团队:基础设施方面的可观察性和产品方面的一切(包括 UberEATS 和 UberRUSH )。由于分布式跟踪是生产监视的一种形式,因此它非常适合于可观察性。

雪落无痕xdj
翻译于 10/28 08:16
0

我们组建了分布式跟踪团队,有两个工程师和两个目标:将现有的原型转化为完整的生产系统,让所有的 Uber 微服务都可以使用和采用分布式跟踪。我们还需要项目的代码名。命名事物是计算机科学的两大难题中的一个,因此我们花了几周的头脑风暴和跟踪的主题,侦探,和打猎,直到我们选定了这个名字 Jaeger(ˈyā-gər),德国的猎人或狩猎服务员。

NYC 团队已经有了运行 Cassandra cluster 的操作经验,而 Cassandra cluster 是 Zipkin 后端直接支持的数据库,因此我们决定放弃基于 Riak/Solr 的原型。我们重新实现了 Go 中的收集器,以接受 TChannel 通信量,并将其以与 Zipkin 兼容的二进制格式存储在 Cassandra 中。这使我们可以使用 Zipkin web 和查询服务,而无需进行任何修改,还提供了通过自定义标记搜索跟踪的缺失功能。我们还在每个收集器中构建了一个动态可配置的乘法因子,以便将入站流量乘以n倍,以便用生产数据对后端进行压力测试。

早期的 Jaeger 架构仍然依赖于 Zipkin UI 和 Zipkin 存储格式。

雪落无痕xdj
翻译于 10/28 08:20
0

在业务上排第二的是让跟踪有效,这样,所有存在的服务就不能使用由 RPC 支持的 T通道。在这之后,我们花费了几个月的时间来建立了客户端侧的库,这些库支持 Go, Java,Python 和 Node.js 来监控任意的服务(包括基于 HTTP 的服务)。尽管 Zipkin 后端相当知名且受欢迎,但它在监控方面缺乏一个好的案例,特别是在Java / Scala 生态系统之外。我们还考虑过各种开源的监控库,但是他们是由不同的人维护的,无法保证线上的互操作性,还通常使用完全不同的API,并且大多数要求使用 Scribe 或 Kafka 作为中介传输。我们最终决定编写自己的库,这些库将进行集成测试,以实现互操作性,支持我们所需的传输,最重要的是,提供不同语言的一致的监控API。我们构建了所有客户端库,以便从一开始就支持 OpenTracing API。

溪边九节
溪边九节
翻译于 10/22 10:43
0

我们在客户端库的最初版本中构建的另一个新特性是能够轮询跟踪后端以获得抽样策略。当一个服务接收请求,没有跟踪的元数据,跟踪器通常通过生成一个新的随机跟踪ID开始一个新的跟踪请求。然而,大多数生产跟踪系统,特别是那些需要处理超级的规模,不会配置文件或记录在存储每一个跟踪。这样做会导致从服务到跟踪后端的流量非常大,可能比服务处理的实际业务流量大几个数量级。相反,大多数跟踪系统只采样少量的跟踪,只对这些采样的跟踪进行概要和记录。做出抽样决策的精确算法就是我们所说的抽样策略。抽样策略的例子包括:

  • 采样一切。这对于测试很有用,但是在生产中很昂贵!

  • 一种基于概率的方法,对给定的轨迹以一定的固定概率随机抽样。

  • 一种速率限制方法,其中每时间单元采样X条轨迹。例如,可以使用漏桶算法的变体。

雪落无痕xdj
翻译于 10/28 08:23
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(0)

返回顶部
顶部