mcpField
S
stream-aggregation-helper

stream-aggregation-helper

github.com/victoriametrics云与 DevOps59174 次安装2 周前更新
代码仓库

关于 stream-aggregation-helper

面向 VictoriaMetrics 运维人员的流聚合技能,指导评估拓扑、选择聚合规则并生成 YAML 与分阶段上线方案。它还提供用于核验结果的查询和指标,并检查分片键等正确性条件。

主要功能

  1. 01评估流聚合是否适用
  2. 02调查基数与采集拓扑
  3. 03选择间隔、函数和标签
  4. 04生成 YAML 与上线计划
  5. 05用查询和指标验证规则

适用场景

  1. 01降低存储前的时序基数
  2. 02为 vmagent 设计聚合配置
  3. 03核查分片或高可用聚合结果

安装

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

$npx skills add https://github.com/victoriametrics/skills --skill stream-aggregation-helper

安装后在 Claude Code 中输入/stream-aggregation-helper确认已加载。

详细介绍

流聚合助手

为 VictoriaMetrics 设计、部署和验证流聚合规则。流聚合会在样本到达存储之前,于 vmagent(或单节点 vmsingle)处按时间和标签聚合样本,从而降低基数、采样频率或查询成本。

此技能分为五个阶段:

  1. 门控 — 流聚合是否适合解决此问题?
  2. 信息收集 — 当前基数、采集拓扑以及使用方的需求。
  3. 决策 — 在流水线中的位置、间隔、输出函数、by/without 和扩展方式。
  4. 配置 — 生成可直接应用的 YAML 和分阶段推出计划。
  5. 验证 — 用于确认规则是否按预期运行的查询和 vm_streamaggr_* 指标。

环境

使用与 victoriametrics-query 技能相同的环境变量。此技能无需这些变量也能运行(仅访谈模式),但如果能直接查询用户的 VictoriaMetrics,结果会准确得多。

# $VM_METRICS_URL - base URL (optional)
#   cluster: export VM_METRICS_URL="https://vmselect.example.com/select/0/prometheus"
#   single:  export VM_METRICS_URL="http://localhost:8428"
# $VM_CURL_CONFIG - curl config file with auth header (unset if no auth)
#   Remote: export VM_CURL_CONFIG="$HOME/.config/victoriametrics/curl.conf"
#   Local:  leave unset (defaults to /dev/null - no auth)

阶段 1:流聚合是合适的工具吗?

仅当以下所有条件都成立时,流聚合才是合适的选择:

  • 对于规则生成的每个输出序列,所有对其有贡献的输入样本 都会到达同一个聚合器进程。以下三种常见拓扑符合这一条件:
    1. 单个 vmagent(或 vmsingle)会接收所有匹配序列的样本。
    2. 多个 vmagent 按某个标签(例如 instance、kubernetes_namespace、 __name__)分片流量,并且聚合保留该分片键——参见 阶段 3a(案例 F)中的分片键不变量。分层拓扑(例如 第 1 层 vmagent 按 instance 分片 → 第 2 层 vmagent 在 instance 内聚合)明确受支持。
    3. 所有 HA 副本都会接收所有样本,并且聚合输出可安全去重(类似 gauge 的输出: min、max、avg、rate_*、increase*、last、quantiles 等)。类似 counter 的输出 (total、total_prometheus、histogram_bucket)无法进行 HA 聚合,因为每个副本 都维护着独立的单调状态。参见“高可用性”部分。
  • 用户愿意丢失匹配指标的每个源的分辨率(每个 Pod、每个实例), 或者只想删除他们实际并未使用的高基数标签。
  • 使用方(仪表板、告警、记录规则)要么不需要被删除的维度,要么可以迁移到聚合后的指标名称。
  • vmagent 重启时状态丢失可以接受。聚合状态存储在内存中;默认情况下,重启后会丢弃最初的一到两个间隔的数据。

分片键不变量:只有当某个输出序列的每个输入样本都发往同一个聚合器时,该序列才正确。换成规则来说:用于分片流的标签必须出现在下游每条聚合规则的 by: 中(或者不得出现在 without: 中)。删除这些标签,就会得到每个分片各自的部分聚合结果——而且不会发出任何警告。

如果其中任一条件不成立,请引导用户选择其他方案:

情况更合适的工具
需要删除整个未使用的指标使用 action: drop 的 metric_relabel_configs——参见 victoriametrics-unused-metrics-analysis
需要在不按时间分桶的情况下删除标签使用 action: labeldrop 的 metric_relabel_configs,或使用 -streamAggr.dropInputLabels
需要对现有数据进行历史降采样vmstorage 降采样(-downsampling.period)
想为查询生成派生指标,而非用于摄取vmalert 中的记录规则
单节点设置,但希望在存储前进行流聚合在 vmsingle 上使用 -streamAggr.config 即可——使用的是同一套引擎,只需说明这一点
位于会拆分流量的负载均衡器后方(轮询)按标签确定性地分片流量(-remoteWrite.shardByURL.labels),然后在保留分片键的同时进行聚合。轮询负载均衡器会将单个输出序列拆分到多个副本上 → 结果不完整。参见常见错误 › 位于负载均衡器后方
分层 vmagent(第 1 层分片,第 2 层聚合)受支持,且在大规模部署中很常见。第 2 层上的聚合规则必须在 by: 中保留分片键(或从 without: 中省略该键)。参见下文的案例 F。

如果通过了这项检查,请继续。如果没有通过,请推荐替代方案并停止。


阶段 2:信息收集

收集三组信息。仅当设置了 VM_METRICS_URL 时,才并行运行实时查询;否则直接向用户询问相同的问题。

2a. 基数示意图

问题涉及哪个指标/集合?由哪些标签决定序列数量?

# Top series by metric name (find the offenders)
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
  "$VM_METRICS_URL/api/v1/status/tsdb?topN=20" | \
  jq '.data | {totalSeries, seriesCountByMetricName, seriesCountByLabelName}'

# For each suspect metric, see unique values per label
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
  "$VM_METRICS_URL/api/v1/status/tsdb?topN=20&match[]=METRIC_NAME" | \
  jq '.data.labelValueCountByLabelName'

直接测量聚合后的基数。 确定候选 by: 或 without: 后,针对当前数据运行等效的分组查询,而不是将各标签的唯一值数量相乘。标签数量的乘积公式假设标签值的所有笛卡尔积组合都存在,但这种情况很少见;更糟的是,如果标签之间存在相关性,或者你漏列了某些标签,估算结果还可能过于乐观。实测数量才是权威依据:

# Post-aggregation series count, using the labels you plan to KEEP.
# Replace <metric> and <kept_labels>. For counters use rate(...[5m]); for gauges use last_over_time.
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
  --data-urlencode 'query=count(sum(rate(<metric>[5m])) by (<kept_labels>))' \
  "$VM_METRICS_URL/api/v1/query" | jq '.data.result'

# Equivalent form using the labels you plan to DROP:
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
  --data-urlencode 'query=count(sum(rate(<metric>[5m])) without (<dropped_labels>))' \
  "$VM_METRICS_URL/api/v1/query" | jq '.data.result'

这样就能得出规则在当前数据上会生成的实际输出序列数,这是 Phase 4d 中估算节省量的正确依据。提交配置前,先对每条候选规则运行查询。

如果无法执行实时查询,可在访谈中询问:

  • 问题涉及哪个指标或指标模式?
  • 该指标的序列总数大约是多少?
  • 该指标包含哪些标签?每个标签大约有多少个唯一值?
  • 是计数器、仪表还是直方图?
  • 抓取间隔/推送频率是多少?

无法执行实时查询时,只将标签数量的乘积公式视为上限,并明确告知用户,数据可访问后必须验证实际聚合后数量。

2b. 采集拓扑

样本目前如何流转?选择以下一种方式绘制示意图:

A: app  -> vmagent -> remote write -> TimeSeries DB         (vmagent already in path)
B: app  -> remote write -> TimeSeries DB                    (no vmagent — must insert one)
C: app  -> [Prometheus | OTel | StatsD-exporter] -> remote write -> TimeSeries DB
D: app  -> load balancer -> N vmagents -> TimeSeries DB     (LB splits stream — dangerous for SA)
E: app  -> vmagent (HA pair / N replicas) -> TimeSeries DB  (multi-vmagent — dedup needed)
F: app  -> vmagent cluster (sharded) -> TimeSeries DB       (sharded by `-remoteWrite.shardByURL`)

目标端以通用的“TimeSeries DB”表示——流聚合是 vmagent 端的功能,可与任何兼容远程写入的后端配合使用(vmstorage/vmsingle、Prometheus、Cortex、Mimir、Thanos receive 等)。下文提到 vmstorage 去重(-dedup.minScrapeInterval)的注意事项仅适用于 VictoriaMetrics 后端;对于其他后端,应在后端实现同等去重,或接受 HA 聚合会写入重复数据的事实。

访谈问题:

  • 路径中是否已经有 vmagent?如果有,有多少个副本?
  • 样本到达聚合器之前是否经过负载均衡器?
  • 同一序列是否恰好由一个源实例提供,还是由多个实例提供(例如 HA 对、多区域)?
  • 对于 K8s 用户:使用的是 VMAgent CRD(vm-operator)还是原始部署?
2c. 消费者意图

下游查询实际需要什么?

  • 仪表板/告警查询的分辨率是多少?(例如 rate(...[5m])、5 分钟内的 p99)
  • 是否有消费者需要此指标的 Pod 级/实例级分辨率?如果回答“否,只需要服务级”,这是最有力的信号,表明 SA 适用。
  • 告警规则或记录规则是否引用了该指标名称?SA 后重命名指标会导致这些规则失效——请记录需要更新的查询。
  • 是否有不同目标端需要不同的聚合方式?如果有,请使用按远程写入分别配置(-remoteWrite.streamAggr.config);否则优先使用全局配置(-streamAggr.config)。

Phase 3:决策

针对每个指标或指标组,确定五项内容。

3a. vmagent 的部署位置
拓扑适合 SA?说明
A. vmagent 已在链路中,且只有一个副本最适合。聚合结果精确——一个 vmagent 能看到每个样本。
B. 尚未部署 vmagent将 vmagent 插入存储环节之前。会增加一个进程,但可免费获得聚合、缓冲和重试功能。
C. 位于 Prometheus/OTel collector 之后将 vmagent 插入 collector 与远程写入之间;如果允许抓取,也可以用 vmagent 替换 collector。除非 collector 支持流式聚合,否则不要在 collector 内部聚合。
D. 位于负载均衡器之后**不要聚合。**每个 vmagent 只能看到一部分样本 → 聚合结果不完整。先按 __name__ 或标签分片(-remoteWrite.shardByURL.labels),或者将流量发送到单个聚合器。
E. 多个 vmagent 副本(HA)仅适用于可安全去重的输出(仪表盘样式:min、max、avg、rate_*、increase*、last、quantiles、sum_samples 等)。所有副本必须接收相同的输入、运行相同的配置,并生成相同的输出标签——vmstorage 去重(-dedup.minScrapeInterval)会合并重复数据。不要给副本添加不同的标签:这会导致存储中的可安全去重序列永久翻倍。计数器样式的输出(total、total_prometheus、histogram_bucket)无法通过这种方式实现 HA——每个副本的单调状态会在重启后产生差异。请参阅高可用性一节。
F. 分片的 vmagent 集群(单层,或多层拓扑的第 1 层)如果聚合遵循分片键,则适用。分片标签(例如 instance、kubernetes_namespace、__name__)必须出现在每条下游规则的 by: 列表中,或等价地,必须不在 without: 中。如果丢弃分片键,每个分片都会生成各自的结果片段 → 总数会悄无声息地出错。常见配置:按 instance 分片,并按 by: [instance, method, status_code, le] 聚合;按 kubernetes_namespace 分片,并按 by: [kubernetes_namespace, workload] 聚合。如果分片键会将同一序列拆分到多个分片(例如,按 pod 分片,但希望跨 pod 聚合),则该拓扑无法满足分片键不变量——请重新分片,或在分片前先在第 1 层 vmagent 上聚合。聚合直方图时,切勿按 le(或 vmrange)分片——同一个直方图的所有桶必须进入同一个聚合器。按 le 分片会将同一直方图的 le="0.1"、le="0.5"、le="+Inf" 等路由到不同分片;每个分片只能看到部分桶,histogram_quantile() 会变得不连贯,分位数也会悄无声息地偏移。应按 instance、kubernetes_namespace 或 __name__ 等非桶标签对直方图分片。
3b. 聚合间隔

两个约束决定 interval:

  1. 下限:至少为输入抓取/推送间隔的 2 倍。小于此值可能会在只有一个样本到达时产生空桶。默认使用 2× scrape_interval(因此 15s 抓取时为 30s)。
  2. 上限:不得超过最短的使用方查询范围。如果告警使用 rate(...[2m]),间隔必须 ≤ 1m,最好为 30s。对于使用 5m 时间窗口的仪表盘,1m 即可。

其他调整项:

  • 延迟较高 / 样本偏斜 → 使用更长的间隔,或启用 enable_windows: true(内存用量翻倍,但使用双窗口状态以确保准确性)。这对直方图至关重要。
  • 替换记录规则 → 与规则的评估间隔一致(通常为 1m)。
  • 为降低存储成本而降采样 → 通常使用 1m–5m;超过 5m 会牺牲交互式查询体验。

默认值:高频应用指标使用 interval: 30s,替换记录规则时使用 interval: 1m,仅用于降采样时使用 interval: 5m。

3c. 输出函数

这是最常选错的一项。先根据指标类型选择,再根据使用方的需求选择。

输入类型使用方需求输出备注
Counter对每个输出序列的速率求和(例如,sum(rate(req_total[5m])))rate_sum结果是每秒速率的 gauge。直接用 sum/avg_over_time 查询——不要再次使用 rate()。
Counter计算每个区间的增量,并将其作为计数器供之后的 rate() 使用total输出是单调递增的计数器,能够抵御各来源的重置和序列变化。使用 rate() 查询。使用 total_prometheus 可跳过每个序列的第一个样本。
Counter计算每个区间的增量,并将其作为值(而非计数器)increase / increase_prometheus输出是每个区间增量的 gauge。
Counter计算输入序列的平均速率rate_avg需要速率平均值而非速率总和时使用。
Histogram bucket(*_bucket,le 风格)histogram_quantile()rate_sum + enable_windows: true推荐用法。聚合后的序列必须具有相同的桶集合。
Histogram bucket将桶计数总和作为计数器total可以使用,但相比 rate_sum 更容易受重置影响。
Gauge最新值last成本最低。与 last_over_time 匹配。
Gauge对各实例求和sum_samples例如,计算所有 pod 的内存总量。
Gauge最小值 / 最大值 / 平均值min / max / avg与函数同名。
Gauge分布 / 百分位数quantiles(0.5, 0.9, 0.99) 或 histogram_bucketquantiles 会为每个 phi 输出一个序列,并附带 quantile="<phi>" 标签。histogram_bucket 返回 VictoriaMetrics 风格的桶,可供之后进行分位数查询——需要之后查询任意分位数时选择它;分位数集合固定时选择 quantiles(...)。
Gauge唯一值数量unique_samples适用场景较少;用于基数类测量。
两者皆可样本数量count_samples / count_seriescount_samples = 落入的点数。count_series = 区间内存在的不同序列数。

最常见的错误是对 gauge 使用 total,或对 counter 使用 sum_samples——两者都会产生无意义的结果。Counter → total/rate_*/increase*。Gauge → 其他所有选项。

3d. by 与 without
  • 已知要删除哪些标签(例如,pod、instance)时,使用 without:。技能应优先使用 without——之后出现新标签时,这种方式更不容易出错。
  • 已知要保留的确切标签集合,并且希望确保删除任何新标签时,使用 by:。输入包含许多不受控标签时,必须使用此方式。
  • 聚合 histogram bucket 时,始终保留 le。
  • 始终保留下游查询已有分组所使用的标签。
  • 删除明显具有高基数且未被使用的标签:pod、instance、replica、container_id。对于分片拓扑(案例 F),保留分片键。对于 HA 拓扑(案例 E),确保各副本的标签集合完全相同——不要使用每副本各自的标签。
  • drop_input_labels 与 without/by:两者都会从输出中移除标签。标签无界时(例如,request_id、trace_id、session_id),优先使用 drop_input_labels(或 -streamAggr.dropInputLabels 标志):它会在去重和状态键生成之前移除标签,因此聚合器永远不会为该标签的每个取值分配状态,即使只是暂时分配也不会。标签基数合理,但不希望它出现在输出中时,使用 without/by。
3e. vmagent 如何扩展?

单个 vmagent 在普通硬件上每秒可处理数百万个样本。仅在以下情况下扩展:

  • 当前负载下,负责聚合的 vmagent 的 CPU 使用率持续超过 70%。
  • histogram_quantile(0.99, sum(rate(vm_streamaggr_flush_duration_seconds_bucket[1m])) by (vmrange)) 接近聚合间隔。
  • 大量活跃输出序列造成内存压力(每个输出序列占用的 RAM 约为一个较小的常量;启用 enable_windows: true 时会翻倍)。
  • 输入速率分布在多个来源,难以在一个 vmagent 中完成汇总。

扩展时:

  • 按序列分片(-remoteWrite.shardByURL.labels),确保每个输入序列恰好进入一个 vmagent。每个分片会生成一部分不同的输出(分片键的标签值不同);在查询时跨分片汇总。参见阶段 3a(情况 F)。
  • HA 副本(每个副本都接收每个样本):使用完全相同的配置和输出标签;vmstorage 去重会合并重复数据。仅适用于去重安全的输出。参见高可用性部分。
3f. 如何使用聚合后的输出

这里有两个相关的决定:除了聚合数据外,哪些数据会继续流向下游,以及聚合指标的名称。

输入保留——规则匹配后,原始(未聚合)样本如何处理。这些是命令行标志(或每个远程写入对应的设置),不是 YAML 字段:

用户意图标志设置结果
仅保留聚合数据——丢弃匹配的原始数据 (默认稳态)两个标志都不设置写入聚合数据。丢弃匹配的原始数据。仍会写入未匹配规则的样本。最常见的最终状态。
同时保留原始数据和聚合数据 (第 2 阶段上线,或消费者仍需要完整分辨率的过渡期)-streamAggr.keepInput=true(或 -remoteWrite.streamAggr.keepInput=true)写入聚合数据和所有原始样本。尚未实现基数缩减——仅临时使用。
仅保留聚合数据——同时丢弃所有未匹配规则的数据 (专用归档/长期存储路径)-streamAggr.dropInput=true(或 -remoteWrite.streamAggr.dropInput=true)写入聚合数据。匹配和未匹配的原始数据都会被丢弃。仅当目标端应当只包含聚合数据时才使用此设置——例如,将每个远程写入的 SA 配置指向长期存储,同时由另一个(不执行聚合的)远程写入将完整分辨率的数据发送到其他位置。

这些标志可与 4c 中的上线阶段配合使用:通常先在验证期间设置 keepInput=true,然后移除该设置以进入默认稳态;只有当目标端纯粹用于接收聚合数据时,才设置 dropInput=true。

输出指标命名——默认情况下,聚合指标名称会自动生成且能自我说明:

<original_name>:<interval>[_by_<labels>][_without_<labels>]_<output>

标签列表会按字母顺序排序;当输出为 quantiles(...) 时,每个输出序列都会带有一个 quantile="<phi>" 标签。建议使用默认设置——这样查询时可以区分聚合数据和原始指标,并在原始数据仍在流入时避免发生冲突。

仅在有明确理由时才覆盖默认设置:

  • keep_metric_names: true(每条规则单独设置)——保留聚合序列的原始指标名称。仅当 outputs: 中有一个条目时才有效。在默认行为下是安全的(匹配的原始数据会自动从远程写入中丢弃)。如果设置了 -streamAggr.keepInput=true,则有风险,因为聚合数据和匹配的原始数据会使用同一个名称,但语义不同——启用 keep_metric_names 前,请先恢复默认设置。

  • output_relabel_configs:(每条规则单独设置)——写入前对聚合样本执行完整的重新标记。可用于移除后缀、遵循项目命名约定或重写标签:

    output_relabel_configs:
      # Strip the auto-added ":interval_..._output" suffix
      - source_labels: [__name__]
        regex: '(.+):.+'
        target_label: __name__
        replacement: '$1'
    
      # Or enforce an "agg_" prefix
      - source_labels: [__name__]
        regex: '(.+)'
        target_label: __name__
        replacement: 'agg_$1'
    

    重命名时,请规划相应的消费者迁移(仪表板、告警、记录规则)——参见阶段 4c 的第 4 阶段。

3g. 分辨率与 SA 间隔——替换 sum(rate(X[5m]))

一个常见需求是:“我的仪表板和告警中用了 sum(rate(req_total[5m]))——请对它进行预聚合。”最直接的做法是让 SA 间隔与速率窗口匹配:

- match: 'req_total'
  interval: 5m            # too coarse — see below
  by: [<labels>]
  outputs: [rate_sum]

这样会每 5 分钟生成一个数据点。对于告警来说,这种分辨率往往过于粗糙(告警每 30 秒至 1 分钟评估一次,需要最新样本);对于图表来说也是如此(5 分钟的步长会让图表呈现阶梯状)。原来的 rate(...[5m]) 还会在每次抓取时计算一个滚动的 5 分钟窗口;而 5 分钟的 SA 间隔生成的是一个不重叠的 5 分钟桶——两者并不相同。

建议的默认设置:使用更细的 SA 间隔(通常为 1m),并在查询时通过 avg_over_time 重建滚动平滑效果:

- match: 'req_total'
  interval: 1m
  by: [<labels>]
  outputs: [rate_sum]

查询改写:

# Original
sum(rate(req_total[5m]))

# After SA with interval: 1m, rate_sum  (rate_sum is already per-second, so don't rate() again)
sum(avg_over_time(req_total:1m_..._rate_sum[5m]))

# Original with grouping
sum by (route) (rate(req_total[5m]))

# After SA — use a subquery to apply avg_over_time after the sum-by
avg_over_time(
  (sum(req_total:1m_..._rate_sum) by (route))[5m:1m]
)

[5m:1m] 子查询表示“在 5 分钟窗口内,以 1 分钟为步长计算内部表达式”——也就是说,它恰好实现了原始 rate(...[5m]) 的每 1 分钟计算一次滚动 5 分钟窗口的行为。

权衡表:

SA interval每 5 分钟写入的样本数适用场景
5m1仅用于回填/长期归档。当没有使用者以快于 5 分钟的频率查询,且阶梯状图表可以接受时,才适用。
1m (适用于使用 [5m] 的使用者的默认值)5高分辨率图表和告警;在查询时使用 avg_over_time(...[5m]) 可重现原始平滑效果。
30s10使用者会采用较短的窗口([2m]、[1m]),或放大查看亚分钟级图表。通常过于精细。

同样的原则也适用于 increase、increase_prometheus、rate_avg 以及 Gauge 输出(min、max、avg、sum_samples):选择 SA 间隔时,应依据任意使用者希望查询的最快频率,而不是使用者采用的最大范围窗口。查询时,再使用匹配的 *_over_time 包装器重建范围窗口。


阶段 4:配置与上线

生成一份完整独立的计划,让用户可以从头到尾阅读并直接执行,无需点击查看文档。预期输出格式如下——请根据实际规则调整,并省略不适用的部分:

流聚合计划:http_duration_p99

决策选择原因
部署位置现有的 tier-2 vmagent已在数据链路中;tier-1 按 kubernetes_namespace 分片,tier-2 在其范围内聚合
间隔1m抓取间隔的 2 倍(15s);使用方采用 [5m] 窗口——在查询时重建滚动平均值(阶段 3g)
输出rate_sum + enable_windows: true直方图的推荐模式;使用方需要 p99
分组without: [pod, instance, path]去掉每个来源和原始 URL;保留 kubernetes_namespace(分片键)、method、status_code、route、le
输入保留默认(切换后,匹配的原始数据会从远程写入中丢弃)切换后没有使用方需要原始数据
时间序列数量(前 → 后)约 5.3M → 约 8.8K(通过 count(sum by (...)(rate(...[5m]))) 测得)减少约 600 倍
高可用 / 分片Tier-2 有 2 个高可用副本;配置和标签相同,由存储进行去重输出为 rate_sum(适用于高可用)。不要添加副本标签。
流聚合配置 — 粘贴到 -streamAggr.config(或 VMAgent.spec.streamAggrConfig.rules)
- name: http_duration_p99
  match: 'http_request_duration_seconds_bucket'
  interval: 1m
  enable_windows: true
  without: [pod, instance, path]
  outputs: [rate_sum]
上线步骤
  1. 使用 -streamAggr.keepInput=true 部署配置(或在 CRD 中设置 streamAggrConfig.keepInput: true)。重新加载(kill -SIGHUP … 或 /-/reload)。
  2. 等待 ≥1 小时,然后运行交叉检查查询(见下方验证区块)。聚合后的 p99 应与原始 p99 相差不超过几个百分点。
  3. 移除 -streamAggr.keepInput=true。重新加载。默认情况下,匹配的原始数据现在会从远程写入中丢弃——基数下降由此实现。
  4. 将仪表板 / 告警迁移到 http_request_duration_seconds_bucket:1m_without_instance_path_pod_rate_sum。后缀标签按字母顺序排序。
验证查询
# 交叉检查:原始数据与聚合后的 p99(在阶段 2 中 keepInput 开启时运行)
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

histogram_quantile(0.99,
  sum by (le) (
    avg_over_time(http_request_duration_seconds_bucket:1m_without_instance_path_pod_rate_sum[5m])
  )
)

# 在 vmagent /metrics 上监控
# - histogram_quantile(0.99, sum(rate(vm_streamaggr_flush_duration_seconds_bucket{name="http_duration_p99"}[1m])) by (vmrange)) << 1m
# - vm_streamaggr_counter_resets_total — 保持平稳(出现尖峰 = 高可用冲突)
# - count({__name__="http_request_duration_seconds_bucket:1m_without_instance_path_pod_rate_sum"}) ≈ 8800

请根据实际场景调整结构:对于使用 quantiles(0.95) 的 gauge,无需设置 enable_windows;用下采样规则替代记录规则时,可以省略交叉检查(没有原始数据可供比较)。如果部署为单副本,则删除高可用行。

4a. 生成配置

模板:

- name: <descriptive_name>             # shows up in vm_streamaggr_* metrics
  match: '<series_selector>'           # e.g. '{__name__=~"http_request_.*"}'
  interval: <30s|1m|5m>
  # enable_windows: true               # REQUIRED for histograms; recommended when samples are delayed
  # staleness_interval: 2m             # default 2*interval; raise it if input is sporadic
  # ignore_first_sample_interval: 2m   # for total/increase/histogram_bucket after restart
  # ignore_old_samples: true           # if the stream contains backfilled / replayed data, is automatically applied when enable_windows are enabled
  without: [pod, instance, replica]    # or: by: [route, method, status_code, le]
  outputs: [rate_sum]                  # see output picker above
  # keep_metric_names: true            # only with a single output; drops the ":interval_..." suffix
  # drop_input_labels: [container_id]  # drops labels *before* dedup + aggregation
  # input_relabel_configs: [...]       # rare; transform labels before aggregating
  # output_relabel_configs: [...]      # rare; e.g. strip the auto-added suffix

不要将 keep_input 或 drop_input 放入 YAML 中——它们是命令行标志(-streamAggr.keepInput、-streamAggr.dropInput,以及针对每个远程写入的变体)。一些常见指南在这点上都有错误。

选择 name 字段时,应体现原始指标名称和聚合输出,同时保持简单易懂。

4b. 配置所在位置
机制适用场景
-streamAggr.config将一份全局配置复制到所有 -remoteWrite.url 目标。**默认选择。**聚合只运行一次。
-remoteWrite.streamAggr.config(按 URL 配置)不同目标需要不同的聚合结果(例如,长期存储仅接收聚合数据)。聚合会针对每个目标运行一次,因此需要更多 CPU。
vmsingle -streamAggr.config单节点部署;引擎相同,YAML 相同。
4c. 分阶段上线
  1. 阶段 1 — 观察。部署时保留输入数据,让原始数据和聚合数据都写入存储:

    • 不要设置 -streamAggr.dropInput(默认会保留未匹配的数据并写入聚合结果)。
    • 设置 -streamAggr.keepInput=true,以便同时写入匹配的原始样本。这样你就能并排比较原始查询和聚合查询。
  2. 阶段 2 — 验证(见阶段 5)。聚合结果应与基于原始数据计算的对应 sum(rate(...)) / histogram_quantile(...) 相符,至少持续一小时;如果可以,也要验证 vmagent 重启后的情况。

  3. 阶段 3 — 切换。移除 -streamAggr.keepInput。这样即可实现基数缩减。默认情况下,SA 会写入聚合结果,并从远程写入输出中丢弃匹配的原始样本(只有未匹配规则的样本会继续以原始数据形式流动)。你无需在抓取端丢弃原始指标;实际上你绝不能这样做,因为 vmagent 的处理流程是 scrape → relabel → dedup → stream aggregation → remote write,所以在抓取端丢弃会移除聚合器所需的输入,导致规则不产生任何结果。

    只有在你还想丢弃所有未匹配规则的样本时,才可以选择设置 -streamAggr.dropInput=true(这种情况较少见;适用于此远程写入目标专用于聚合数据,且另一个远程写入目标负责传输完整分辨率数据的场景)。三种输入保留模式请参见阶段 3f。

  4. 阶段 4 — 迁移使用方。更新仪表板 / 告警,使其使用新的指标名称(带自动添加的后缀)。后缀格式为 :<interval>[_by_<labels>][_without_<labels>]_<output>,其中两个标签列表都会按字母顺序排序:without: [pod, instance, path] 会生成 _without_instance_path_pod_;by: [function, region, cold_start] 会生成 _by_cold_start_function_region_。对于 outputs: [quantiles(0.5, 0.99)],每个输出序列还会带有 quantile="<phi>" 标签。如果你希望保留原始指标名称,可设置 keep_metric_names: true(仅在只有一个输出时有效),并确保原始数据已被完全丢弃,否则会出现两个名称相同但语义不同的序列。

无需重启即可重新加载:

kill -SIGHUP $(pidof vmagent)
# or
curl -X POST http://vmagent:8429/-/reload
4d. 估算节省量

对于每条规则,向用户提供两个数字:

  • 处理前的序列数(当前):从 TSDB 状态中的 seriesCountByMetricName 获取,或由用户提供。
  • 处理后的序列数(测得):在阶段 2a 中对当前数据运行 count(sum(rate(<metric>[5m])) by (<kept_labels>)) 查询得到的数量。这是权威数字——按当前数据,规则大致会生成这么多输出序列。

只有在无法获取实时数据时,才使用标签值乘积公式作为备选,并标注为上限:

output_series ≤ Π(unique values of each label in `by`, including __name__)

当标签值之间存在相关性时(大多数路由并不会返回每种状态码),乘积会高估;如果存在用户未列出的标签,也可能会低估。这两点都是优先使用实测数量的原因。

合理性检查:如果聚合后的实测数量大于处理前的数量,说明 by / without 设置有误——你保留的标签产生了超出预期的唯一组合。


阶段 5:验证

规则生效后,运行以下检查。在阶段 2(保留原始数据时)运行这些检查,并在阶段 3 再次运行。

5a. 流聚合内部指标

检查执行聚合的 vmagent 暴露的指标(或者在 vmagent Grafana 仪表板的 Stream aggregation 部分查看):

指标说明
vm_streamaggr_matched_samples_total{name="..."}规则匹配的输入样本数。应与预期的抓取速率 × 序列数相符。
vm_streamaggr_output_samples_total{name="..."}发出的聚合样本数。大致为 output_series / aggregation_interval。
vm_streamaggr_flush_duration_seconds_bucket刷新每个间隔所用的时间。可通过 histogram_quantile(0.99, rate(vm_streamaggr_flush_duration_seconds_bucket[5m])) 计算第 99 百分位数。该值应远低于 interval。如果接近 interval,请扩容 vmagent 或调高 interval。
vm_streamaggr_ignored_samples_total因各种原因被丢弃的样本数。too_old 原因导致的峰值可能表示上游延迟。另请查看 vm_streamaggr_samples_lag_seconds_bucket 了解交付延迟。
vm_streamaggr_counter_resets_total检测到的计数器重置次数。突然激增 = 输入样本重复或冲突;请检查上游问题或拓扑配置错误(例如,使用计数器类型输出的 HA 副本;参见“高可用性”部分)。
5b. 对照聚合数据与原始数据(仅限第 2 阶段)

对于使用 rate_sum 聚合的计数器:

# raw
sum by (route, method, le) (rate(http_request_duration_seconds_bucket[5m]))

# aggregated (rate_sum is already a per-second rate — sum it, don't rate it again)
sum by (route, method, le) (http_request_duration_seconds_bucket:30s_without_pod_instance_rate_sum)

两者的变化趋势应大致一致,偏差在几个百分点以内。如果两者明显偏离,请检查以下情况:

  • 多个 vmagent 发生冲突(第 3a 阶段,情况 E)。
  • 位于负载均衡器后方(第 3a 阶段,情况 D)。
  • dedup_interval 与存储的 -dedup.minScrapeInterval 不匹配。
  • 对于直方图:样本延迟——启用 enable_windows 和/或调高 interval。

通过 rate_sum 处理直方图时:

# raw p99
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

# aggregated p99
histogram_quantile(0.99, sum by (le) (avg_over_time(http_request_duration_seconds_bucket:30s_without_pod_instance_rate_sum[5m])))
5c. 合理性检查
  • count({__name__="<aggregated_name>"}) 得出的输出序列数 ≈ 第 4d 阶段的估算值。
  • 没有关于 histogram_quantile(0.99, sum(rate(vm_streamaggr_flush_duration_seconds_bucket[1m])) by (vmrange)) 接近 interval 的告警。
  • 对于 total/increase 输出:计数器保持单调递增;只有在 vmagent 重启时才会重置(除非设置了 flush_on_shutdown: true,否则这是预期行为)。

高可用性

支持运行多个 vmagent 副本以提高弹性,但仅限特定条件——流聚合会在聚合器进程中维护状态,且该状态无法在副本间复制。

工作机制

多个副本会生成相同的输出序列——标签集完全一致——并由 vmstorage 的去重机制合并重复项。只有满足以下条件时才能奏效:

  1. 每个副本接收完全相同的输入——相同的抓取目标,或通过扇出(而非轮询)进行远程写入。HA 副本前的轮询负载均衡器会拆分数据流,这既会破坏 HA,也会导致每个副本生成不完整的聚合结果。
  2. 每个副本运行完全相同的聚合配置。
  3. 每个副本生成的输出样本带有完全相同的标签(不要添加副本专属标记,见下文)。
  4. 为 vmstorage 设置 -dedup.minScrapeInterval(通常与聚合 interval 相同);每条规则的 dedup_interval 也应与之匹配。
  5. 选择的聚合输出支持安全去重(见下一节)。

不要为 HA 副本添加不同的标签(-remoteWrite.label=vmagent=A、=B)。不同标签会使序列确实变成不同序列——vmstorage 不会去重,存储开销会翻倍,而且消费者必须在所有地方使用 sum without (vmagent)。“为每个副本添加标记”的模式适用于分片拓扑(情况 F),此时每个副本看到的是互不重叠的输入切片,因此必须将它们区分开;它不适用于副本看到相同输入的 HA 场景。

适用于 HA 与不适用于 HA 的输出
输出适用于 HA?原因
min、max、avg、last、sum_samples、count_samples、count_series、unique_samples、stddev、stdvar、quantiles(...)✅每个时间间隔的值都根据该间隔内看到的样本独立计算。看到相同输入的两个副本会得出相同的值 → 去重结果干净。
rate_sum、rate_avg、increase、increase_prometheus✅(有注意事项)输出是速率或增量的每间隔 gauge,而不是单调递增计数器。一个副本重启后,其内部的逐序列状态可能短暂不一致(直到下一个陈旧窗口),但输出值会趋于一致。
total、total_prometheus❌输出是保存在聚合器内存中的单调递增计数器。一个副本重启后,其输出会重置为 0,而另一个副本的计数器会继续递增。去重会为每个(序列、时间戳)选取一个样本——当同一序列上有两个不一致的计数器数据流时,消费者会看到交替的序列,rate() 会将计数器回退读作计数器重置,从而产生明显的跳变和尖峰。无法干净地对两个不一致的单调递增计数器去重。
histogram_bucket❌每个桶的计数器会像 total 一样累积,因此存在相同的不一致问题。

对于坚持使用计数器形态的消费者(例如使用 rate(...[5m]) 的旧版告警),适用于 HA 的方案包括:

  • 将聚合切换为 rate_sum 并更新查询(rate_sum 本身已经是每秒速率,应对它求和或平均,而不是使用 rate())。
  • 切换为 increase 并使用 increase 风格的查询。
  • 运行单个聚合器(主动/被动故障转移),而不是真正的 HA——接受故障转移期间短暂的数据空档,而非单调状态不一致。
HA 与分片——模式不同,不要在同一维度上混用
  • 分片(情况 F):每个副本看到的是互不重叠的输入切片。每个副本都会生成不同的输出序列——可以保留 by: 中的分片键(首选,在查询时汇总),也可以添加唯一标签。不同副本有意生成不同的值。
  • HA(情况 E):每个副本看到的是相同的输入,并生成带有相同标签的输出。不同副本生成相同的值,由 vmstorage 的去重机制合并。

一级分片 + 二级 HA 的拓扑是有效的:一级按 instance 分片;每个一级分片将数据扇出到两个二级 HA 副本,这两个副本看到相同分片的数据流,并生成相同的输出。在二级配置的 by: 中会包含分片键(instance);二级的 HA 副本在其他方面配置相同。


风险提示(取决于输出选项)

仅在所选输出会触发相应风险时显示这些提示——不要不加区分地全部列出。

当输出 ∈ {total, total_prometheus, increase, increase_prometheus, rate_avg, rate_sum, histogram_bucket}
  • 陈旧状态:没有新样本时,超过 staleness_interval(默认为 2 × interval)后,每条时间序列的状态都会重置。对于输入不规律的场景(Lambda、cron 作业、批处理工作进程),请提高 staleness_interval,否则指标重新出现时数值会飙升。请参阅陈旧状态。
当输入不规律时(任何输出,尤其是仪表盘指标)
  • 即使输出不保留每条时间序列的状态(例如 quantiles、last、sum_samples),样本之间的长时间间隔也会导致相应输出时间序列在这些间隔内缺失——仪表盘中的指标会出现断档。请提高 staleness_interval,以覆盖最长的预期静默时间;或者在查询时使用 last_over_time(<aggregated>[<gap>]) 来弥补短暂缺失。
  • 重启后的首个样本:total/increase 假设新时间序列从 0 开始,因此重启后的首个样本看起来会像一次大幅增长。请设置 ignore_first_sample_interval,或使用 *_prometheus 变体跳过首个样本。
  • 计数器重置(vm_streamaggr_counter_resets_total 数值递增):原因可能是重复样本或 vmagent 副本之间发生冲突。请在聚合前进行去重(dedup_interval),或确保每条输出时间序列仅由一个聚合器生成。
当聚合直方图时(任何输出)
  • 桶一致性:只有当输入时间序列的 le(或 vmrange)标签完全一致时,才能聚合直方图。桶边界不一致会导致分位数悄然出错。
  • 对样本延迟的敏感性:直方图是必须同步更新的一组逻辑相关时间序列。样本未对齐会导致分位数出错。缓解措施:enable_windows: true、延长 interval,并确保 vmagent + 传输管道有足够余量(队列不累积)。
  • +Inf 桶:输入中必须存在;histogram_quantile 需要它。
  • 切勿按 le(或 vmrange)分片:在分片式 vmagent 拓扑(情形 F)中,分片键不得是桶标签。直方图是一组仅 le 不同的逻辑相关时间序列——所有桶都必须落到同一个聚合器上,才能生成一致的桶集合。按 le 分片会将一个直方图的桶拆分到不同分片;每个分片随后都会输出不完整的桶集合,对合并结果执行 histogram_quantile() 会悄然出错。请选择非桶标签(instance、kubernetes_namespace、__name__ 等)作为分片键,并在每条聚合规则的 by: 列表中保留 le。
当运行多个 vmagent 副本时
  • HA 拓扑(每个副本都能看到每个样本):输入相同 + 配置相同 + 输出标签相同 → vmstorage 去重会合并重复项。不要为副本添加标签。此方法仅适用于可安全去重的输出——请参阅“高可用性”部分。
  • 分片拓扑(每个副本拥有互不重叠的数据分片——情形 F):请在 by: 中保留分片键;如果无法保留,则为每个副本添加唯一标签,并在查询时使用 sum without (vmagent) (...) 重新聚合。请参阅集群模式。
  • 在副本前使用轮询式负载均衡器,对两种模式都不适用:HA 模式会失去去重安全性(每个副本只能看到部分数据流),分片模式则会失去确定性。分片时请使用确定性的 -remoteWrite.shardByURL.labels=...,HA 模式则使用扇出方式(每个副本接收每个样本)。
启用去重时
  • dedup_interval 必须等于存储端的 -dedup.minScrapeInterval。若不匹配,原始查询结果 (例如 sum(rate(...)))将与聚合后的指标不一致。请参阅常见错误。

Kubernetes / VictoriaMetrics Operator

流聚合配置可通过 VMAgent CRD 中的 streamAggrConfig(全局)或 remoteWrite[].streamAggrConfig(按目标配置)指定。

VMAgent CRD — 全局流聚合

vm-operator 通过 camelCase 字段公开 streamAggrConfig。这里的 keepInput/dropInput 是 顶层字段(对应 -streamAggr.keepInput / -streamAggr.dropInput 标志),而不是各规则内部的字段。 字段名称取决于 Operator 版本;如不确定,请查阅 VMAgent CRD 参考。

apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAgent
metadata:
  name: vmagent
spec:
  replicaCount: 2                   # HA: both replicas see every sample; identical config below
  # Do NOT add a per-replica externalLabel for HA — it defeats vmstorage dedup. See HA section.
  extraArgs:
    streamAggr.dedupInterval: "30s" # match vmstorage -dedup.minScrapeInterval
  streamAggrConfig:
    keepInput: true                 # stage 2 — top-level, NOT inside a rule. Remove after verifying.
    rules:
      - match: 'http_request_duration_seconds_bucket'
        interval: 30s
        enable_windows: true
        without: [pod, instance]
        outputs: [rate_sum]          # HA-safe (per-interval rate, not monotonic counter)
  remoteWrite:
    - url: http://vmsingle.monitoring.svc:8429/api/v1/write
VMAgent CRD — 按远程写入目标聚合
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAgent
metadata:
  name: vmagent
spec:
  remoteWrite:
    - url: http://vmstorage-long-term.monitoring.svc:8429/api/v1/write
      streamAggrConfig:
        dropInput: true              # send ONLY aggregates to long-term store
        rules:
          - match: '{__name__=~".+"}'
            interval: 5m
            outputs: [last, max, min, count_samples]
    - url: http://vmstorage-hot.monitoring.svc:8429/api/v1/write
      # raw, full-resolution data to the hot store
双层拓扑 — tier-1 分片,tier-2 聚合(分片键 = instance)

tier-1 vmagent 按 instance 分片,将数据发送给 tier-2 聚合型 vmagent。每个 tier-2 实例负责一组互不重叠的 instance 值,因此它能看到这些实例的所有样本,并生成正确的聚合结果——前提是 instance 在 by: 中保留。

tier-1(仅分片,不聚合):

vmagent-tier1 \
  -remoteWrite.url=http://vmagent-tier2-0:8429/api/v1/write \
  -remoteWrite.url=http://vmagent-tier2-1:8429/api/v1/write \
  -remoteWrite.url=http://vmagent-tier2-2:8429/api/v1/write \
  -remoteWrite.shardByURL=true \
  -remoteWrite.shardByURL.labels=instance

tier-2(保留分片键的聚合):

# tier2-stream-aggr.yaml
- match: 'http_request_duration_seconds_bucket'
  interval: 30s
  enable_windows: true
  by: [instance, method, status_code, route, le]   # `instance` MUST stay — it's the shard key
  outputs: [rate_sum]
vmagent-tier2 \
  -remoteWrite.url=http://vmstorage:8429/api/v1/write \
  -streamAggr.config=/etc/vmagent/tier2-stream-aggr.yaml

如果还需要按服务汇总(不包含 instance),请在查询时执行: sum without (instance) (http_request_duration_seconds_bucket:30s_by_...:rate_sum);不要在 tier-2 上添加第二条聚合规则, 因为每个 tier-2 只拥有部分实例。

裸机 / Docker — 命令行标志
vmagent \
  -remoteWrite.url=http://vmsingle:8429/api/v1/write \
  -streamAggr.config=/etc/vmagent/stream-aggr.yaml \
  -streamAggr.dedupInterval=30s \
  -streamAggr.keepInput=true                        # stage 2 only

对于 HA(每个副本都抓取相同目标,且输出仅用于安全去重):省略任何按副本区分的标签 ——vmstorage 要折叠重复数据,标签必须完全相同。对于分片拓扑(每个副本负责互不重叠的数据子集,情况 F), 仅当分片键未在 by: 中保留时,才添加按副本区分的标签:

  -remoteWrite.label=vmagent=$HOSTNAME   # sharded ≠ HA; do NOT use this for HA

完整示例

用户:“600 个 pod,http_request_duration_seconds_bucket 基数激增,标签为 pod, instance, method, path, status_code, le, route,有 40 个 route、10 个 le 桶,vmagent 已在数据路径中,只需要服务级 p99。”

决策:

决策选择原因
门槛通过 — 数据路径中已有 vmagent,可以接受丢失 pod 级数据
间隔30s是 15s 抓取间隔的 2 倍;与 5m 查询窗口匹配。
输出rate_sum + enable_windows: trueHistogram 桶的推荐模式;使用方是 histogram_quantile。
without[pod, instance, path](假设 route 是规范化形式)删除所有不需要的标签;保留 method、status_code、route、le。
部署位置现有 vmagent已在数据路径中。确认只有一个副本,或使用 HA 标记。
扩容否只有一条规则;当前 vmagent 足够。

估算:4 个 method × 5 个 status × 40 个 route × 11 个桶(包括 +Inf)≈ 8.8K 条序列,而原始数据为 600 个 pod × 8.8K ≈ 5.3M → 约减少 600 倍。

配置:

- name: http_duration_p99
  match: 'http_request_duration_seconds_bucket'
  interval: 30s
  enable_windows: true
  without: [pod, instance, path]
  outputs: [rate_sum]

上线:启用 -streamAggr.keepInput=true,部署后使用第 5b 阶段的交叉检查查询验证一小时, 然后移除该标志。移除 keepInput 后,默认不再向远程存储写入匹配的原始数据,从而实现基数降低。不要在抓取端丢弃该指标;否则会移除聚合器所依赖的输入数据。

使用方的查询更新——注意,后缀会按字母顺序排列标签(instance, path, pod):

# Before
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

# After
histogram_quantile(0.99,
  sum by (le, route) (
    avg_over_time(http_request_duration_seconds_bucket:30s_without_instance_path_pod_rate_sum[5m])
  )
)

常见错误

错误修正方法
在 YAML 中设置 keep_input / drop_input这些是命令行标志(-streamAggr.keepInput、-streamAggr.dropInput 或各个远程写入的对应变体)。
对 gauge 使用 totaltotal 用于计数器。对 gauge 使用 last/avg/sum_samples。
对计数器使用 sum_samples这会对原始计数器值求和,毫无意义。请使用 total、increase 或 rate_sum。
对 rate_sum 输出使用 rate() 查询rate_sum 已经是每秒速率。对其求和或平均,不要再计算速率。
查询 total 输出时不使用 rate()total 是单调递增的计数器,请使用 rate() 或 increase()。
间隔短于抓取间隔会产生空桶和抖动的输出。设置 interval ≥ 2× scrape_interval。
为 HA 副本添加不同的 vmagent=... 标签这会破坏 vmstorage 去重,导致存储中的序列永久翻倍。对于 HA,副本必须发出完全相同的标签;去重会将它们合并。“为每个副本添加标签”的模式适用于分片拓扑,而非 HA。
HA 副本使用 total 或 total_prometheus 输出计数器状态位于进程内,单个副本重启后状态就会分歧,导致使用者看到跳变。改用 rate_sum 或 increase,或者运行单个聚合器。
HA 副本位于轮询负载均衡器之后每个副本只能看到部分流,因此聚合结果不完整,也无法去重。请使用扇出(每个副本都接收每个样本)或确定性分片。
聚合器位于轮询负载均衡器之后每个 vmagent 只能看到一部分数据,导致聚合结果不完整。先按标签进行确定性分片(-remoteWrite.shardByURL.labels),或将流量终止于单个聚合器。
分片的 vmagent 在 without: 中丢弃分片键(或在 by: 中省略分片键)每个分片都会对其输入切片生成部分聚合结果;不同分片的输出会落到同一序列上,导致总数悄无声息地出错。每条下游规则的 by: 中都保留分片键,并在查询时进行后续汇总。
按 le(或 vmrange)对直方图进行分片这会将一个直方图的桶拆分到多个分片;每个分片都会输出部分桶集合,因此对合并结果使用 histogram_quantile() 会悄无声息地得出错误结果。请按非桶标签(instance、kubernetes_namespace、__name__)对直方图进行分片,并在 by: 中保留 le。
dedup_interval 与存储的 -dedup.minScrapeInterval 不同原始查询结果与聚合查询结果会出现偏差。请将二者设为相同值。
聚合直方图时未启用 enable_windows样本未对齐会导致分位数错误。请设置 enable_windows: true。
将输出重命名为原始指标名称,但原始数据仍在流动会出现两个同名但不同的序列。请丢弃输入(-streamAggr.dropInput)或保留后缀。
每条记录规则使用一条聚合规则每条规则都会扫描每个样本。请合并 interval 和 by/without 相同的规则,并使用列表形式的 match:。
为零星输入将 staleness_interval 设得过低样本之间状态会重置,导致总数错误。请调高该值,使其覆盖预期的间隔。
多个输出时设置 keep_metric_names: true不允许;此设置仅适用于单个输出。
忘记更新告警规则/记录规则旧规则会查询已不存在的指标(或者更糟,指标仍存在,但现在是空的原始数据)。

快速参考

# Skeleton — start here, fill in `match`, `interval`, `without`/`by`, `outputs`.
- name: <name>
  match: '<selector>'
  interval: 30s
  without: [pod, instance]
  outputs: [rate_sum]              # see output picker
  # enable_windows: true           # histograms; delayed inputs
  # dedup_interval: 30s            # match storage dedup
  # staleness_interval: 4m         # for sporadic inputs

输出选择器(适用于 90% 的情况):

  • Counter,需要速率总和:rate_sum
  • Counter,需要单调递增计数器:total
  • Histogram 桶:rate_sum + enable_windows: true
  • Gauge,最新值:last
  • Gauge,跨来源求和:sum_samples
  • Gauge,百分位数:quantiles(0.5, 0.9, 0.99)

快速决策:

  • 间隔 = max(2 × scrape_interval, 最短使用方窗口 / 2)
  • 除非会按来源使用,否则移除 pod/instance
  • 高可用 vmagents:相同的输入 + 相同的配置 + 相同的输出标签 → vmstorage 会去重。不要给副本添加标签。仅使用高可用安全的输出(不要使用 total/total_prometheus/histogram_bucket)。
  • 分片 vmagents:在 by: 中保留分片键,或为每个副本添加标签,并在查询时汇总。
  • 位于轮询负载均衡器后方:停止。使用确定性分片(-remoteWrite.shardByURL.labels)或扇出。
  • 替换 recording rule:将 interval 设为规则的评估间隔,并使用列表形式的 match: 将多个规则合并到一个配置项中。

评论

支持 Markdown