mcpField
O
observability-sre-triage

observability-sre-triage

github.com/elastic云与 DevOps6021.1k 次安装今天更新
代码仓库

关于 observability-sre-triage

observability-sre-triage 是面向 SRE 的服务故障排查技能,依据 SLO、告警、黄金信号、依赖和日志逐层判断服务状态与故障位置。它使用 elastic CLI 查询 Elasticsearch,并要求结论严格依据现有遥测证据。

主要功能

  1. 01按信号层级判断服务状态
  2. 02分析 SLO、错误预算与告警
  3. 03用延迟、错误率和吞吐量定位症状
  4. 04检查依赖与基础设施信号
  5. 05通过日志筛选补充故障解释
  6. 06区分遥测缺失与服务健康

适用场景

  1. 01判断服务健康、降级或不健康
  2. 02定位变慢或错误率升高的环节
  3. 03分析 SLO 燃烧与告警触发原因
  4. 04筛选解释故障的日志和请求子群

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/elastic/agent-skills --skill observability-sre-triage

安装后在 Claude Code 中输入/observability-sre-triage确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
apm-signals.md
log-investigation.md
slo-and-alerts.md

详细介绍

SRE 服务排查

判断服务处于健康、降级还是不健康状态,并说明原因。排查是分层的,而不是核对清单:SLO 和告警用于判断服务是否违反了自身承诺,基于追踪数据得出的黄金信号描述了故障的表现,依赖项和基础设施解释了故障的来源,日志则提供了可写入事件频道的说明。按层级逐步排查,直到证据足以支持判断,然后停止。

编写和调整 SLO 定义、错误率消耗规则及告警阈值时,请使用 observability-service-reliability skill。此 skill 仅读取这些状态。对于 Kubernetes 工作负载、节点或控制平面的诊断——例如重启循环、确认 OOM kill、节点压力、准入拒绝、发布卡住——请交由 observability-k8s-investigation skill 处理。此 skill 用于检查 Kubernetes 托管的服务是否已饱和;它不会诊断其背后的 Pod 或节点为何发生故障。

环境配置

此 skill 通过 elastic CLI 执行 Elasticsearch 操作。如果未安装 elastic CLI,请告知用户需要安装它的原因。不要猜测凭据、直接调用 HTTP API,或尝试其他变通方法。

此 skill 以 HTTP 简写形式引用操作(例如,GET /、GET /_cat/indices、GET /{index}/_mapping、 GET /{index}/_settings/index.mode、POST /_query)。本文末尾的 Operations 表格将每个简写映射到相应的 elastic CLI 命令——始终使用 CLI,而不是直接调用 HTTP API。

无法访问集群时的分析

上述 CLI 检查只限制_查询集群_,不限制分析。当用户已在问题中提供证据(指标值、计数、状态原因、日志行、告警负载、配置)时,应基于这些证据进行推理并给出结论。

如果确实需要用户未提供的数据,仍要说明你会检查什么以及如何检查——指出能够解决问题的具体查询、索引和字段,然后请求配置 CLI。指出要检查的内容,即使无法访问集群也有帮助;只请求配置则不然。

本文所有操作均使用 ES|QL(POST /_query)或 Kibana Observability API。不要使用 Query DSL,也不要使用 ES|QL KQL 搜索函数——应使用原生方式表达谓词(WHERE service.name == "checkout")。

待完成的工作

  • 判断“服务 X 是否健康?”,给出结论及其依据
  • 判断“服务 X 为什么变慢/报错/没有活动?”,将变化定位到服务、依赖项或其基础设施
  • 在事件期间查看 SLO 状态、错误率消耗和剩余错误预算
  • 确定当前适用于某个服务的告警规则,包括适用于所有服务的规则
  • 从嘈杂的日志流中筛选出能够解释服务降级的故障
  • 找出能够区分故障或缓慢子群体的属性(版本、主机、Pod、区域、路由)
  • 区分健康的服务和没有遥测数据的服务

输出规范

适用于此技能下生成的每条回复。

  • 坚持最有证据支持的结论。 当证据指向一个方向时,就明确说明。不要为了显得谨慎而降低确信程度——面对明确无误的证据仍含糊其辞,是缺陷,不是谦逊。
  • 给出判定:健康、降级或不健康,并说明原因。未明确指出这三种状态之一的故障排查回答,就没有完成任务。
  • 只在结论中说明一次确信程度。 不要在每条要点中重复。
  • 不要超出证据范围进行猜测。 如果遥测数据没有显示原因,就不要在回答中提及。指出未知之处,然后到此为止。绝不要提出任何未经信号测量的机制(例如“可能是 GC 暂停”“可能是邻居干扰”)。
  • 如实报告缺失情况。 零行意味着数据缺失或未采集;绝不意味着底层状况健康。“没有依赖项指标”不等于“依赖项没问题”。
  • 不要凑字数。 不要复述问题,不要叙述运行了哪些查询(除非结果与结论相关),也不要总结摘要。
  • 以发现结论收尾。 不要在末尾提出“要我继续深入调查吗?”之类的建议。可执行的后续事项应列在建议清单中,并以建议的形式表述,而不是提问。

信号层级

信号之间经常相互矛盾。按以下顺序决定以哪个信号为准。

排名信号权威性
1SLO 状态和消耗速率存在 SLO 时具有权威性。它们体现了针对该服务约定的“良好”定义
2活跃告警规则没有 SLO 覆盖该症状时具有权威性。数据来源为 Alerting API
3错误率、延迟、吞吐量描述降级情况。仅当不存在上述更高优先级信号时才具有决定性
4依赖项健康状况定位上游或下游原因;本身不决定判定结果
5ML 异常与学习到的基线相比的偏差,而非与目标相比的偏差。用于佐证并限定时间范围
6基础设施(CPU、内存、OOM)解释机制。黄金信号正常时,饱和的 Pod 是风险,不代表服务中断
7日志用于解释,绝不用于判定。日志量不代表健康状况

冲突规则:

  • SLO 健康,延迟升高 → 已降级,但仍处于错误预算范围内。判定以 SLO 为准;将趋势作为风险报告,并附上消耗速率。
  • SLO 违规,但当前时间窗口内的指标看起来正常 → 相信 SLO,并检查其时间窗口。SLO 按小时或天评估;即使 15 分钟的 ES|QL 窗口看起来正常,预算也可能已经耗尽。
  • 告警触发,但未定义 SLO → 以告警作为判定结果。将每条规则的 params 解析出来,确认其实际针对该服务后再归因。
  • 日志噪声大,黄金信号平稳 → 未降级。日志量大但错误率或延迟没有变化,这是日志配置问题,不是健康状况问题。
  • 吞吐量骤降,错误率平稳 → 调用方停止了调用。应检查上游,再判断是否归咎于此服务。
  • 任何查询返回零行 → 数据缺失。指出哪个信号不可用,并相应缩小判定范围;绝不要把无数据解释为健康。

路由:症状到首个信号

当前症状首先获取参考
“X 是否正常?”/ 不明确SLO 状态,然后是活跃规则,最后是黄金信号slo-and-alerts.md
“X 很慢”与上一时段相比的延迟百分位数,然后是依赖项延迟apm-signals.md
“X 出错了”/ 5xx按路由统计的错误率,然后是失败事务关联apm-signals.md
“X 已宕机”/ 没有流量吞吐量,然后确认服务是否仍在接收任何数据apm-signals.md
“只有部分请求有问题”根据候选属性对问题子群进行关联apm-signals.md
“触发了警报”/“SLO 正在消耗预算”规则 params 和 SLO 消耗速率,然后是规则监控的指标slo-and-alerts.md
“日志里有什么?”/ 日志嘈杂日志漏斗——通过 NOT 排除条件迭代筛选log-investigation.md
怀疑 OOM、节流、重启容器 CPU 和内存限制利用率apm-signals.md
在非 K8s 主机上“是否已饱和?”从 hostmetrics 接收器获取主机 CPU、内存和平均负载apm-signals.md
“哪个下游服务拖累了 X?”按目标统计调用量、延迟和故障率apm-signals.md

数据源

已根据 Elasticsearch 9.6.0 验证的 OTel 原生数据流:

数据索引模式
跟踪(跨度、事务)traces-*.otel-*;经典 Elastic APM Agent 摄取的数据也会写入 traces*apm*
日志logs-*.otel-*
原始指标metrics-*.otel-*;经典 APM Agent 摄取的数据也会写入 metrics*apm*
服务清单(1 分钟汇总)metrics-service_summary.1m.otel-*
事务汇总(1 分钟)metrics-service_transaction.1m.otel-*、metrics-transaction.1m.otel-*
依赖项汇总(1 分钟)metrics-service_destination.1m.otel-*
Kubernetesmetrics-kubeletstatsreceiver.otel-*、metrics-k8sclusterreceiver.otel-*、logs-k8seventsreceiver.otel-*
主机(VM、裸机)metrics-hostmetricsreceiver.otel-*;Elastic Agent 系统集成的数据会写入 metrics-system.*

跟踪、指标和日志都会填充 service.name,因此它是三者之间的关联键。在 ES|QL 中使用扁平的 OTel 字段路径(k8s.pod.name,而不是 resource.attributes.k8s.pod.name)。分析 OTel 应用指标时,ES|QL 的 TS(时间序列)命令可以更高效地查询指标。它在 Serverless 上已正式发布;在 Stack 上,9.2 中为预览版,9.4 中正式发布,因此低于 9.4 的版本请改用 FROM 和 BUCKET。TS 也不接受 COUNT(*)——请改为统计某个字段。

此技能及其参考资料中的操作示例均针对上述 OTel 原生数据流编写。使用经典 Elastic APM Agent 进行插桩的服务会将数据写入 traces-apm* 和 metrics-apm*,且字段名称不同(transaction.duration.us、event.outcome),因此这些操作示例不会返回该服务的数据。如果某项服务显然仍在正常运行,但结果为空,这说明查询范围存在边界,并不能证明服务发生了故障:请检查该服务实际写入哪个索引系列(GET /_cat/indices),并报告数据摄取路径,而不要根据无结果就下结论。

ES|QL 功能可用性

此技能使用的三个功能均比其 8.11 基础版本更新。依赖这些功能前,请检查 GET /: build_flavor: "serverless" 表示三者均可用;否则请根据 Stack 列对比 version.number。 如果实际情况是查询未运行,绝不要报告“无数据”——应说明哪个功能不可用,并使用 备用方案。

功能ServerlessStack许可证用于备用方案
FORKGApreview 9.1-9.3, GA 9.4+任意日志漏斗和子群体比较将每个分支作为单独查询运行,并自行合并结果
CATEGORIZEGApreview 9.0, GA 9.1Platinum日志漏斗中的消息分类按截断后的消息前缀分组,或基于结构化错误字段构建漏斗
TSGApreview 9.2, GA 9.4任意OTel 应用指标查询对同一数据流使用 FROM 和 BUCKET

CATEGORIZE 所需的 Platinum 许可证不是版本检查。许可证为 Basic 或 Gold 的 9.6 Stack 集群会像 8.11 集群无法使用 FORK 一样,无法使用该功能;错误信息会指出许可证问题,而非语法问题。在 Serverless 上, 该函数为 GA,且没有单独的许可证限制。

流程:排查服务降级问题

  1. 确定服务和时间范围。 根据请求解析服务名称和时间范围。使用用户指定的时间 范围——如果问题发生在过去,不要默默地假定范围是最近一小时。如果未指定范围,则使用最近一小时,并说明这一点。通过 POST /_query 在 traces-*.otel-* 上执行 COUNT(*) BY service.name,确认遥测数据中确实存在该服务; 如果没有出现该名称,请先消除歧义,再继续查询。

    决策:后续所有查询所限定的服务和时间范围。数据:范围内不同的 service.name 值。

  2. 查看 SLO 状态和消耗速率。 使用 GET kbn:/api/observability/slos 列出 SLO,并使用 GET kbn:/api/observability/slos/{id} 获取绑定到该服务的 SLO。查看状态、当前 SLI、消耗速率和剩余错误预算。

    决策:是否存在双方认可的服务目标,以及该目标是否遭到违反?如果是,结论已经确定,后续步骤仅用于解释原因。如果该服务没有 SLO 覆盖,请说明一次,然后继续执行步骤 3。

  3. 确定哪些告警规则适用于此服务,以及其中哪些正在触发。 调用 GET kbn:/api/alerting/rules/_find,并传入 per_page=100&filter=alert.attributes.enabled:true;如果 total 超过已收到的数量,则使用 page 分页。然后在客户端筛选响应。不要查询 .alerts* 索引来 确定活动状态——告警 API 响应才是事实来源。需要规则的 params 时,使用 GET kbn:/api/alerting/rule/{id} 获取其完整定义。

    不要在服务端缩小此调用的范围。 _find 的 filter 参数是针对已保存对象 attributes 的 KQL, 而 params 不属于这些属性——在确实存在相关规则的集群上,filter=alert.attributes.params.serviceName:<name> 仍会返回零条规则。 使用 search=apm&search_fields=tags、alertTypeId 或 consumer 进行范围缩小则更糟:这会依据命名约定或规则类型允许列表丢弃规则,而被丢弃的规则中尤其常见的是适用于所有服务的规则。 请参阅 references/slo-and-alerts.md,了解实测失败情况。 在获取的集合中,同时评估 params.serviceName 与该服务匹配的规则,以及未设置 params.serviceName 的规则,因为后者是同样适用于该服务的全服务规则。读取每条规则的 execution_status.status:active 表示规则上次运行产生了告警,ok 表示规则已运行但未产生告警,error 表示规则完全未在评估——这是盲区,而不是通过。

    判断:有哪些规则覆盖了该服务,其中是否有规则当前正在触发?数据:规则 params.serviceName、规则类型和执行状态。

  4. 检查 ML 异常(如有作业)。 使用 GET /_ml/anomaly_detectors 列出作业,并使用 GET /_ml/anomaly_detectors/_stats 确认它们正在运行——已停止的作业不会产生异常,这与没有异常并不是一回事。使用 GET /_ml/anomaly_detectors/{id}/results/records 获取评分记录。

    判断:延迟、吞吐量或错误率是否偏离了其学习到的基线,何时发生?使用异常时间窗口缩小步骤 5 和 6 的范围。

  5. 衡量黄金信号。 对 traces-*.otel-* 运行 ES|QL,统计吞吐量、延迟(平均值、p95、p99)和错误率,按时间窗口分桶,并与紧邻其前、时长相同的窗口进行比较。请参阅 references/apm-signals.md。

    判断:与服务自身相比,服务是否确实发生了变化,变化体现在哪个维度?数据:当前和先前时间窗口的请求数、延迟百分位数和失败率。

  6. 定位问题:先检查依赖,再检查子群体,最后检查基础设施。

    • 依赖 — 按 span.destination.service.resource 聚合 metrics-service_destination.1m.otel-*,统计调用量、平均延迟和失败率。如果此查询未返回该服务的任何行,则表示该服务的依赖未进行 APM 检测;请报告依赖数据不足,不要声称上游服务运行正常。
    • 子群体 — 如果只有部分流量存在问题,请按候选属性比较失败率或慢请求率与总体比率,找出占比异常高的属性。请参阅 references/apm-signals.md。
    • 基础设施 — 首先读取服务 span 上的资源属性(k8s.pod.name、container.id、host.name),再根据属性内容选择后续检查。如果存在 Pod 和命名空间属性,则表示服务运行在 Kubernetes 上:检查 metrics-kubeletstatsreceiver.otel-* 中的 k8s.container.cpu_limit_utilization 和 k8s.container.memory_limit_utilization。如果存在 host.name 但没有 Pod 属性,则表示服务运行在 VM 或裸机上,此时所有 k8s.* 字段都为空:改为检查 metrics-hostmetricsreceiver.otel-* 中的 system.cpu.utilization、system.memory.utilization 和 system.cpu.load_average.1m。OOM 终止、CPU 限流和主机饱和都会直接降低 APM 健康状况。请参阅 references/apm-signals.md。
    • 近期变更 — 部署是导致阶跃变化的最常见原因。使用 GET kbn:/api/apm/services/{serviceName}/annotation/search,在事件时间窗口内搜索该服务的部署注释,并在子群体查询中按 service.version 比较失败率或延迟率。若注释位于问题开始的时间窗口内,则存在较强的相关性;在归因之前,请确认它能够合理解释该症状。

    判断:原因是在此服务内部、在它调用的某项服务中、在一部分实例中、在其下层基础设施中,还是由某项已上线的变更引起?

    如果 Kubernetes 分支检查发现饱和、重启或 OOM 终止,则已确定故障机制;剩余诊断——Pod 为何被终止、节点是否承受压力、发布是否卡住——属于 observability-k8s-investigation 技能。请移交处理,不要继续在此处诊断。

  7. 结合日志说明。 按 service.name 限定日志范围;如果已知具体的故障 Trace,则按 trace.id 限定范围,然后运行漏斗分析,直到剩余结果集小到可以逐条阅读。请参阅 references/log-investigation.md。日志用于确认并阐明原因;不能推翻步骤 2 和 3 的结论。

  8. 给出结论。 判断状态为健康、降级或不健康,并说明原因和置信度,随后给出建议。注明任何不可用的信号。

示例

“结账服务健康吗?” — 确定时间窗口,读取其 SLO,然后读取活动规则(包括适用于所有服务的规则),再对照前一时间窗口,查询 traces-*.otel-* 中的吞吐量、延迟百分位数和错误率。如果可用性 SLO 为 99.2%,目标为 99.5%,且消耗速率高于 1,则结论为不健康,原因是违反了 SLO;黄金信号用于解释原因,而非决定结论。

“前端为什么这么慢?” — 比较当前时间窗口与等长的上一时间窗口的 p95 和 p99。如果服务级延迟上升,而 metrics-service_destination.1m.otel-* 中按目标服务划分的延迟持平,则增加的耗时发生在服务内部;如果某个目标服务的平均响应时间也同步上升,则该依赖服务就是原因,前端则是受影响方。

“只有部分结账请求失败” — 执行子群体对比:按 service.version、k8s.pod.name、host.name 和 cloud.region 分组查看失败率,并与总体失败率对比。如果某个属性值的失败率达到总体的数倍,且请求量足以构成影响,那么该属性值就是相关因素。在实时数据中,按路由对前端服务器 Span 分组后发现,POST 请求的慢请求率为 3.8%,总体为 0.9%——高出 4 倍,因此可以将问题定位到写入路径。

“购物车服务的日志看起来很糟” — 在 logs-*.otel-* 上运行漏斗分析,并限定 service.name == "cart":通过一次 FORK 获取趋势、总量、样本和消息分类,然后针对每种主要模式添加 NOT ... LIKE 排除条件,并使用累积的完整过滤条件重新运行,直到剩余模式少于 20 种。日志量大本身并不能作为服务健康状况的结论——在判定服务降级前,先检查黄金信号。

“支付服务的上游健康吗?” — 查询该服务的 metrics-service_destination.1m.otel-*。如果没有结果,说明该服务不会发出依赖指标。请报告该服务的依赖数据不可用,并根据现有信号给出结论;不要报告上游健康。

“api-gateway 触发了警报” — 不使用服务端筛选,获取已启用的规则,然后在内存中匹配 params.serviceName == "api-gateway" 以及没有 params.serviceName 的规则,并读取 execution_status.status,查看哪些规则正在触发。通过 GET kbn:/api/alerting/rule/{id} 读取触发规则的阈值,然后在 ES|QL 中对相同时间窗口查询相同指标,确认该规则描述的是真实变化,而不是阈值设置得过于严格。

指南

  • 按顺序检查信号层级,并在证据足以支持结论时停止。不要对每个请求都运行本文档中的所有查询。
  • 如果存在 SLO,以 SLO 状态和消耗速率为依据。如果不存在,则依次参考告警、ML 异常、吞吐量、延迟、错误率、依赖项、基础设施和日志,并说明该服务没有 SLO 覆盖。
  • 使用告警 API 获取活跃告警状态。绝不为此查询 .alerts* 索引。始终同时评估服务范围的规则,以及没有 params.serviceName 的规则。
  • 获取告警规则时不要缩小范围,并在客户端进行筛选。_find 无法按 params 筛选,标签搜索会遗漏不遵循命名约定的规则,而 executionStatus.status:active 只会返回当前正在触发的规则——这些方式都会悄然隐藏上一条所要求的适用于所有服务的规则。
  • 始终使用用户指定的时间范围。将每项指标与紧邻其前、时长相同的时间窗口进行比较——没有基线的绝对数值不足以支持结论。
  • 零行表示缺少数据。指出哪个信号不可用,不要把没有数据当作通过。
  • 每个查询都要按 service.name 和有界的 @timestamp 范围限定,并使用 LIMIT 限制输出。只需查看趋势时,优先使用较粗的时间桶。
  • 统计失败的跨度时,优先使用 event.outcome == "failure";在 OTel 跟踪数据中,status.code == "Error" 与之等效,但成功记录中的该字段为 null,因此无法直接计数。
  • 衡量服务自身的吞吐量和延迟时,使用 kind == "Server" 筛选服务端流量,避免重复计算客户端跨度。
  • 将 log.level 和 severity_text 仅视为提示,绝不能依赖它们进行筛选。在真实的 OTel 数据中,大多数日志记录完全没有级别;即使有,大小写和词汇也不一致(INFO、Information、SEVERE、Normal)。尤其绝不要写 log.level == "error"——ECS 的小写词汇并非 OTel SDK 发出的内容,因此即使服务正在记录错误,也会返回零行且不报错,进而误判服务健康。如果确实需要严重性谓词,请使用标准化的数值 severity_number >= 17。
  • 日志用于解释,不用于决策。绝不能仅凭日志内容得出结论。
  • 不要臆造字段名称。如果某个字段可能在此部署中不存在,请先使用 GET /_resolve/index/{pattern} 确认数据流存在,再基于该字段构建查询。
  • 检查饱和度之前,先确定服务的运行位置。Kubernetes 与主机遥测数据使用的字段名称完全不同,因此对 VM 托管的服务执行 Kubernetes 查询会返回零行,但这并不能说明服务是否达到饱和。
  • 当 POST /_query 的结果中大多数列为空时,传入 --drop-null-columns。基础设施指标天然稀疏——没有声明限制的地方就不会有利用率数据。此标志会在 all_columns 下列出被抑制的列名,从而压缩无用信息,同时确保没有内容被隐藏。

操作

HTTP API(简写)elastic CLI 命令
GET /elastic es info
POST /_queryelastic es esql query --format tsv --query '<esql>'
GET /_resolve/index/{pattern}elastic es indices resolve-index --name '<pattern>'
GET /_ml/anomaly_detectorselastic es ml get-jobs
GET /_ml/anomaly_detectors/_statselastic es ml get-job-stats
GET /_ml/anomaly_detectors/{id}/results/recordselastic es ml get-records --job-id '<id>'
GET kbn:/api/observability/sloselastic kb slo find-slos-op --space-id '<space>' --kql-query '<kql>'
GET kbn:/api/observability/slos/{id}elastic kb slo get-slo-op --space-id '<space>' --slo-id '<id>'
GET kbn:/api/alerting/rules/_findelastic kb alerting get-alerting-rules-find --filter '<filter>'
GET kbn:/api/alerting/rule/{id}elastic kb alerting get-alerting-rule-id --id '<id>'
GET kbn:/api/apm/services/{serviceName}/annotation/searchelastic kb apm-annotations get-annotation --service-name '<service>' --environment '<env>' --start '<iso8601>' --end '<iso8601>'

SLO 查找命令接受 KQL 查询字符串,这是因为 API 约定如此;这并不违反数据查询使用 ES|QL 的规则。

注释搜索路由会拒绝缺少 environment 的请求,因此在服务的环境未知时,请传入 ENVIRONMENT_ALL。此处仅涉及搜索操作:该技能是只读的,因此不会绑定配套的创建注释操作。

评论

支持 Markdown