Istio可观测性(链路)
可观测性的英文是 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 链路追踪
先从代码层面看看 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 页面,可以看到整个微服务的调用关系展示:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)