Knative Tracing 介绍

前言

为了实现系统组件的水平扩展、敏捷开发、CD(持续集成)、解耦等各类诉求,现代的大型软件架构每每都是微服务架构。 微服务架构自己会面临一系列诸如:后台错误缘由分析、各个微服务组件的调用状况诊断等。因此须要有一个 Tracing 系统解决这些问题。javascript

除了微服务之外 Serverless 应用也面临一样的问题。一个完整的业务实现想要基于 Serverless 模型来开发的话可能会分解成多个 Serverless 模块,每个模块单独经过 Knative 的 Serving 部署,那么这些不一样的 Serving 之间就须要调用链进行事务的串联。php

而 Knative 自己没有 Tracing 的设计,须要借助 ServiceMesh 层来实现 tracing。对于开源的 Knative 来讲 ServiceMesh 层其实就是 Istio。说到 Istio 的 Tracing 就要提到 OpenTracing 了。OpenTracing 如今已经成了云原生的 Tracing 标准。因此本文咱们从 OpenTracing 到 Istio 再到 Knative 来层层分析看看 Knative 应用应该如何使用 Tracing。前端

为何须要 Opentracing

目前流行的分布式跟踪系统 Zipkin、Jeager 等。若是这些 Tracing 系统使用不兼容的 API 或者他们对开发语言支持不友好那么就会给微服务开发带来困难。java

OpenTracing 经过提供平台无关、厂商无关的 API,使得开发人员可以方便的添加(或更换)追踪系统的实现。 OpenTracing 还提供了流行开发语言的 SDK,这样就很是方便非服务的 tracing 实现。经常使用的开发语言 SDK 以下:python

什么是 tracing

首先咱们要搞清楚什么是 Trace,在前面的介绍中咱们提到 Trace 是为了解决微服务调用的一些链路追踪等问题,因此 Trace 应该是可以表达一个事务在各个微服务中的执行过程。git

因此一个 Trace 表明了一个事务或者流程在系统中的执行过程。在 OpenTracing 标准中,Trace 是由多个 Span 组成的一个有向无环图,每个 Span 表明事务的一个执行节点或者片断,每个 Span 都是有名字而且有起止时间的。github

分布式追踪中的每一个组件都一个或者多个 Span。例如,在一个常规的 RPC 调用过程当中,OpenTracing 推荐在 RPC 的客户端和服务端,至少各有一个 Span,用于记录 RPC 调用的客户端和服务端信息。apache

Causal relationships between Spans in a single Trace

        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C is a `ChildOf` Span A)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)

如上所示是一个由 8 个 Span 组成的完整的 Trace 调用流程。swift

关于 Span 有两个主要的关系须要解释一下:ChildOf 和 FollowsFrom,从上面的示意图中看起来都是前面的 Span 触发后面的 Span,从图上出不出区别。但实际区别仍是很大的。ruby

  • ChildOf

表示父子关系。意思是父 Span 须要等待子 Span 结束才能结束。因此子 Span 会 block 父 Span 的执行。如上所示的 [Span A] 必须等待 B 和 C 都结束本身才算结束,同理 C 也要等待 E 和 F 都结束才行。

  • FollowsFrom

表示兄弟关系。兄弟关系和父子关系的根本区别就是不须要等待兄弟的结束。好比上面的 F 和 G 的关系就是 F 结束以后触发的 G,因此 F 要先于 G 结束,不须要等待 G。

ChildOf 有点儿像是函数调用,父函数须要等待子函数结束。FollowsFrom 有点儿像是 Goroutine 父 goroutine 并不须要等待子 goroutine 的结束,二者各自的生命周期是独立的。

上图能够很好的表示各组件的组合关系,但没法表示组件的调用时间、是串行调用仍是并行调用。因此通常的 Trace 实现都会用以下所示的方式展现 Trace 的调用过程

Temporal relationships between Spans in a single Trace


––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time

 [Span A···················································]
   [Span B··············································]
      [Span D··········································]
    [Span C········································]
         [Span E·······]        [Span F··] [Span G··] [Span H··]

Istio Tracing

Istio 自己是基于 OpenTracing 调用链追踪,而且 ZipKin 和 Jeager 两种实现均可以用于 Istio。

上图是 Istio 官方例子中的 Bookinfo 应用的例子。Bookinfo 应用由以下四个模块组成

  • Product page

Python 语言开发,用来展现书本信息

  • Reviews

Java 语言开发,接收 Product page 的请求,返回评论信息,同时也调用 Ratings 微服务。Reviews 有三个版本:v一、v二、v3,这个三个版本在响应 Product page 的请求时各有不一样:v1 的响应没有评分等级、v2 的响应包含黑色的评分等级、v3 的响应包含红色的评分等级

  • Ratings

Nodejs 语言开发,接受 Reviews 的请求,返回评分信息

  • Details

Ruby 语言开发,接受 Product page 的请求,返回书本详细属性

从前端入口 Gateway 那个 Envoy 上进行一次调用,到四个不一样语言开发的服务间完成调用,一次调用输出的 Tracing 多是这样:


如上图所示有四个 Span:

  • product page Span
  • detail Span
  • reviews Span
  • rationgs Span

并且这四个 Span 构成了一个调用链,也就是说 Ratings Span 知道本身的父 Span 是 reviews,reviews Span 也知道本身的父 Span 是 product page。那么问题来了 reviews Span 和 ratings Span 是怎么知道本身的父 Span 的呢?

咱们知道 Istio 是经过 Sidecar 的方式接管流量的,对于 Sidecar 里面的 Envoy 来讲是没有办法判断进来的哪一个请求和出去的哪一个是一个事务。

咱们看一下 Istio bookInfo 的代码 https://github.com/istio/istio/blob/master/samples/bookinfo/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java#L146 就能发现其实这个 tracing 的完整流程是须要应用程序参与的。

对于上图所示对于 HTTP 请求需求在 Header 中把 Tracing 的信息获取出来而且在调用下游服务的时候还须要继续传递下去。也就是说 Istio 只是负责接管流量而且建立 Span,可是微服务 Span 之间的衔接是须要应用程序来配合的。

Knative Tracing

了解了 Istio Tracing 的原理咱们就比较清楚 Knative 应该的玩法了。Knative Service 部署的应用若是有多个微服务模块,那么微服务模块之间的完整调用链的衔接就须要应用程序来完成。

对于 HTTP 协议而言就须要应用程序提取接受到的请求的 Tracing Header 中的信息而且传递到款下一个微服务的调用中。

另外虽然 OpenTracing 是一个通用的标准,可是 OpenTracing 并无规定 HTTP Header 应该如何设置。也就是说虽然 Istio 支持 Zipkin 和 Jeager 两种 Tracing 实现,可是这两种是不能混用的,由于这两种实现的 Header 设置是不同的。不过 Jeager 设置一下也是能够支持 ZipKin 标准的(参考这里),因此咱们的应用程序开发的时候也要想好是基于 Zipkin 的规范实现仍是基于 Jeager 的规范实现。

小结

  • OpenTracing 是一个通用的 Tracing 标准,可是标准规定的边界还有点儿窄,还有一些地方没有标准
  • Istio Tracing 是须要应用程序一块儿配合实现的,不然就是一堆杂乱的 Span 堆在一块儿,没法串联整个事务链
  • Istio Envoy 默认是基于 apache/ncubator-zipkin-b3-propagation 标准实现的 HTTP Header 注入,建议应用程序开发也使用这个标准,这也是 ZipKin 遵循的标准
  • Knative Service 部署的服务须要主动参与 Tracing 的实现,不然没法完成这个调用链的串联。因此若是说 Tracing 是 Serverless 必不可少的一部分,那么 Knative Service 部署的应用有其余依赖的微服务模块的话就必需要主动参与 Tracing 的实现


原文连接 本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索