如何使用OpenTelemetry进行端到端跟踪。服务请求流经的组件数量越多,您对监控的需求就越大。当然,监控只是状态跟踪的开始。你需要一个可以跨越所有组件的聚合视图,通过指标和日志两个维度实现可观察性。1.W3C规范具有可追溯性的解决方案通常可以通过异构技术栈来规范监控的标准格式。目前,市场上已经存在几种不同的实施规范。在大多数情况下,正如下面著名的XKCD漫画中所描述的,新的规范要求通常会导致另一个附加规范的实施。在这里,我向大家介绍一个市场正在追随的W3C新规范——TraceContext。该规范定义了标准的HTTP标头和数字格式,以支持分布式跟踪场景中的上下文信息。毕竟,上下文信息不仅在分布式系统中唯一标识各种请求,而且还定义了一种添加和传播特定于提供者的信息的方式。TraceContext规范只是标准化了如何在服务之间发送和修改上下文信息。它还包含如下图所示的两个关键概念:Trace需要跟踪跨多个组件的请求路径。跨度绑定到单个组件并通过父子关系链接到另一个跨度。目前TraceContext的实现有很多,OpenTelemetry就是其中之一。2.OpenTelemetry可以作为黄金标准OpenTelemetry是多种语言的工具、API和SDK的集合。用户可以使用它来检测、生成、收集和导出遥测类型的数据,例如指标、日志和跟踪,以便分析软件性能和行为。OpenTelemetry是由CNCF管理的项目。之前有两个项目:OpenTracing,顾名思义,就是专注于追踪的OpenCensus,其目标是管理指标和追踪。通过合并两个项目的日志功能,OpenTelemetry目前提供了一组专注于可观察性的层。该级别具有以下特点:通过多语言检测API和不同语言的标准化实现提供收集器等基础设施组件提供W3CTraceContext等可互操作的格式值得注意的是,虽然OpenTelemetry是TraceContext的一种实现,但是它的功能要广泛得多。TraceContext仅限于HTTP场景,而OpenTelemetry可以跨越Kafka等非Web组件。3.UseCase接下来,让我们以电子商务网站为例进行深入探讨。假设电商网站是围绕微服务设计的,包括管理商品的目录和处理商品价格的定价,这两个微服务。如下图所示,catalog是一个用Kotlin写的SpringBoot应用,pricing是一个PythonFlask应用。每个微服务都可通过RESTAPI访问,并受API网关保护。当用户访问应用时,首页会从后台获取所有商品及其价格的信息并呈现。跟踪允许我们跨微服务或数据库网关跟踪请求的路径。4.网关跟踪我们将使用ApacheAPISIX在入口点(也称为网关)生成跟踪ID。这里的ApacheAPISIX提供了丰富的流量管理功能如:负载均衡、动态上游、金丝雀发布、断路器、认证、可观察性等。同时基于插件架构的ApacheAPISIX根据OpenTelemetry生成跟踪数据通过提供OpenTelemetry插件规范。但是,该插件仅支持基于HTTP的二进制编码-OLTP。接下来,让我们配置opentelemetry插件:YAMLapisix:enable_admin:false#1config_center:yaml#1plugins:-OpenTelemetry#2plugin_attr:opentelemetry:resource:service.name:APISIX#3collector:address:jaeger:4318#4#1:InstandaloneRunApacheAPISIX模式使应用程序易于理解。#2:将opentelemetry配置为全局插件。#3:设置服务名称。它将是出现在跟踪显示组件中的名称。#4:将跟踪发送到jaeger服务。我们将在下面详细讨论。为了跟踪每条路由,我们需要通过以下代码将插件设置为全局插件,而不是为每条路由添加插件:YAMLglobal_rules:-id:1plugins:opentelemetry:sampler:name:#1:由于跟踪本身会性能也有影响,跟踪的越多,影响就越大,因此我们仔细平衡性能影响与可观察性收益。但是,在这种情况下,我们仍然希望能够跟踪每个请求。5.收集、存储和展示痕迹虽然TraceContext是W3C规范,OpenTelemetry是事实标准,但市面上仍然有很多收集、储存和展示痕迹的方案。例如,虽然Elastic技术栈可以处理各种存储和展示,但用户必须依赖其他产品进行采集;Jaeger和Zipkin可以通过一个完整的套件来全面实现收集、存储和展示跟踪。OpenTelemetry之前的Jaeger和Zipkin虽然trace传输格式不同,但都可以与OpenTelemetry格式集成。鉴于Jaeger可以提供一个集成的Docker镜像,每个功能都有对应的组件,它们都嵌入在同一个镜像中,方便配置。所以,我这里选择Jaeger。其镜像相关端口分配如下:端口协议组件功能16686HTTP查询服务前端4317HTTP收集器,如果启用,通过gRPC接受OpenTelemetry协议(OTLP)4318HTTP收集器,如果启用,通过HTTP接受OpenTelemetry协议它的DockerCompose代码For:YAMLservices:jaeger:image:jaegertracing/all-in-one:1.37#1environment:-COLLECTOR_OTLP_ENABLED=true#2ports:-"16686:16686"#3#1:使用all-in-的镜像一。#2:启用OpenTelemetry格式收集器(非常重要)。#3:公开UI端口。至此,我们已经完成了基础架构的构建,现在我们将专注于在应用程序中启用跟踪。6.Flask应用的跟踪定价服务是一个简单的Flask应用。它提供用于从数据库中获取单个产品和价格的端点。下面是对应的代码:Python@app.route('/price/')#1-2defprice(product_str:str)->Dict[str,object]:product_id=int(product_str)price:Price=Price.query.get(product_id)#3ifpriceisNone:returnjsonify({'error':'Productnotfound'}),404else:low:float=price.value-price.jitter#4high:float=价格.value+price.jitter#4return{'product_id':product_id,'price':round(uniform(low,high),2)#4}#1:端点#2:路由需要产品的ID。#3:使用SQLAlchemy从数据库中获取数据。#4:在这里,我们随机生成价格。当然,RealPricingEngine并非如此。值得注意的是,我们正在使用一种低效的方式来一次调用并获取一个价格以用于观察跟踪目的。而在现实生活中,路由器应该能够接受多个产品的ID,并在一个“请求-响应”中获取所有相关的价格。我们可以通过两种方式检测应用程序,自动和手动。由于手动需要开发时间,而自动省力又快,所以我建议你从自动开始,如果需要的话添加手动方法。首先,我们需要添加几个Python包:opentelemetry-distro[otlp]==0.33b0opentelemetry-instrumentationopentelemetry-instrumentation-flask接下来,我们需要配置以下参数:YAMLpricing:build:./pricingenvironment:OTEL_EXPORTER_OTLP_ENDPOINT:http:///jaeger:4317#1OTEL_RESOURCE_ATTRIBUTES:service.name=pricing#2OTEL_METRICS_EXPORTER:none#3OTEL_LOGS_EXPORTER:左右滑动查看完整代码#1:将跟踪发送给Jaeger。#2:设置服务名称。它将是出现在跟踪显示组件中的名称。#3:在这里,让我们暂时忽略日志和指标。然后,我们不使用标准的flaskrun命令,而是将其包装如下:Shellopentelemetry-instrumentflaskrun到目前为止,我们已经从方法调用和Flask路由中收集了span。下面,我们根据需要手动添加额外的跨度:PythonfromOpenTelemetryimporttrace@app.route('/price/')defprice(product_str:str)->Dict[str,object]:product_id=int(product_str)withtracer.start_as_current_span("SELECT*FROMPRICEWHEREID=:id",attributes={":id":product_id})asspan:#1price:Price=Price.query.get(product_id)#...#1:添加使用配置的标签和属性的额外跨度。7.SpringBoot应用的跟踪目录服务是一个用Kotlin开发的ReactiveSpringBoot应用。它提供了以下两个端点:获取单个产品和获取所有产品。两人都是先查商品数据库,再查询上面定价服务的价格。对于Python,我们还可以采用自动和手动两种检测方式。在这里,让我们从容易实现的自动化开始。在JVM上,我们通过代理执行此操作:Shelljava-javaagent:opentelemetry-javaagent.jar-jarcatalog.jar与Python一样,它为每个方法调用和HTTP入口点创建跨度。同时,它还会检测JDBC调用。在此示例Reactive堆栈中,我们使用R2DBC。因此,我们需要配置以下默认行为:YAMLcatalog:build:./catalogenvironment:APP_PRICING_ENDPOINT:http://pricing:5000/priceOTEL_EXPORTER_OTLP_ENDPOINT:http://jaeger:4317#1OTEL_RESOURCE_ATTRIBUTES:service.name=orders#2OTEL_METRICS_EXPORTER:none#3OTEL_LOGS_EXPORTER:#1:将跟踪发送给Jaeger。#2:设置服务名称。它将是出现在跟踪显示组件中的名称。#3:在这里,让我们暂时忽略日志和指标。而对于Python,我们直接加上人工检测。当然,目前有两种选择:程序化和基于注解。除非我们引入SpringCloudSleuth,否则前者稍微复杂一些。接下来,让我们添加一个额外的依赖项:XMLio.opentelemetry.instrumentationopentelemetry-instrumentation-annotations1.17.0-alpha对应的注解代码为:Kotlin@WithSpan("ProductHandler.fetch")//1suspendfunfetch(@SpanAttribute("id")id:Long):Result{//2valproduct=repository.findById(id)returnif(product==null)Result.failure(IllegalArgumentException("Product$idnotfound"))elseResult.success(product)}#1:添加一个带有配置标签的额外跨度。#2:使用参数作为属性,将键(key)设置为id,其对应的值作为参数的运行时值。8、输出结果下面,我们使用如下命令查看本例的结果:Shellcurllocalhost:9080/productscurllocalhost:9080/products/1通过如下JaegerUI,我们可以发现两条trace(每次调用一次):我们还可以深入研究单个跟踪的跨越:值得注意的是,我们也可以在没有上面的UML图的情况下推断出它的数据流图。这样的数据流图很擅长展示组件的内部调用。而且,每个跨度包括自动和手动检测添加的属性:9.总结综上所述,我已经展示了如何使用OpenTelemetry跨微服务或数据库网关来实现对某个请求路径的端到端跟踪。虽然在现实世界中,tracing可能会涉及非HTTP相关的组件,比如Kafka、消息队列等,但大部分系统还是会以某种方式依赖HTTP。此外,跨组件跟踪HTTP请求是系统可观察性的良好开端。原文链接:https://dzone.com/articles/end-to-end-tracing-with-opentelemetry