作者:来自 Elastic David Hope
日志记录领域即将发生改变。在这篇文章中,我们将概述从单纯的日志记录到包含日志、跟踪和 APM 的完全集成解决方案的推荐流程。
通过 APM 和跟踪优先考虑客户体验
企业软件开发和运营已成为一个有趣的领域。我们拥有一些非常强大的工具,但作为一个行业,我们未能采用许多可以让我们的生活更轻松的工具。目前未充分利用的一种工具是应用程序性能监控 (application performance monitoring - APM) 和跟踪,尽管 OpenTelemetry 已经使采用它成为可能。
然而,日志记录无处不在。每个软件应用程序都有某种日志,故障排除的默认工作流程(即使在今天)是从客户和系统遇到的异常转到日志,然后从那里开始寻找解决方案。
这有各种挑战,其中主要挑战之一是日志通常没有提供足够的信息来解决问题。如今,许多服务返回模棱两可的 500 错误,几乎没有任何线索。如果根本没有错误或日志文件,或者问题是系统非常慢,该怎么办?仅靠日志记录无法帮助解决这些问题。这使得用户只能使用半损坏的系统和糟糕的用户体验。我们都曾遇到过这种情况,这令人非常沮丧。
我发现自己在问一个问题,为什么客户体验往往排在错误之后?如果客户体验是重中之重,那么应该制定策略来采用跟踪和 APM,并使其与日志记录一样重要。用户应该停止默认使用日志,而主要考虑日志,就像现在许多人所做的那样。这也需要对思维模型进行一些必要的改变。
实现这一目标的途径是什么?这正是我们将在这篇博文中探讨的内容。我们将首先讨论支持组织变革,然后概述从单纯的日志记录转向具有日志、跟踪和 APM 的完全集成解决方案的建议历程。
培养新的监控思维:如何推动 APM 和跟踪采用
要让团队转变故障排除思维,需要进行哪些组织变革?
首先,企业应该考虑需要在团队之间广泛分享的战略优先事项和目标。在非常大的组织中,有一件事可以帮助推动这一点,那就是考虑一个致力于可观察性的产品团队或一个拥有自己的路线图和优先事项的 CoE(Center of Excellence - 卓越中心)。
这个团队(无论是虚拟的还是永久的)应该从客户的角度出发,然后逆向工作,从关键问题开始,例如:我需要收集什么?我需要观察什么?我该如何行动?一旦团队成员理解了这些问题的答案,他们就可以开始思考推动这些结果所需的技术决策。
从跟踪和 APM 的角度来看,最令人担忧的领域是客户体验、服务水平目标和服务水平结果。从这里开始,组织可以开始实施工作计划,以不断改进和跨团队共享知识。这将有助于让团队围绕一个具有共同目标的共同框架保持一致。
在接下来的几个部分中,我们将经历一个四步旅程,以帮助你最大限度地利用 APM 和跟踪。此旅程将带你完成成功采用 APM 的以下关键步骤:
- 提取:你必须做出哪些选择才能激活跟踪并开始将跟踪数据提取到可观察性工具中?
- 集成:跟踪如何与日志集成以实现完整的端到端可观察性,除了简单的跟踪之外,你还可以利用哪些其他方法来更好地解决数据问题?
- 分析和 AIOPs:通过机器学习改善客户体验并减少噪音。
- 规模和总拥有成本:推出企业范围的跟踪并采用策略来处理数据量。
1. 采集 - ingest
为 APM 目的采集数据通常涉及 “instrumenting - 检测” 应用程序。在本节中,我们将探讨检测应用程序的方法,讨论一下采样,最后总结一下使用常见模式进行数据表示的注意事项。
开始使用检测
我们有哪些选项可以采集 APM 和跟踪数据?我们将讨论许多选项来帮助指导你,但首先让我们回顾一下。APM 有着悠久的历史 —— 在 APM 的最初实现中,人们主要关注计时方法,如下所示:
通常,你会有一个配置文件来指定要计时的方法,而 APM 实现会使用方法计时来检测指定的代码。
从这里开始,事情开始发展,APM 的第一批新增功能之一就是添加跟踪。
对于 Java,使用所谓的 Java 代理来实现一个系统来执行此操作相当简单。你只需指定 -javagent 命令行参数,代理代码就可以访问 Java 中的动态编译例程,这样它就可以在将代码编译为机器代码之前对其进行修改,从而允许你使用计时或跟踪例程 “wrap - 包装” 特定方法。因此,自动检测 Java 是原始 APM 供应商所做的第一件事。
OpenTelemetry 有这样的代理,大多数提供 APM 解决方案的可观察性供应商都有自己的专有方法来实现这一点,通常具有比开源工具更高级和不同的功能。
从那时起,事情发生了变化,Node.JS 和 Python 现在很流行。
因此,出现了自动检测这些语言运行时的方法,这些方法大多通过在启动代码之前将库注入代码来实现。OpenTelemetry 有一种方法可以在 Kubernetes 上使用 Operator 和 sidecar 来实现这一点,它支持 Python、Node.JS、Java 和 DotNet。
另一种选择是开始在你自己的代码中添加 APM 和跟踪 API 调用,这与添加日志记录功能没有什么不同。你甚至可能希望在代码中创建一个抽象来处理这个横切问题,尽管现在有了可以实现这一点的开放标准,这不再是一个问题。
你可以在下面和此处看到一个示例,了解如何将 OpenTelemetry 跨度(spans)和属性(attributes)添加到你的代码中以进行手动检测。
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 attributes)、异常(exceptions)和指标(metrics)中来消除进行任何日志记录的需要。缺点是你只能使用自己拥有的代码来执行此操作,因此你将无法通过这种方式删除所有日志。
采样 - sampling
许多人没有意识到 APM 是一个昂贵的过程。它会为你的应用程序增加大量 CPU 周期和内存,尽管有很多价值,但肯定需要做出权衡。
你是否应该 100% 地采样所有内容并承担成本?还是应该考虑明智的权衡,使用更少的样本甚至基于尾部的采样,许多产品通常都支持这种采样?在这里,我们将讨论两种最常见的采样技术 —— 基于头部的采样(head-based)和基于尾部的采样(tail-based) —— 以帮助你做出决定。
基于头部的采样
在这种方法中,采样决策是在跟踪开始时做出的,通常是在服务或应用程序的入口点。跟踪的采样速率是固定的,并且此决策会传播到分布式跟踪中涉及的所有服务。
使用基于头部的采样,你可以使用配置来控制速率,从而控制采样并报告给 APM 服务器的请求百分比。例如,采样率为 0.5 意味着只有 50% 的请求被采样并发送到服务器。这对于减少收集的数据量非常有用,同时仍能保持应用程序性能的代表性样本。
基于尾部的采样
与基于头部的采样不同,基于尾部的采样在整个跟踪完成后做出采样决策。这允许根据实际跟踪数据做出更智能的采样决策,例如仅报告有错误的跟踪或超过特定延迟阈值的跟踪。
我们建议使用基于尾部的采样,因为它最有可能减少噪音并帮助你专注于最重要的问题。它还有助于降低数据存储方面的成本。然而,基于尾部的采样的一个缺点是它会导致从 APM 代理生成更多数据。这可能会占用应用程序的更多 CPU 和内存。
OpenTelemetry 语义约定和 Elastic Common Schema
OpenTelemetry 规定了语义约定或语义属性,以便为各种操作和数据类型建立统一的名称。遵守这些约定有助于实现代码库、库和平台之间的标准化,最终简化监控流程。
创建 OpenTelemetry 跨度进行跟踪非常灵活,允许实施者使用特定于操作的属性对其进行注释。这些跨度表示系统内部和系统之间的特定操作,通常涉及广泛认可的协议,如 HTTP 或数据库调用。为了有效地表示和分析监控系统中的跨度,需要补充信息,具体取决于协议和操作类型。
统一不同语言的归因方法对于操作员来说至关重要,这样他们就可以轻松地关联和交叉分析来自多语言微服务的遥测数据,而无需掌握特定于语言的细微差别。
Elastic 最近将 Elastic Common Schema 贡献给 OpenTelemetry,增强了语义约定以涵盖日志和安全性。
遵守共享模式可带来巨大好处,使操作员能够快速识别复杂的交互并关联日志、指标和跟踪,从而加快根本原因分析并减少搜索日志和确定特定时间范围所花费的时间。
我们提倡在应用程序中定义跟踪、指标和日志数据时遵守 ECS 等既定模式,尤其是在开发新代码时。这种做法将在解决问题时节省时间和精力。
2. 集成 - integrate
集成对于 APM 非常重要。你的解决方案与其他工具和技术(如云)的集成程度,以及将日志和指标集成到跟踪数据中的能力,对于充分了解客户体验至关重要。此外,大多数 APM 供应商都有用于综合监控(synthetic monitoring)和分析的邻近解决方案,以便获得更深入的视角来增强你的 APM。我们将在下一节中探讨这些主题。
APM + 日志 = 超能力!
由于 APM 代理可以检测代码,因此它们也可以检测用于日志记录的代码。这样,你可以直接在 APM 中捕获日志行。这通常很容易启用。
启用此功能后,你还可以自动注入以下有用的字段:
- service.name、service.version、service.environment
- trace.id、transaction.id、error.id
这意味着日志消息将自动与事务(transaction)相关联,如下所示,从而更容易减少平均解决时间 (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,许多供应商也有自己的运算符或 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 收集器 — 因此这可能是开销最低的解决方案。了解有关此选项的更多信息。
合成通用分析 - sythetic universal profiling
大多数 APM 供应商都为主要 APM 用例添加了附加组件。通常,我们会看到合成和持续分析被添加到 APM 解决方案中。APM 可以与两者集成,将这些技术结合在一起以提供更多问题见解具有一定的价值。
合成
合成监控是一种通过模拟用户交互和流量来衡量 Web 应用程序、网站和 API 的性能、可用性和可靠性的方法。它涉及创建模拟真实用户行为的脚本或自动化测试,例如浏览页面、填写表单或单击按钮,然后从不同位置和设备定期运行这些测试。
这使开发和运营团队能够比他们可能更早地发现问题,在许多情况下比真实用户更早发现问题。
合成可以与 APM 集成 - 在脚本运行时将 APM 代理注入网站,因此即使你最初没有将最终用户监控放入你的网站,也可以在运行时注入。这通常发生在没有任何用户输入的情况下。从那里,每个请求的跟踪 ID 可以通过系统的各个层传递下去,从而允许团队从合成脚本一直跟踪请求到应用程序堆栈的最低层(例如数据库)。
通用分析 - universal profiling
“分析”是一种分析程序复杂性的动态方法,例如 CPU 利用率或函数调用的频率和持续时间。通过分析,你可以准确找到应用程序中消耗最多资源的部分。“持续分析 - Continuous profiling ” 是分析的更强大版本,它增加了时间维度。通过了解系统随时间变化的资源,你可以找到、调试和修复与性能相关的问题。
通用分析是对此的进一步扩展,它允许你始终捕获有关系统中运行的所有代码的分析信息。使用 eBPF 之类的技术可以让你查看系统中的所有函数调用,包括 Kubernetes 运行时之类的内容。这样做可以让你最终看到未知的未知数 —— 你不知道是问题的东西。这与 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
服务级别指标 (service level indicators - SLIs) 和服务级别目标 (service level objectives - SLOs) 是维护和增强应用程序性能的关键组件。SLI 代表关键性能指标,例如延迟、错误率和吞吐量,有助于量化应用程序的性能,而 SLO 则建立目标性能级别以满足用户期望。
通过选择相关的 SLI 并设置可实现的 SLO,组织可以使用 APM 工具更好地监控其应用程序的性能。根据应用程序要求、用户期望或竞争格局的变化不断评估和调整 SLI 和 SLO 可确保应用程序保持竞争力并提供卓越的用户体验。
为了定义和跟踪 SLI 和 SLO,APM 成为了解用户体验所需的关键视角。实施 APM 后,我们建议组织执行以下步骤。
- 定义跟踪它们所需的 SLO 和 SLI。
- 定义 SLO 预算及其计算方式。反映业务观点并设定切合实际的目标。
- 从用户体验的角度定义要衡量的 SLI。
- 定义不同的警报和寻呼规则,仅在面向客户的 SLO 降级时寻呼,记录症状警报,在关键症状警报时通知。
综合监控和最终用户监控 (EUM) 还可以帮助从用户的角度获取了解延迟、吞吐量和错误率所需的更多数据,这对于从中获得良好的业务重点指标和数据至关重要。
4. 规模和总拥有成本
随着视角的扩大,客户经常会遇到可扩展性和总拥有成本问题。所有这些新数据可能会让人不知所措。幸运的是,你可以使用各种技术来处理这个问题。跟踪本身实际上可以帮助解决容量挑战,因为你可以分解非结构化日志并将其与跟踪相结合,从而提高效率。你还可以使用不同的采样方法来应对规模挑战(即我们之前提到的两种技术)。
除此之外,对于大型企业规模,我们可以使用 Kafka 或 Pulsar 等流式管道来管理数据量。这还有一个额外的好处,你可以免费获得:如果你关闭使用数据的系统或它们面临中断,你丢失数据的可能性就会降低。
有了这种配置,你的 “可观察性管道” 架构将如下所示:
这将彻底将你的数据源与你选择的可观察性解决方案分离开来,这将为你的可观察性堆栈提供未来保障,使你能够实现大规模,并减少对特定供应商代码的依赖,从而收集数据。
我们建议你做的另一件事是智能地进行检测。这将带来两个好处:你将在被检测的应用程序中重新获得一些 CPU 周期,并且你的后端数据收集系统将有更少的数据需要处理。例如,如果你知道你不想跟踪对特定端点的调用,则可以将这些类和方法从检测中排除。
最后,数据分层是一种管理数据存储的变革性方法,可以显着降低企业的总拥有成本 (total cost of ownership - TCO)。首先,它允许组织根据其可访问性需求和数据的价值将数据存储在不同类型的存储介质中。例如,经常访问的高价值数据可以存储在昂贵的高速存储中,而访问频率较低的低价值数据则可以存储在更便宜、更慢的存储中。
这种方法通常包含在云存储解决方案中,通过确保企业只需支付他们在任何给定时间所需的存储费用,可以实现成本优化。此外,它还提供了根据需求扩展或缩减的灵活性,无需在存储基础设施上投入大量资本支出。这种可扩展性还减少了为满足潜在未来需求而进行昂贵的过度配置的需要。
结论
在当今竞争激烈、节奏飞快的软件开发领域,仅仅依靠日志记录已不足以确保一流的客户体验。通过采用 APM 和分布式跟踪,组织可以更深入地了解其系统,主动检测和解决问题,并保持强大的用户体验。
在本博客中,我们探讨了从仅日志记录方法转变为集成日志、跟踪和 APM 的全面可观察性策略的过程。我们讨论了培养优先考虑客户体验的新监控思维的重要性,以及推动 APM 和跟踪采用所需的必要组织变革。我们还深入研究了旅程的各个阶段,包括数据提取、集成、分析和扩展。
通过理解和实施这些概念,组织可以优化其监控工作,减少 MTTR,并让客户满意。最终,通过 APM 和跟踪优先考虑客户体验可以让企业在当今充满挑战的环境中更成功、更具弹性。
在 Elastic 了解有关 APM 的更多信息。
原文:Gaining new perspectives beyond logging: An introduction to application performance monitoring — Elastic Observability Labs