Istio可观测性(链路)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Istio可观测性(链路)相关的知识,希望对你有一定的参考价值。
可观测性的英文是 Observability,这是伴随着云原生技术发展产生的一个新兴词汇,在传统的 IT 中,并没有这种说法。简单来说,可观测性是通过系统输出信息到外部,以检测系统内部的运行状态。Trace,通过内部打点的方式串联起微服务的各个组件。Metrics,通过输出服务的 Metrics 信息,达到外部监测的目的。
链路追踪:通过在程序内打点记录日志的方式,记录每次请求的调用链路信息。特点是数据精准、细致,适合查看某一次请求的调用链路,一般用于查看某些响应较慢的接口瓶颈。
监控指标:主要是用时序性数据库记录每个时间点的监控数据,一般通过主动拉取服务 Metrics 数据的方式记录,然后实时计算一段时间的数据,并通过图形界面的方式展现出来。它的特点是实时性强、可观测指标丰富,适合查看一段时间内的指标趋势。
日志:日志是比较传统的可观测性组件了,无论是在单体服务时代还是微服务时代,我们都会用日志排查问题。日志的特点是数据比较离散,之间没有关联。当然,可以通过在日志中打印
在微服务架构中,随着服务和中间件数量变多,往往一个接口要请求几十次服务和上百次
Trace 链路追踪原理
链路追踪系统基本源于
Dapper 通过一个全局唯一的 TraceId 表示请求调用链,并定义了 span,span 表示一次调用(可以是远程调用,也可以程序内的函数调用)。每个 span 包含了两个重要信息,一个是当前 SpanId,另外一个就是 ParentSpanId。
如何将
下面是这些
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"
通过上面的数据,可以了解这个接口的运行时间
链路追踪系统,通过收集程序中的打点日志的方式,通常提供了以下功能。
排查根因:分析单次请求的调用链路,排查问题根因。
调用关系图:通过
日志追踪:通过关联日志
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 作为链路追踪的存储系统。用,但是这样就产生了日志写盘的性能问题。如果每条日志都直接落盘,那么系统的 IO 消耗会非常大,所以实践中采用了异步落盘的机制,减少对业务请求的影响,也同时减少了系统调用和系统
Service Mesh 中可以无感知接入 Trace 吗?
实际上因为 Service Mesh 经常宣传无侵入的接入方式,这块造成了一定的误解,早期 Istio 文档描述得也不是很清楚,但后面的 Istio 文档做了更正,在。
Sidecar 依然依赖应用程序传递 Trace 所需要的 header,但通过 Sidecar 可以简化 Trace 的接入,Trace SDK 只要保证能够将应用程序的 Trace header 在每次请求中传递下去就可以了,而不用负责繁重的数据上报工作。但如果除了服务的链路信息,还希望收集一些 DB 中间件的调用信息,数据上报的工作还是无法避免的。
Trace 组件如何在项目中落地?
Trace 的落地一直是一个比较难的问题,即便用 Service Mesh 可以简化 Trace 落地的方式,但对一些语言来说,侵入性比较强是最大的痛点。对于
比如 php、Go、Node.js 等语言,都需要通过 SDK 的方式接入。如果是在微服务拆分的中后期,想要再增加 Trace 系统就十分困难了,所以这里建议在决定使用微服务架构的初期,在框架内集成。另外
采样率如何设置?
实际上 Trace 的采样率一直是个头疼的问题,设置太低可能有些接口采集不到有效的信息,设置得太高又会造成成本过高的问题。在,这样可以保证同一个服务中,低
Trace 链路的连续性
一般情况,会在网关中生成初始的
像 Jaeger 之类的组件,在没有拿到 Trace 信息的情况下,会默认生成新的 TraceId 。实际上,并不建议这样的做法,一是增加了。
关于链路追踪的一些思考
客户端链路追踪
大部分情况下,所说的链路追踪,主要还是用于内部服务,以展示微服务的调用关系和排查微服务导致的链路复杂性问题。但实际上可以考虑在客户端就生成。
RPC SDK 的设计
实际上
其实在微服务中,有很多场景需要将最顶层的网关层的数据传递下去,以保证上游(upstream)的微服务能够获取这些信息。比如客户端 IP、灰度流量标签等。所以在微服务 SDK 设计的时候最好定义一组 header,方便我们解析并向上游传递,比如 X-Mesh-Xxx。
Trace 链路追踪
先从代码层面看看
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)
可以看到,通过
下面看一下上述内容如何在代码中得以体现:
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 自定义的方式自动传递。启动
选择
点击一条具体的链路,进入详情页面,可以详细展示整个微服务调用的链路,包含每个阶段耗时的详细信息,方便排查具体哪个环节出现了问题:
点击
以上是关于Istio可观测性(链路)的主要内容,如果未能解决你的问题,请参考以下文章
Istio和Kubernetes帮助Trulia房产网站消除单体架构增强微服务的可观测性