通过 APM 和跟踪优先考虑客户体验
企业软件开发和运营已经成为一个有趣的领域。我们拥有一些非常强大的工具可供使用,但作为一个行业,我们未能采用许多可以使我们的生活更轻松的工具。其中一种目前未被充分利用的工具是应用性能监控 (APM) 和跟踪,尽管 OpenTelemetry 使其可以低摩擦地采用。
然而,日志记录是无处不在的。每个软件应用程序都有某种类型的日志,并且(即使在今天)进行故障排除的默认工作流程是从客户和系统遇到的异常到日志,并从那里开始寻找解决方案。
这存在各种挑战,其中主要的一个是日志通常没有提供足够的信息来解决问题。如今,许多服务会返回含糊不清的 500 错误,几乎没有提供任何信息。如果没有错误或日志文件,或者问题是系统速度非常慢,该怎么办?仅凭日志无法帮助解决这些问题。这会让用户使用半损坏的系统,并带来糟糕的用户体验。我们都经历过这种情况,而且可能会令人非常沮丧。
我发现自己会问的问题是,为什么客户体验通常会排在错误之后?如果客户体验是首要任务,那么就应该制定一项采用跟踪和 APM 的策略,并使其与日志记录一样重要。用户应该停止默认使用日志并主要考虑日志,就像许多人今天所做的那样。这也需要对思维模式进行一些必要的更改。
实现这一目标的路径是什么?这正是我们将在本博文中探讨的内容。我们将从讨论支持组织变革开始,然后我们将概述一个从仅使用日志到集成日志、跟踪和 APM 的完整解决方案的推荐路径。
培养新的监控思维模式:如何推动 APM 和跟踪的采用
为了让团队改变他们的故障排除思维模式,需要进行哪些组织变革?
最初,企业应考虑需要在团队中广泛分享的战略优先事项和目标。在大型组织中,有助于推动这一点的一个方法是考虑一个专门从事可观测性的完整产品团队或具有自己路线图和优先事项的 CoE(卓越中心)。
这个团队(无论是虚拟的还是永久的)都应该以客户为中心,并从关键问题(如:我需要收集什么?我需要观察什么?我该如何行动?)开始反向工作。一旦团队成员理解了这些问题的答案,他们就可以开始考虑推动这些结果所需的技术决策。
从跟踪和 APM 的角度来看,最令人关注的领域是客户体验、服务水平目标和服务水平结果。从这里开始,组织可以开始实施工作计划,以持续改进并在团队之间分享知识。这将有助于使团队围绕具有共同目标的共同框架保持一致。
在接下来的几个部分中,我们将介绍一个四步路径,以帮助您最大限度地提高 APM 和跟踪的成功率。此路径将引导您完成以下关键步骤,从而成功采用 APM
- 摄取: 您需要做出哪些选择才能激活跟踪并开始将跟踪数据摄取到您的可观测性工具中?
- 集成: 跟踪如何与日志集成以实现完整的端到端可观测性,以及除了简单的跟踪之外,您还可以利用哪些其他方法来获得更好的数据分辨率?
- 分析和 AIOP: 通过机器学习改善客户体验并减少噪音。
- 规模和总体拥有成本: 在企业范围内推出跟踪,并采取策略来处理数据量。
1. 摄取
出于 APM 目的摄取数据通常涉及“检测”应用程序。在本节中,我们将探讨检测应用程序的方法,简要讨论一下采样,最后将重点介绍使用通用模式进行数据表示。
开始使用检测
我们有哪些选项可以摄取 APM 和跟踪数据?我们将讨论许多选项来指导您,但首先让我们退一步。APM 有着悠久的历史 — 在最初的 APM 实现中,人们主要关注方法计时,如下所示
通常,您有一个配置文件来指定要计时的哪些方法,并且 APM 实现会使用方法计时来检测指定的代码。
从这里开始,事情开始发展,APM 的第一个新增功能之一是添加跟踪。
对于 Java,使用所谓的 Java 代理来实现此系统非常简单。您只需指定 -javagent 命令行参数,代理代码就可以访问 Java 中的动态编译例程,以便在将代码编译为机器代码之前对其进行修改,从而允许您使用计时或跟踪例程“包装”特定方法。因此,自动检测 Java 是最初的 APM 供应商所做的第一件事之一。
OpenTelemetry 拥有类似的代理,并且大多数提供 APM 解决方案的可观测性供应商都有自己独特的方法来实现这一点,这些方法通常比开源工具具有更高级和不同的功能。
自那时起,情况已经发生了变化,Node.JS 和 Python 现在非常流行。
因此,出现了自动检测这些语言运行时的方法,这些方法主要通过在启动代码之前将库注入到代码中来实现。OpenTelemetry 提供了一种在 Kubernetes 上使用 Operator 和 sidecar 来实现此目的的方法,这里 提供了支持 Python、Node.JS、Java 和 DotNet 的方法。
另一种方法是将 APM 和跟踪 API 调用添加到您自己的代码中,这与添加日志功能类似。您甚至可能希望在代码中创建一个抽象来处理这种横切关注点,尽管现在有了可以实现此功能的开放标准,这已不再是一个大问题。
您可以在下面看到如何将 OpenTelemetry span 和属性添加到您的代码以进行手动检测的示例,以及这里。
from flask import Flask
import monitor # Import the module
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import urllib
import os
from opentelemetry import trace
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
# Service name is required for most backends
resource = Resource(attributes={
SERVICE_NAME: "your-service-name"
})
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT'),
headers="Authorization=Bearer%20"+os.getenv('OTEL_EXPORTER_OTLP_AUTH_HEADER')))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
RequestsInstrumentor().instrument()
# Initialize Flask app and instrument it
app = Flask(__name__)
@app.route("/completion")
@tracer.start_as_current_span("do_work")
def completion():
span = trace.get_current_span()
if span:
span.set_attribute("completion_count",1)
通过以这种方式实现 APM,您甚至可以通过将所有必需的日志信息存储在 span 属性、异常和指标中来消除进行任何日志记录的需要。缺点是您只能对您拥有的代码执行此操作,因此您将无法以这种方式删除所有日志。
采样
许多人没有意识到 APM 是一个昂贵的过程。它会给您的应用程序增加大量的 CPU 周期和内存,尽管它有很多价值,但肯定需要权衡取舍。
您应该 100% 采样所有内容并承担成本吗?还是应该考虑使用较少的样本甚至基于尾部的采样来进行智能权衡,许多产品通常都支持这种采样?在这里,我们将讨论两种最常见的采样技术——基于头的采样和基于尾部的采样——以帮助您做出决定。
基于头的采样
在这种方法中,采样决策是在跟踪开始时做出的,通常是在服务或应用程序的入口点。以固定的速率对跟踪进行采样,并且此决策会传播到分布式跟踪中涉及的所有服务。
通过基于头的采样,您可以使用配置来控制采样率,从而控制采样并报告给 APM 服务器的请求百分比。例如,采样率为 0.5 表示仅采样 50% 的请求并将其发送到服务器。这对于减少收集的数据量,同时仍然保持应用程序性能的代表性样本非常有用。
基于尾部的采样
与基于头的采样不同,基于尾部的采样是在整个跟踪完成后才做出采样决策。这允许根据实际的跟踪数据做出更智能的采样决策,例如仅报告有错误的跟踪或超过特定延迟阈值的跟踪。
我们建议使用基于尾部的采样,因为它最有可能减少噪声并帮助您专注于最重要的问题。它还有助于降低数据存储端的成本。然而,基于尾部的采样的缺点是,它会导致 APM 代理生成更多的数据。这可能会占用您的应用程序上更多的 CPU 和内存。
OpenTelemetry 语义约定和 Elastic Common Schema
OpenTelemetry 规定了语义约定或语义属性,以建立各种操作和数据类型的统一名称。遵守这些约定可以促进代码库、库和平台之间的标准化,最终简化监控过程。
创建用于跟踪的 OpenTelemetry span 非常灵活,允许实施者使用特定于操作的属性对其进行注释。这些 span 表示系统内部和系统之间的特定操作,通常涉及诸如 HTTP 或数据库调用之类的广泛认可的协议。为了在监视系统中有效地表示和分析 span,需要补充信息,这取决于协议和操作类型。
统一跨不同语言的属性方法对于操作员轻松关联和交叉分析来自多语言微服务的遥测数据至关重要,而无需掌握特定于语言的细微差别。
Elastic 最近对 OpenTelemetry 贡献了 Elastic Common Schema,以增强语义约定,使其涵盖日志和安全性。
遵守共享模式会产生可观的收益,使操作员能够快速识别复杂的交互并关联日志、指标和跟踪,从而加快根本原因分析并减少搜索日志和查明特定时间范围所花费的时间。
我们提倡在您的应用程序中定义跟踪、指标和日志数据时,特别是开发新代码时,坚持使用已建立的模式(例如 ECS)。这种做法将在解决问题时节省时间和精力。
2. 集成
集成对于 APM 非常重要。您的解决方案与其他工具和技术(例如云)的集成程度,以及其将日志和指标集成到您的跟踪数据中的能力,对于充分了解客户体验至关重要。此外,大多数 APM 供应商都有用于合成监控和分析的相邻解决方案,以获得更深入的视角来增强您的 APM。我们将在以下部分探讨这些主题。
APM + 日志 = 超能力!
由于 APM 代理可以检测代码,因此它们也可以检测用于日志记录的代码。通过这种方式,您可以直接在 APM 中捕获日志行。这通常很容易启用。
启用此功能后,您还将自动获得注入的有用的字段,例如
- service.name,service.version,service.environment
- trace.id,transaction.id,error.id
这意味着日志消息将自动与事务相关联,如下所示,从而更容易减少平均故障排除时间 (MTTR) 并找到大海捞针。
如果这适用于您,我们强烈建议您启用它。
在 Kubernetes 内部部署 APM
人们通常希望在 Kubernetes 环境中部署 APM,而跟踪对于监视云原生环境中的应用程序至关重要。您可以通过三种不同的方式来解决这个问题。
1. 使用 sidecar 自动检测
对于 Kubernetes,可以使用 init 容器和一些可以在运行时修改 Kubernetes 清单以自动检测您的应用程序的东西。
init 容器将仅用于在启动时将所需库或 jar 文件复制到您需要的主 Kubernetes pod 中。然后,您可以使用 Kustomize 将所需的命令行参数添加到您的代理引导程序中。
如果您不熟悉它,Kustomize 会在运行时添加、删除或修改 Kubernetes 清单。它甚至可以作为 Kubernetes CLI 的一个标志使用——只需执行 kubectl -k 即可。
OpenTelemetry 有一个 operator,可以自动为您完成所有这些操作(无需 Kustomize),适用于 Java、DotNet、Python 和 Node.JS,许多供应商也有自己的 operator 或 helm charts 可以实现相同的结果。
2. 将 APM 烘焙到容器或代码中
在 Kubernetes(实际上是任何容器化环境)中部署 APM 的第二种选择是使用 Docker 将 APM 代理和配置烘焙到 dockerfile 中。
在此处查看使用 OpenTelemetry Java 代理的示例
# Use the official OpenJDK image as the base image
FROM openjdk:11-jre-slim
# Set up environment variables
ENV APP_HOME /app
ENV OTEL_VERSION 1.7.0-alpha
ENV OTEL_JAVAAGENT_URL https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_VERSION}/opentelemetry-javaagent-${OTEL_VERSION}-all.jar
# Create the application directory
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
# Download the OpenTelemetry Java agent
ADD ${OTEL_JAVAAGENT_URL} /otel-javaagent.jar
# Add your Java application JAR file
COPY your-java-app.jar $APP_HOME/your-java-app.jar
# Expose the application port (e.g. 8080)
EXPOSE 8080
# Configure the OpenTelemetry Java agent and run the application
CMD java -javaagent:/otel-javaagent.jar \
-Dotel.resource.attributes=service.name=your-service-name \
-Dotel.exporter.otlp.endpoint=your-otlp-endpoint:4317 \
-Dotel.exporter.otlp.insecure=true \
-jar your-java-app.jar
3. 使用服务网格 (Envoy/Istio) 进行跟踪
您在此处的最后一个选择是您是否正在使用服务网格。服务网格是用于处理微服务架构中服务到服务通信的专用基础设施层。它提供了一种透明、可扩展且高效的方式来管理和控制服务之间的通信,使开发人员能够专注于构建应用程序功能,而无需担心服务间通信的复杂性。
这样做的好处是,我们可以在代理中激活跟踪,从而了解服务之间的请求。我们无需更改任何代码,甚至无需为此运行 APM 代理;我们只需打开代理中存在的 OpenTelemetry 收集器——因此这可能是开销最低的解决方案。了解有关此选项的更多信息。
综合指标 通用分析
大多数 APM 供应商都有针对主要 APM 用例的附加组件。通常,我们会看到将综合指标和持续分析添加到 APM 解决方案中。APM 可以与两者集成,并且将这些技术结合在一起以获得更多的问题见解具有一定的价值。
综合指标
综合监控是一种用于衡量 Web 应用程序、网站和 API 的性能、可用性和可靠性的方法,通过模拟用户交互和流量来实现。它包括创建模拟真实用户行为的脚本或自动化测试,例如浏览页面、填写表单或单击按钮,然后定期从不同的位置和设备运行这些测试。
这使开发和运营团队能够比其他方式更早地发现问题,在许多情况下在真实用户发现问题之前捕获问题。
综合指标可以与 APM 集成 - 在脚本运行时将 APM 代理注入到网站中,因此即使您最初没有将最终用户监控放入您的网站中,也可以在运行时注入它。这通常在没有任何用户输入的情况下发生。从那里,每个请求的跟踪 ID 可以通过系统的各个层传递下来,从而使团队可以从综合指标脚本到应用程序堆栈的最低级别(例如数据库)跟踪请求。
通用分析
“分析”是一种动态分析程序复杂性的方法,例如 CPU 利用率或函数调用的频率和持续时间。通过分析,您可以精确定位应用程序的哪些部分消耗的资源最多。“连续分析”是分析的更强大版本,它增加了时间维度。通过了解系统资源随时间的变化情况,您可以找到、调试和修复与性能相关的问题。
通用分析是对此的进一步扩展,它允许您捕获系统中所有时间运行的所有代码的分析信息。使用像 eBPF 这样的技术可以让您查看系统中 所有 的函数调用,包括 Kubernetes 运行时之类的东西。这样做使您最终能够看到未知的未知事物——您不知道是问题的东西。这与 APM 非常不同,APM 实际上是关于跟踪单个跟踪和请求以及整体客户体验。通用分析是关于克服您甚至不知道存在的问题,甚至回答“我最昂贵的代码行是什么?”这个问题。
通用性能分析可以链接到 APM,例如,显示在特定客户问题期间发生的性能分析,或者通过查看线程级别存在的全局状态将性能分析直接链接到追踪。这些技术结合使用可以产生奇效。
通常,性能分析以如下所示的“火焰图”形式查看。框代表执行特定函数所花费的“CPU 占用”时间量。
3. 分析和 AIOps
关于 APM 的有趣之处在于,它开启了一个全新的分析世界,而不仅仅是日志。您突然可以访问来自应用程序内部的信息流。
这使您可以轻松捕获诸如特定客户当前在您最关键的电子商务商店上花费的金额之类的东西,或者查看经纪应用程序中失败的交易,以了解这些失败对收入损失的影响。您甚至可以将机器学习算法应用于此数据,以预测未来的支出或查看此数据中发生的异常情况,从而为您打开一个了解业务运营的新窗口。
在本节中,我们将研究如何做到这一点以及如何充分利用这个新世界,以及如何将 AIOps 实践应用于这些新数据。我们还将讨论为 APM 数据设置 SLI 和 SLO。
将业务数据放入追踪中
通常,有两种方法可以将业务数据放入追踪中。您可以修改代码并添加 Span 属性,例如 此处 提供的示例,如下所示。或者,您可以编写扩展或插件,这样做的好处是可以避免代码更改。OpenTelemetry 支持 在其自动检测代理中添加扩展。大多数其他 APM 供应商通常也有类似的功能。
def count_completion_requests_and_tokens(func):
@wraps(func)
def wrapper(*args, **kwargs):
counters['completion_count'] += 1
response = func(*args, **kwargs)
token_count = response.usage.total_tokens
prompt_tokens = response.usage.prompt_tokens
completion_tokens = response.usage.completion_tokens
cost = calculate_cost(response)
strResponse = json.dumps(response)
# Set OpenTelemetry attributes
span = trace.get_current_span()
if span:
span.set_attribute("completion_count", counters['completion_count'])
span.set_attribute("token_count", token_count)
span.set_attribute("prompt_tokens", prompt_tokens)
span.set_attribute("completion_tokens", completion_tokens)
span.set_attribute("model", response.model)
span.set_attribute("cost", cost)
span.set_attribute("response", strResponse)
return response
return wrapper
利用业务数据获取乐趣和利润
将业务数据放入追踪后,您就可以开始享受它的乐趣了。查看下面金融服务欺诈团队的示例。在这里,我们跟踪交易——我们大型商业客户的平均交易价值。至关重要的是,我们可以看到是否有任何异常交易。
其中很多都是由机器学习驱动的,机器学习可以对交易进行分类或进行 异常检测。一旦您开始捕获数据,就可以执行许多有用的操作,并且使用灵活的平台,将机器学习模型集成到此过程中会变得轻而易举。
SLI 和 SLO
服务级别指标 (SLI) 和服务级别目标 (SLO) 是维护和增强应用程序性能的关键组成部分。SLI 代表延迟、错误率和吞吐量等关键性能指标,有助于量化应用程序的性能,而 SLO 则建立目标性能级别以满足用户期望。
通过选择相关的 SLI 并设置可实现的 SLO,组织可以使用 APM 工具更好地监控应用程序的性能。不断评估和调整 SLI 和 SLO 以响应应用程序需求、用户期望或竞争格局的变化,确保应用程序保持竞争力并提供卓越的用户体验。
为了定义和跟踪 SLI 和 SLO,APM 成为了理解用户体验所需的关键视角。实施 APM 后,我们建议组织执行以下步骤。
- 定义跟踪 SLI 和 SLO 所需的指标。
- 定义 SLO 预算及其计算方式。反映业务视角并设置实际目标。
- 从用户体验角度定义要衡量的 SLI。
- 定义不同的警报和寻呼规则,仅在面向客户的 SLO 降级时进行寻呼,记录症状警报,并在关键症状警报时通知。
合成监控和最终用户监控 (EUM) 还可以帮助获得更多必要数据,以了解用户视角的延迟、吞吐量和错误率,这对于获取良好的业务重点指标和数据至关重要。
4. 规模和总拥有成本
随着视角的增加,客户经常会遇到可扩展性和总拥有成本问题。所有这些新数据都可能让人不知所措。幸运的是,您可以使用各种技术来解决此问题。追踪本身实际上可以帮助解决容量挑战,因为您可以分解非结构化日志并将其与追踪组合在一起,从而提高效率。您还可以使用不同的采样方法来应对规模挑战(即,我们之前提到的两种技术)。
除此之外,对于大型企业规模,我们可以使用 Kafka 或 Pulsar 等流式管道来管理数据量。这还有一个额外的免费好处:如果您关闭消耗数据的系统或它们面临中断,则丢失数据的可能性较小。
有了这种配置,您的“可观测性管道”架构将如下所示
这会将您的数据源与您选择的可观测性解决方案完全分离,这将使您的可观测性堆栈在未来得到验证,使您能够达到大规模,并减少您对特定供应商数据收集代码的依赖。
我们建议做的另一件事是对检测保持智能化。这将带来两个好处:您将在检测的应用程序中获得一些 CPU 周期,并且您的后端数据收集系统将需要处理的数据更少。例如,如果您知道您对跟踪对特定端点的调用不感兴趣,则可以从检测中排除这些类和方法。
最后,数据分层是一种变革性的数据存储管理方法,可以显著降低企业的总拥有成本 (TCO)。主要地,它允许组织根据其可访问性需求和数据的价值,将数据存储在不同类型的存储介质上。例如,频繁访问的高价值数据可以存储在昂贵的高速存储中,而较少访问的低价值数据可以存储在更便宜、速度更慢的存储中。
这种方法通常包含在云存储解决方案中,通过确保企业仅为在任何给定时间需要的存储付费来实现成本优化。此外,它还提供了根据需求向上或向下扩展的灵活性,无需在存储基础设施上进行大量资本支出。这种可扩展性还减少了为处理潜在的未来需求而进行昂贵的过度配置的需要。
结论
在当今竞争激烈且快节奏的软件开发环境中,仅仅依赖日志记录已不足以确保一流的客户体验。通过采用 APM 和分布式追踪,组织可以更深入地了解其系统,主动检测和解决问题,并保持稳健的用户体验。
在这篇博客中,我们探讨了从仅日志记录方法转变为集成日志、追踪和 APM 的综合可观测性策略的过程。我们讨论了培养以客户体验为优先的新监控心态的重要性,以及推动 APM 和追踪采用所需的必要组织变革。我们还深入研究了此过程的各个阶段,包括数据摄取、集成、分析和扩展。
通过理解和实施这些概念,组织可以优化其监控工作,减少 MTTR,并使客户满意。最终,通过 APM 和追踪优先考虑客户体验可以在当今充满挑战的环境中带来更成功、更具弹性的企业。