首页 > 其他分享 >Istio可观测性(链路)

Istio可观测性(链路)

时间:2023-01-19 10:55:14浏览次数:47  
标签:Trace header b3 Istio 观测 headers 链路 日志

可观测性的英文是 Observability,这是伴随着云原生技术发展产生的一个新兴词汇,在传统的 IT 中,并没有这种说法。简单来说,可观测性是通过系统输出信息到外部,以检测系统内部的运行状态Trace,通过内部打点的方式串联起微服务的各个组件。Metrics,通过输出服务的 Metrics 信息,达到外部监测的目的

链路追踪:通过在程序内打点记录日志的方式,记录每次请求的调用链路信息。特点是数据精准、细致,适合查看某一次请求的调用链路,一般用于查看某些响应较慢的接口瓶颈

监控指标:主要是用时序性数据库记录每个时间点的监控数据,一般通过主动拉取服务 Metrics 数据的方式记录,然后实时计算一段时间的数据,并通过图形界面的方式展现出来。它的特点是实时性强、可观测指标丰富,适合查看一段时间内的指标趋势

日志:日志是比较传统的可观测性组件了,无论是在单体服务时代还是微服务时代,我们都会用日志排查问题。日志的特点是数据比较离散,之间没有关联。当然,可以通过在日志中打印 TraceId 和链路追踪关联起来。一般日志要通过日志收集系统使用,比如常见的 ELK 日志系统。

在微服务架构中,随着服务和中间件数量变多,往往一个接口要请求几十次服务和上百次 DB 才能返回数据,链路过长,很难定位到底是哪个环节出了问题;又或者某个接口延时过高,也很难排查到底是链路中的哪个环节出了问题,这个时候就需要链路追踪系统帮忙了。

Trace 链路追踪原理

链路追踪系统基本源于 Google 的一篇 Dapper 论文,这篇论文详细解释了链路追踪的实现原理。

Dapper 通过一个全局唯一的 TraceId 表示请求调用链,并定义了 span,span 表示一次调用(可以是远程调用,也可以程序内的函数调用)。每个 span 包含了两个重要信息,一个是当前 SpanId,另外一个就是 ParentSpanId。

如何将 Trace 所需的信息传递给被调方服务呢?答案就是通过 HTTP 的 header 头传递下去,当然如果是其他协议,比如 Dubbo,就要想其他办法了。但 gRPC 和 HTTP 相对简单,只要通过 header 传递就可以了。

下面是这些 header 值的含义。

X-Request-ID:请求 ID,一般 Sidecar 会在入口层生成统一的请求 ID,用于一次请求在内部服务之间传递,方便通过请求 ID 查询一次请求的所有日志。

X-B3-TraceId:链路追踪的唯一标识,长度为  64 位。由网关层生成,一次外部请求使用唯一的 TraceId 。

X-B3-SpanId:SpanId 的长度是 64 位,表示当前操作在跟踪树中的位置。

X-B3-ParentSpanId:父 SpanId,如果该值不存在,表示是根节点。

X-B3-Sampled:采样率,当设置为 1 时,表示采样。

下面来看一个 Trace 的真实数据,方便更好的理解 Trace:

{"duration":2065,"operationName":"/ping","parentSpanID":"0","process.serviceName":"negri.sidecarserverlistener.myapp","process.tags.hostname":"MacBook-Pro-3.local","process.tags.ip":"192.168.1.88","spanID":"5f1db306ef459b2f","startTime":1609241265147010,"tags.http.method":"GET","tags.http.status_code":"200","tags.http.url":"/ping","tags.peer.address":"http://127.0.0.1:8888","tags.span.kind":"server","traceID":"5f1db306ef459b2f"}

通过上面的数据,可以了解这个接口的运行时间 duration,记录了服务名、TraceId、SpanId、ParentSpanId 等上面我们聊到的常用数据,另外还记录了所需要的一些自定义数据,放在了 Tags 字段中。

链路追踪系统,通过收集程序中的打点日志的方式,通常提供了以下功能。

排查根因:分析单次请求的调用链路,排查问题根因。

调用关系图:通过 Trace 中的服务信息,绘制服务调用关系图。

日志追踪:通过关联日志 RequestId,可以链接到日志系统,查看更详细的日志信息。

Jaeger

Jaeger 是 Uber 公司开源的、采用 Go 语言开发的分布式链路追踪系统,由以下几个模块组成。

jaeger-client:Jaeger 提供的符合 OpenTracing 标准的各种语言的 SDK,包括 Java、Go、Node.js 等。Client 负责收集 Trace 数据发送到 Agent。

jaeger-agent:jaeger-client 的代理程序,部署在所有宿主机上,这样的目的和 Sidecar 类似,屏蔽了一些路由和 Collector 节点发现的细节,让 Client 更加轻量化。Client 通过 UDP 协议和 Agent 通信,也避免了日志落盘再采集导致的一些性能问题。

jaeger-collector:负责收集 Agent 上报的链路追踪数据,并做一些数据验证工作,以及对数据做一些处理然后上报到存储系统。

jaeger-db:后端存储系统,支持 Cassandra 和 ElasticSearch。

jaeger-query:专门负责调用链查询的一个服务,提供一套独立的 UI 界面,用于绘制调用关系和展示服务链路。

spark-job:基于 Spark 的运算任务,可以计算服务的依赖关系、调用次数等。

Trace 系统中的常见问题

TraceId 如何设计

TraceId 只要全局唯一就可以了,这里可以参考 SOFATrace 中的设计,通过 8 位的 IP 地址和 13 位的时间戳,以及四位的自增序列,加上本身进程的 PID 号,这样组成的字符串就可以保证全局唯一了。

Trace 日志落盘

在实际项目中,并没有采用原生的 Jaeger Agent 的方式收集日志,而是采用了阿里云提供的 sls 作为链路追踪的存储系统。用 SDK 将数据直接落盘,以日志的方式存储在磁盘上,然后由 Logtail 将日志收集到 sls,但是这样就产生了日志写盘的性能问题。如果每条日志都直接落盘,那么系统的 IO 消耗会非常大,所以实践中采用了异步落盘的机制,减少对业务请求的影响,也同时减少了系统调用和系统 IO 的消耗。

Service Mesh 中可以无感知接入 Trace 吗?

实际上因为 Service Mesh 经常宣传无侵入的接入方式,这块造成了一定的误解,早期 Istio 文档描述得也不是很清楚,但后面的 Istio 文档做了更正,在 Service Mesh 中只要少量代码就可以接入 Trace 了

Sidecar 依然依赖应用程序传递 Trace 所需要的 header,但通过 Sidecar 可以简化 Trace 的接入,Trace SDK 只要保证能够将应用程序的 Trace header 在每次请求中传递下去就可以了,而不用负责繁重的数据上报工作。但如果除了服务的链路信息,还希望收集一些 DB 中间件的调用信息,数据上报的工作还是无法避免的。

Trace 组件如何在项目中落地?

Trace 的落地一直是一个比较难的问题,即便用 Service Mesh 可以简化 Trace 落地的方式,但对一些语言来说,侵入性比较强是最大的痛点。对于 Java  相对来说比较容易,可以通过 Java Agent 无感知地接入,但对于其他语言就没那么容易了。

比如 PHP、Go、Node.js 等语言,都需要通过 SDK 的方式接入。如果是在微服务拆分的中后期,想要再增加 Trace 系统就十分困难了,所以这里建议在决定使用微服务架构的初期,在框架内集成 Trace SDK,并默认开启,免去后续的麻烦。另外 Trace 中的项目名称一定要和公司的服务注册名称统一,这样才有利于统一排查问题。

采样率如何设置?

实际上 Trace 的采样率一直是个头疼的问题,设置太低可能有些接口采集不到有效的信息,设置得太高又会造成成本过高的问题。在 Jaeger 中提供了动态采样率的功能,这样可以保证同一个服务中,低 QPS 的接口可以被有效采集,而高 QPS 的接口有较低的采样率。

Trace 链路的连续性

一般情况,会在网关中生成初始的 TraceId 和最上层的 SpanId,后续的服务只要拿到 downstream 集群,通过 header 传递下来的 Trace 信息,就可以继续生成本服务的 Trace 数据了。

像 Jaeger 之类的组件,在没有拿到 Trace 信息的情况下,会默认生成新的 TraceId 。实际上,并不建议这样的做法,一是增加了 SDK 的复杂度,造成维护困难;二是链路一旦断掉,对于排查问题的帮助就不是很大了

关于链路追踪的一些思考

客户端链路追踪

大部分情况下,所说的链路追踪,主要还是用于内部服务,以展示微服务的调用关系和排查微服务导致的链路复杂性问题。但实际上可以考虑在客户端就生成 client-traceid,然后将 client-traceid 和内部 TraceId 关联起来,这样就能够展示整个链路追踪中客户端耗时占比

RPC SDK 的设计

实际上 Trace 系统需要将 Trace header 的信息一层层传递下去,所以 RPC 的 Client SDK 需要具备“在请求的时候带上 Trace 所需信息”的能力。

其实在微服务中,有很多场景需要将最顶层的网关层的数据传递下去,以保证上游(upstream)的微服务能够获取这些信息。比如客户端 IP、灰度流量标签等。所以在微服务 SDK 设计的时候最好定义一组 header,方便我们解析并向上游传递,比如  X-Mesh-Xxx。

Trace 链路追踪

先从代码层面看看 Istio 是如何接入 Trace 系统的。虽然网格代理 Envoy 能够自动识别 Trace 中的 header,在请求 upstream 的时候自动生成 span 并携带发送,但是如果要将整个链路追踪信息串在一起,还需要代码中额外携带一些链路追踪信息才能完成。

Istio 规定需要携带以下 header:

x-request-id
x-b3-traceid
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context

首先看一下入口方法:

@app.route('/productpage')
@trace()
def front():
    product_id = 0  # TODO: replace default value
    headers = getForwardHeaders(request)
    user = session.get('user', '')
    product = getProduct(product_id)
    detailsStatus, details = getProductDetails(product_id, headers)

可以看到,通过 getForwardHeaders 方法,获取了在请求其他服务时需要传递的 header 参数,在getProductDetails 调用的时候,传递了通过 getForwardHeaders 方法获得的 header 参数。

下面看一下上述内容如何在代码中得以体现:

def getForwardHeaders(request):
    headers = {}
    # x-b3-*** 通过 opentracing 的库直接获取
    span = get_current_span() # 获取 downstream header 中传递的 span
    carrier = {}
    tracer.inject(
        span_context=span.context,
        format=Format.HTTP_HEADERS,
        carrier=carrier) # 将 trace header 注入 carrier
    headers.update(carrier) # 更新 headers    
    # 手动获取其他非 x-b3-*** 的 header
    if 'user' in session:
        headers['end-user'] = session['user']    
    # Keep this in sync with the headers in details and reviews.
    incoming_headers = [
    # All applications should propagate x-request-id. This header is included in access log statements and is used for consistent trace
    # sampling and log sampling decisions in Istio.
        'x-request-id',    
    # Lightstep tracing header. Propagate this if you use lightstep tracing
    # in Istio (see
    # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/)
    # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT
    # Lightstep recommends using B3 or TRACE_CONTEXT and most application
    # libraries from lightstep do not support x-ot-span-context.
        'x-ot-span-context',
    # Datadog tracing header. Propagate these headers if you use Datadog
    # tracing.
        'x-datadog-trace-id',
        'x-datadog-parent-id',
        'x-datadog-sampling-priority',
        # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio
        # configurations.
        'traceparent',
        'tracestate',
        # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio
        # configurations.
        'x-cloud-trace-context',
        # Grpc binary trace context. Compatible with OpenCensusAgent nad
        # Stackdriver Istio configurations.
        'grpc-trace-bin',
        # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and
        # Stackdriver Istio configurations. Commented out since they are
        # propagated by the OpenTracing tracer above.
    # 'x-b3-traceid',
        # 'x-b3-spanid',
        # 'x-b3-parentspanid',
        # 'x-b3-sampled',
        # 'x-b3-flags',
        # Application-specific headers to forward.
        'user-agent',
    ]
    # For Zipkin, always propagate b3 headers.
    # For Lightstep, always propagate the x-ot-span-context header.
    # For Datadog, propagate the corresponding datadog headers.
    # For OpenCensusAgent and Stackdriver configurations, you can choose any
    # set of compatible headers to propagate within your application. For
    # example, you can propagate b3 headers or W3C trace context headers with
    # the same result. This can also allow you to translate between context
    # propagation mechanisms between different applications.
    # 传递其他非 b3 header 的头信息
    for ihdr in incoming_headers:
        val = request.headers.get(ihdr)
        if val is not None:
            headers[ihdr] = val    
        return headers

可以看到,通过 Jaeger 的类库,自动将带有 b3 header 的数据存储到了 headers 中,其他的一些 Trace 规范,则需要通过 incoming_headers 自定义的方式自动传递。启动 Jaeger,通过 URL 或者 cURL 的方式多次访问 productpage:

选择 productpage 服务,可以看到如下页面:

点击一条具体的链路,进入详情页面,可以详细展示整个微服务调用的链路,包含每个阶段耗时的详细信息,方便排查具体哪个环节出现了问题:

 

点击 System Architecture 页面,可以看到整个微服务的调用关系展示:

 

 

标签:Trace,header,b3,Istio,观测,headers,链路,日志
From: https://www.cnblogs.com/muzinan110/p/17061140.html

相关文章

  • Istio 灰度发布
    金丝雀发布也被称为灰度发布,实际上就是将少量的生产流量路由到线上服务的新版本中,以验证新版本的准确性和稳定性。Istio和Kubernetes实现金丝雀发布的方式不太一样,Istio......
  • istio入门
    Istio在逻辑上分为数据平面和控制平面。数据平面,由一组高性能的智能代理(基于Envoy改进的istio-proxy)组成,它们控制和协调了被代理服务的所有网络通信,同时也负责收集和......
  • 扰动观测器(DO)稳定性分析(1)
    1.被控对象考虑以下具有可加性有界扰动的连续时间非线性系统:   (1)其中,,,分别表示状态、被控输入、外部扰动。假设1:扰动及其微分是有界的,即   (2)其中,是已知常数。扰......
  • Serverless可观测性
     整体解决方案 指标确定指标对于使用函数计算的用户而言,更关心的是业务逻辑,因此,常用的用户侧函数监控指标会包括以下几种:函数的总执行次数;函数的平均执行耗时;......
  • 分布式请求链路跟踪(SpringCloud Sleuth + zipkin)
    前言在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路......
  • Stata:删除重复观测值
    1.当一条样本的全部变量均与另一条或多条样本重复时,直接使用duplicatesdrop_all,force或gduplicatesdrop_all,force,后者为外部命令,在样本较大时对运算速度的提升......
  • 【计算机网络-数据链路层】局域网(LAN)
    目录1局域网的概念1.1局域网的拓扑结构1.2局域网的传输介质1.3局域网的介质访问控制方式(MAC)1.4局域网的分类2以太网(Ethernet,IEEE802.3标准)2.1以太网的传输介质2.2......
  • 【计算机网络-数据链路层】介质访问控制协议(MAC协议)
    目录1静态划分信道——信道划分MAC协议1.1频分多路复用(FDM)——“并行”1.2时分多路复用(TDM)——“并发”1.2.1同步时分多路复用1.2.2异步时分多路复用1.3波分多路复......
  • m基于matlab的无线光通信CDMA闭环链路功率控制算法仿真,对比了OOK,2PPM,4PPM,8PPM,16P
    1.算法描述        在光通信领域,多址技术主要有WDM(波分复用)、TDM(时分复用)及OCDMA(光码分多址)三种方式。OCDMA技术从70年代中期开始出现,现在引起了人们的广泛......
  • 全景剖析阿里云容器网络数据链路(二):Terway EN
    作者:余凯本系列文章由余凯执笔创作,联合作者:阿里云容器服务谢石对本文亦有贡献前言近几年,企业基础设施云原生化的趋势越来越强烈,从最开始的IaaS化到现在的微服务化,客户的......