mcpField
P
performance-rca

performance-rca

github.com/grafana开发295128 次安装1 天前更新
代码仓库

关于 performance-rca

使用 gcx profiles 调查性能问题的技能,面向需要定位服务成本变化的工程师。它按工作负载、代码路径和证据逐步分析,并要求明确记录无法确认的结论。

主要功能

  1. 01限定配置文件与时间窗口
  2. 02筛选合格的基线对比
  3. 03定位工作负载切片
  4. 04排名函数并追踪调用路径
  5. 05生成基于证据的报告

适用场景

  1. 01调查疑似服务性能回退
  2. 02将 CPU 或分配开销归因到代码路径
  3. 03报告性能分析覆盖缺口

安装

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

$npx skills add https://github.com/grafana/skills --skill performance-rca

安装后在 Claude Code 中输入/performance-rca确认已加载。

附带文件references/advanced-analysis.md

详细介绍

性能根因分析

说明观察到的性能问题:哪些工作负载发生了变化,哪些代码路径导致了问题,以及哪些证据支持这一机制。热点是一个位置;异常是一条线索;两者单独都不能证明根因。结论应止于证据所能支持的最有力判断,也包括无法得出结论的情况。代码变更和主动优化是两项独立任务。

调查约定

  1. 按顺序执行第 1–5 节。复用较早步骤或相关调查中已验证的证据;不要仅为满足顺序而重复发现。解读结果前,先确认该步骤的先决条件。
  2. 比较前,先固定上下文、数据源、UTC 时间窗口、选择器和剖析类型。发现缺失的标识符;绝不臆造。记录对范围或比较所做的任何有依据的变更。
  3. 在声称存在回归前,先按第 2 节确认对照组符合要求。若没有对照组,则继续进行当前成本归因,并标注比较缺失。
  4. 区分空结果、不支持的功能和查询失败。对受影响的剖析路径应用第 1 节的退出条件;仅在满足先决条件时使用其中记载的回退方案。
  5. 明确选择输出格式:发现和指标使用 JSON,函数排序使用 table,调用方与被调用方追踪使用 DOT,导出使用 pprof。保留诊断信息和限制。
  6. 始终生成第 5 节的报告,包括退出后。对不受支持的字段,填写 unknown、not measured 或 not applicable,并说明原因。测量缺失绝不能成为服务运行正常的证据。

1. 保留问题并检查可用的配置文件

复用用户或引用技能提供的上下文、数据源、服务、症状、事件时间范围、部署事实以及跟踪/配置文件链接。确定绝对 UTC 事件时间和基准时间。根据带时间戳的症状证据推导缺失的事件时间范围;如果无法推导,则先询问,再执行查询。在远程命令中保留相同的 --context <context>(下方省略),并显式传入数据源 UID。调查过程保持只读;不要切换上下文或更改采集设置。

gcx help-tree profiles -o text

以下示例需要 gcx v1.3.0 或更高版本;使用 gcx --version 检查。如果未安装 gcx,或版本较旧,请先让用户从 https://github.com/grafana/gcx 安装或升级,然后再继续。不要臆测命令重命名,也不要悄悄移除时间筛选条件。后端仍可能不支持某些命令、标志和输出格式;第 4 节和高级参考资料介绍了相应的备用方案。

# Discover only if the datasource UID is unknown; choose the target environment.
gcx datasources list -t grafana-pyroscope-datasource -o json
gcx profiles data-range -d <pyro-uid> -o json
gcx profiles list-profile-types -d <pyro-uid> \
  --from <incident-from> --to <incident-to> -o json

CLI 支持不代表后端也支持。data-range 会返回租户范围内的 dataIngested、oldestProfileTime 和 newestProfileTime(Unix 毫秒;0 表示未知)。这些边界不能证明所选服务或视角具有连续覆盖。不要假设存在固定的保留期限。

继续操作前,先按以下结果处理。每种退出情况都会停止受影响的配置文件查询,并生成第 5 节的报告;其他可用信号仍可通过 gcx 内置的 debug-with-grafana 技能继续分析(gcx agent skills get debug-with-grafana),并明确提出缺失证据所对应的问题。

  • 退出 A — 性能分析不可用: 没有目标数据源、缺少访问权限,或必需的 CLI 功能不可用。记录确切的限制;如果缺少配置或访问权限,使用 gcx login 和 gcx config check;如果是 CLI 版本不兼容,则使用兼容的 gcx 构建版本。
  • 退出 B — 未报告数据写入: 成功返回的统计信息显示 dataIngested: false。报告检测或数据方面的缺口,而不是服务健康状况。
  • 退出 C — 事件时间未覆盖: 事件时间与已知的可查询边界没有重叠。报告这两个时间范围;不要用今天替代事件时间。若只有部分重叠,则仅分析重叠部分,记录覆盖缺口,并将结论限定在已观察到的时间范围内。
  • 退出 D — 没有可用的事件配置文件: 根据已发现的事实检查选择器、时间范围和视角后,事件样本仍缺失或为 0。仅基准数据缺失应按第 2 节处理,不属于此退出情况。报告尚未解决的覆盖/检测缺口,而不是证明成本为 0。

当统计信息缺失、不受支持或边界未知时,继续进行有范围限制的检查。仅缺少统计信息不得阻止可用的类型/配置文件查询。这些查询仍须匹配到样本,才能用于归因。

对于查询错误,保留操作和诊断信息。修正已确认的选择器/标志错误;遇到不受支持的功能时,使用文档说明的备用方案。不要重试身份验证失败,也不要盲目更换选择器。查询失败不等同于成功返回空结果。

2. 选择分析视角并进行合格的比较

从发现结果中复制准确的 profile 类型 ID,包括其单位。先发现标签, 不要假设 Prometheus job、Tempo 服务名、Kubernetes 工作负载和 Pyroscope service_name 的值完全相同。

症状可查找的分析视角解读限制
CPU 使用率高或受 CPU 限制的延迟CPU 时间无法衡量所有请求延迟、调度延迟或限流情况
分配/GC 压力已分配字节数/对象数分配 churn 不等于保留堆内存,也不代表内存泄漏
堆内存持续增长/OOM使用中字节数/对象数,同时查看分配情况活跃堆采样是快照;不代表进程 RSS、本机内存或保留对象的归属
竞争或阻塞工作互斥锁/阻塞延迟和次数采样和归因依赖具体运行时;不代表 CPU 时间
怀疑存在 goroutine/线程泄漏goroutine/线程数量和堆栈数量增长本身不能证明工作泄漏

如果缺少所需的分析视角,请记录检测能力缺口;不要用 CPU 来排除等待, 也不要用分配情况来证明泄漏。

基线必须代表预期行为,而不只是发生在事故之前。优先选择时长相同且请求 组成/速率、副本数、采集覆盖范围、运行时和采样设置相近的时间段。存在 昼夜负载变化时,可能需要匹配更早的相同时段。也要检查基线窗口中的 profile 类型和数据。如果没有合适的历史基线,请比较未受影响的同类实例。 如果没有合格的对照,请报告当前成本所在位置,不要声称出现了回归。在负载 增加时,稳定但效率低的热点路径仍可能解释新症状;profile 形态未变并不能 证明它无关。

比较部署版本时,保持稳定的工作负载标签不变;如果事故版本/Pod 之前尚不存在, 则将旧版本/Pod 选作基线。记录两个选择器,并说明它们为何具有可比性。绝不要 将不存在的旧 Pod/版本与有数据的新 Pod/版本进行比较,并称其为无限回归。

异常结果可能会提供服务、分析视角、起始时间、受影响标签和候选基线。如果结果 提供了链接、时间段、观测值/预期值和评分,请保留这些信息,并通过 profile 和 症状证据加以验证。阅读高级分析, 了解从异常结果切入的分析路径;运行此技能不需要异常检测器。

3. 定位变化:服务 → 工作负载切片

从已知的可疑对象入手,而不是查看整个租户的排行榜。如果服务未知,请使用 profiles labels 发现 service_name 的值,然后在已知环境中排序;只有必要时 才使用整个租户范围的查询。

gcx profiles labels -d <pyro-uid> \
  --from <incident-from> --to <incident-to> -o json
gcx profiles labels -d <pyro-uid> --label service_name \
  --from <incident-from> --to <incident-to> -o json
# Establish onset and coverage; repeat for the baseline window.
gcx profiles metrics -d <pyro-uid> '{service_name="<suspect>"}' \
  --profile-type <lens> --from <incident-from> --to <incident-to> -o json
# Pick a discovered label that can distinguish affected/unaffected workloads.
gcx profiles metrics -d <pyro-uid> '{service_name="<suspect>"}' \
  --profile-type <lens> --from <incident-from> --to <incident-to> \
  --top --group-by <label> --limit 20 -o json

使用 profiles series 发现某项服务的实际标签组合,无需指定 profile 类型。有关 标签投影,请参阅范围限定的序列发现。 仅凭整个租户的标签清单,无法证明可疑对象上同时存在这些标签。

对基线重复所选的分组。排行榜仅列出数量有限的候选项,并非整个集群的普查; 未显示的分组可能排在限制范围之外。单个分组无法构成同类比较。总量相近并不 意味着每个实例的行为一致;最大的分组也可能只是承载了更多流量或包含更多副本。 必要时使用现有指标中的请求数或副本覆盖量;如果无法获取,请说明这一干扰因素。 不要使用固定比例阈值来判定某个切片存在异常。

使用时间序列值检查问题是否持续以及何时开始;--top 会合并整个时间窗口。 明确标注单位和聚合语义,尤其是活跃堆内存或计数快照。不要将快照值相加并称为 “已分配字节数”,也不要根据原始 profile 总量推断按请求归一化的成本。

4. 定位发生变化的应用栈

针对下方的事件选择器,以及符合条件的基线选择器/窗口,分别运行两个查询。分析整个服务时,省略切片匹配器。

# Rank functions.
gcx profiles query -d <pyro-uid> \
  '{service_name="<suspect>",<label>="<value>"}' \
  --profile-type <lens> --from <incident-from> --to <incident-to> -o table
# Trace callers and callees of the ranked functions.
gcx profiles query -d <pyro-uid> \
  '{service_name="<suspect>",<label>="<value>"}' \
  --profile-type <lens> --from <incident-from> --to <incident-to> -o dot

从表格中获取函数排名和占比。表格会聚合每个函数,并按 SELF 列出前 20 个函数。PERCENTAGE 列是 SELF 除以配置文件总量,而非函数 TOTAL 占比。表格会省略前 20 名以外的内容,因此要计算一组函数(例如所有 GC 函数)的总占比,请从完整配置文件的 -o json 输出或 pprof 导出中汇总 SELF。

超过 60 个字符的名称会被截断并以 ... 结尾;报告或解读前,请从匹配的 DOT 节点中获取完整名称。

使用 DOT 查看调用方/被调用方上下文、源代码位置和完整函数名。其节点可能对应单个源代码行(file.go:line),因此一个函数可能显示为多个节点。标签会将函数名分成多行,先显示包路径的最后一个元素,随后显示 self (%) of cumulative (%);两者相等时则只显示一个值。不要根据 DOT 节点对函数排名或读取 self 占比。当后端明确拒绝 DOT 并报告不支持,或返回火焰图时,gcx 会警告并回退到表格输出。其他错误仍会导致查询失败。检查返回的格式并保留 stderr。JSON 编码的是火焰图线格式数据,而非人类可读的函数表;Agent 输出也可能改为引用必须读取的溢出文件。

DOT 默认会裁剪;虚线边可能跳过中间帧。缺少调用方上下文时,在两个窗口中都使用 -o dot --max-nodes 250 重复同一查询。这会提高节点上限,但不保证图不会被裁剪。两种格式都无法排除路径缺失的可能。对于归因不明确或数值差异,请阅读 pprof 对比并导出两个配置文件。不要假定导出的配置文件能够弥补采集缺口。

解读证据时,请遵守以下限制:

  • 比较每个窗口中的绝对开销和占比,并注明单位和分母。占比相同可能掩盖开销翻倍;占比升高也可能是因为其他工作减少。百分比既不是请求延迟,也不能证明发生了回归。
  • Self/flat 表示该帧上的工作;total/cumulative 包含后代帧。利用调用方/被调用方上下文定位有意义的应用路径。祖先帧的累计值彼此重叠:不要将其相加,也不要把运行时工作归因于臆测的调用方。
  • 运行时符号提示了值得验证的机制。mallocgc 暗示存在分配开销;GC 工作线程可能独立于分配位置运行;仅凭 nanotime 不能证明存在忙等。futex/锁中的 CPU 采样不代表休眠时间。请用受支持的等待分析视角和其他证据确认等待情况。
  • 在 Go 中,堆栈标识的是分配位置,而非保留引用的对象。互斥锁配置文件将竞争归因于持有锁/解锁路径;阻塞配置文件则归因于等待者。将此解读用于其他语言或垃圾回收器前,请核实其实际运行时语义。

如果有匹配的代码检出版本/构建版本,请检查涉及的函数以验证其机制。读取配置文件位置和已验证的部署修订版本元数据;不要假定 Build ID 是 Git SHA,也不要假定 service_repository 一定存在。只有在符号解析和修订版本信息均能佐证时,才报告 file:line;否则保留函数级归因,并说明缺少哪些信息。调查过程中不要编辑源代码。

5. 验证原因并报告

将可疑路径与原始症状关联起来。使用 gcx 的 debug-with-grafana skill 中的相关证据:请求量、CPU 和堆的趋势、节流、排队、依赖延迟、日志、配置或部署变更。复用已有发现;仅查询能够区分主要解释与重要替代解释的信息。对于给定的请求,请参阅追踪关联。

部署时间戳或检测器评分只能说明相关性。要加强因果结论,请提供相符的起始时间、具体机制、受影响与未受影响的对照,以及可重复的差异或已观察到的恢复。不要仅为验证假设而执行回滚或负载测试。

当用户的问题已得到回答,或缺少证据导致无法继续归因时,停止调查。只有在机制与症状之间的联系有证据支持时,才报告根因;否则报告疑似原因、成本位置或无法得出结论的结果。切勿将“在这些分析配置中未观察到”变成“该服务中不存在”。

无论结果如何,都使用此报告。保留字段名称和顺序;仅在支持结论所需时,在 Evidence 下添加详情。受阻或无结果的调查应标记为 inconclusive,并记录退出情况和缺失的证据。没有合格的基线时,只报告测得的成本位置,不要声称存在回归。未知字段必须明确标出;不要用猜测的值填充。

Conclusion:     <established cause | suspected cause | cost location |
                 inconclusive> — <answer to the original question>
Exit:           <A | B | C | D | none> — <reason, if applicable>
Scope:          <context, datasource UID, service>
Lens:           <exact profile type and units>
Incident:       <UTC from/to; exact selector; observed coverage>
Baseline:       <UTC from/to; exact selector; or unavailable with reason>
Control:        <why comparable; workload/collection differences or gaps>
Slice:          <affected label/value; evidence of scope or unknown>
Location:       <application stack; file:line and revision only if verified>
Cost:           <incident vs baseline absolute values, units, aggregation>
Share:          <incident vs baseline; self/cumulative and denominator>
Normalization:  <method and measured denominator | not applied, with reason>
Mechanism:      <supported explanation or explicitly labeled hypothesis>
Evidence:       <profile artifacts/links and corroborating symptom evidence>
Counterevidence: <alternatives checked and results | not checked, with reason>
Confidence:     <high | medium | low> — <reason tied to available evidence>
Limitations:    <coverage, sampling, baseline, symbolization or access gaps>
Next action:    <smallest missing evidence or handoff question | none>

评论

支持 Markdown