任务字节、任务等待与设备三条独立轨道对齐观察窗口,不直接认定责任进程。

iotop 能看出是哪个进程在烧磁盘

iotop 把任务级内核统计接入系统整体的存储调查,可以帮助发现报告大量 IO 或等待的任务,但这两种观察并不相同。被其他工作阻塞的任务,可能是受影响者,而不是争用的来源。内核计数器也不为每种文件系统、缓存与存储层提供完美归因,不能把一个列表直接当成责任判决。 把 iotop 与设备指标、应用延迟放在一起看。活跃写入者或较高等待值,单独都不足以证明存储瓶颈。先比较同一时段,再决定检查哪个组件;两个命令的输出可以形成线索,却不会自动结束调查。应明确自己看到的是工作量、等待时间,还是实际设备完成的操作。 逐个进程的计数器 先核对安装的实现与版本。所引用的 iotop-c 已在主进程行中汇总线程活动;-P 只是隐藏单独的线程行。其他变体应核对各自手册,不能把显示选项误写成首次发生汇总。-o 根据观察到的 IO 活动过滤,不会判断工作是否有害。读写字节速率与 IO 等待百分比单位不同,不能把它们当作同一种消耗排名,也不能只凭一列高低决定是否应终止进程。 Linux 的 read_bytes 统计存储层读取,缓存命中的读取不一定产生这类字节。write_bytes 在页面变脏时记账,不一定等到设备完成写入;取消写入与稍后的回写,会让任务总量和设备吞吐不同。延迟统计记录任务等待,包括同步块 IO 等待;CPU iowait 是另一种整体指标,不能作为可靠的逐任务归责依据。页面缓存中的活动、进程等待和设备忙碌,需要分别解释。 看到谁在扫盘 明确选择排序列,并观察足够时间,区分持续工作和短促突发。写入速率高可能来自预期的备份、索引构建或日志任务。修改调度、限额之前,要关联进程归属、应用角色与当时业务。排名靠前只说明值得进一步检查,不会直接告诉你应轮转日志、迁移任务还是终止进程,正常的批处理也可能占据榜首。 在正确的 PID 命名空间中解析任务,并确认该标识仍指向预期进程。命令行、父子关系与打开文件能够提供上下文,但打开某个文件不证明全部写入都去了那里。两次读取之间 PID 可能复用,陈旧号码可能已经属于无关任务。容器内外看到的号码也可能不同,必须把身份与观察范围一同记录,避免把一个范围中的统计匹配到另一个范围中的进程。 先看再动 观察本身有成本与访问要求。进一步的系统调用追踪可能扰动进程并暴露参数,需要另外获准且范围明确的调查。追加标志属于打开文件时的设置,不是每次 write 系统调用携带的标志。不能把虚构的吞吐、打开文件与追踪输出组合成一次已测得的诊断,也不能看到文件存在就推断写入路径已被证明。 记录基线条件:工具实现、间隔、进程或线程模式、是否累计,以及可比较的业务负载。按文档默认的一秒间隔,-b -n 60 大约是一分钟,不是五分钟,还要考虑启动与首份报告。设备 await 包含排队与服务时间;接近百分之百的利用率,并不能在所有并行 SSD 或 RAID 上证明吞吐已经饱和。 ...

三月 15, 2028 · 3 分钟 · XB Field Notes
production.eu 与 staging 选项指向放大的已转义字面点号,强调正则格式化。

Grafana 变量必须与查询语言的语义一致

Grafana 仪表盘变量让同一组面板能够切换环境、服务或实例,而不必复制多份仪表盘。查询变量从数据源获得选项,自定义变量则可以维护一份有意固定的列表。这两种方式没有绝对优劣:当选项应跟随数据源发现结果变化时,可以使用查询变量;当选择范围就是一份较小且稳定的约定时,维护静态列表同样合理。变量是否合适,取决于它如何表达实际使用需求。 数据源执行查询之前,变量会先被代入查询表达式,所以变量格式本身属于查询正确性的一部分。Prometheus 多选场景应使用正则标签匹配,例如 environment=~"$environment"。Grafana 的 Prometheus 插值逻辑会把选中值处理为正则候选项,并执行所需转义。如果改用精确等号匹配,组合后的文本只会被当成一个标签值,并不会自动变成希望表达的多个环境选择。变量编辑器里的 label_values 是发现选项的辅助语法,不是可以直接复制到面板表达式中使用的 PromQL 函数。 检查最终送出的表达式 这里存在两层语法:正则表达式,以及包裹它的 PromQL 字符串。production.eu 中的点号应表示字面字符,不能成为同时匹配 productionXeu 的通配符。应让数据源按其约定完成插值;使用显式格式覆盖时,要检查真正发送出去的查询。raw 格式会绕过数据源格式化,并不是解决复杂变量值的通用办法。表面上省掉反斜线,可能只是把错误隐藏在返回的数字中。 全部选项也有明确语义。本例没有设置自定义的全部值,因此会展开经过筛选的可用选项。菜单只包含 production.eu 和 staging,全部选择就应覆盖这两个环境。如果把全部值改成点星号等自定义表达式,范围则可能扩大到后台正则允许匹配的其他标签。需要先决定这里的全部究竟指当前菜单中的值,还是更广的查询范围,再用刻意排除的数据验证这种决定。 坏掉的样子 查询变量不会保证永远新鲜,刷新设置决定了 Grafana 何时重新执行选项查询。选项依赖时间范围时,通常还需要随时间范围变化刷新。存在链式变量,也不保证每组上游选择都能找到下游组合;数据本来不存在时,结果完全可能为空。应当区分真实的空集合、已经陈旧的选择和查询错误,不能看到无数据提示,就直接把它解释为服务故障。 变量筛选不是权限控制。菜单里隐藏一个环境,并不会从数据源删除它,也不会阻止其他获准查询请求这些数据。访问边界需要在对应的 Grafana 配置与后台数据服务中落实。分享链接携带的同样只是选中状态,不是权限声明。多个环境通过重复的 var-environment 参数传递,不能随意发明一个逗号拼接的权限列表,再期望后台自动按访问规则理解它。 Grafana 查询格式化:机制与证据边界。 快速验证 保存代码为 grafana-variables.py,准备 Python、本机 Docker daemon 和预先加载的两个镜像后,运行 python3 grafana-variables.py --serve。脚本在专用桥接网络中创建可删除的 Grafana 12.1.0 与 Prometheus 3.5.1 容器,仅通过本机回环地址暴露 Grafana,不挂载宿主机目录。打开输出的仪表盘地址。服务模式默认保留 120 秒,--serve-seconds 接受 1–300;控制键加 C 会进入清理,并保留中断,不把它当作正常结束。Docker 调用各有 30 秒超时,就绪重试采用 20 秒截止时间,最后一个正在执行的请求仍可能耗尽自身超时。清理按确切自有名称检查,删除失败、清单无法确认或命令超时或调用进行中被中断后的状态不确定都会报错。不带服务参数时,只检查手写后台查询。Python 已禁用代理发现及重定向;浏览器代理策略仍须单独控制。 ...

二月 23, 2028 · 5 分钟 · XB Field Notes
原文计数器样本先增长、急降重置,再次增长。第一个样本位于五分钟左开范围之外。箭头从所选观测指向原文样例的每秒 5/6 速率估计。

Prometheus rate 估计计数器每秒的增长速度

请求计数器回答进程累计记录了多少事件,速率则回答这些事件到来得有多快。Prometheus 的 rate 估计指定范围内每秒平均增长量,修正能够观测到的计数器重置,并向范围边界进行外推。结果具有明确的时间单位,适合观察负载变化;但它不是两次抓取之间全部事件的精确还原,更不能凭空补回没有采到的重启历史。 数值来自原文合成样本的 promtool 实验,其历史本地执行证据与本轮绘图分别保留。五分钟范围排除第零分钟,修正可见重置并外推。每条计数器应先求速率再求和。本轮未运行查询引擎或真实 Prometheus 服务。 采样决定能够推断多少事实 普通计数器在重置前持续增长,进程重启是常见的重置原因。相邻已观测样本出现下降时,计数器函数会按重置处理。不过,如果两次抓取之间已经重启并重新累计,而且新值超过旧样本,重置可能完全不可见。提高抓取频率可以提升观测分辨率,但任何查询都不能确定地恢复一段没有被记录的历史。 应先对每条计数器序列求 rate,然后聚合结果。如果先求和,一台实例的增长可能掩盖另一台实例的重置。标签决定序列身份,替换实例使用新标签时,是开始另一条序列,不是自动接续旧实例。队列长度等仪表值本来就允许正常下降,把它们交给计数器速率函数,会把真实下降错误解释为重置。 选择的范围也是指标含义的一部分 范围选择器左开右闭。在第五分钟求 [5m] 时,恰好位于第零分钟的样本不在范围内。对普通浮点计数器求速率至少需要两个可用样本;只有一个样本时得到空结果,并不表示业务流量为零。因此,范围不仅要覆盖正常抓取周期,还需要考虑偶发漏抓后的样本数量,不能仅凭希望曲线更灵敏就无限缩短。 较长窗口通常会平滑短促突发,也让突发离开平均值的时间更晚;较短窗口响应更快,但观测更少,而且可能变成空结果。应依据事件持续时间与检测目标选择范围,并在面板上明确标注。irate 使用最后两个样本,rate 使用所选范围,两者可以针对同一指标回答不同问题,只要面板清楚说明各自含义,不把数值当成同一种口径比较。告警通常应优先使用 rate:即使整体负载仍然偏高,irate 的短暂下降也可能重置待触发告警的 for 等待。 规则评估周期决定何时提出查询问题,不决定目标已经被抓取了多少次。如果目标每分钟才采集一次,即使面板每秒刷新,也不会凭空产生新的观测。分析告警响应速度时,需要分别检查采集与评估这两个周期,不能只缩短页面刷新时间。 坏掉的样子 刚创建的序列可能要等第二个样本到来后才出现在速率面板。把所有空值都补成零,可能掩盖采集故障。遇到空白,应先检查原始序列、时间戳和抓取状态,再判断业务是否空闲。另一种问题是窗口太长:短时间过载已经影响用户,但平均线被长窗口稀释,看起来始终平稳,告警因而没有达到阈值。 重置修正不代表缺口对准确性没有影响。漏抓可能隐藏突发或多次重启,范围边界的外推本身也属于估计。increase 以事件数量单位表达相应的范围估计,即使原始计数器只有整数,也可能返回小数。如果场景需要精确账务或计费总数,应依靠业务记账记录,不能把监控面板描述成可以完整重建交易流水的工具。 快速验证 保存为 rate.py,运行 python3 rate.py。环境需要已经安装的 promtool,可以从 PATH 找到,或用 PROMTOOL 指定其可执行文件路径。此前记录的执行使用 Python 3.12.14 与 promtool 3.14.0;本轮文章复核没有重新运行该引擎。真实查询引擎在临时目录中评估合成样本,不启动服务器、监听端口或容器,也不联系外部服务。 ...

十二月 24, 2027 · 2 分钟 · XB Field Notes
金色等待区间从第一分钟开始,经过两分钟评估后在第三分钟进入红色触发状态,第四分钟变为非活动状态。这些是通知送达之前的规则状态。

Prometheus 规则先跟踪告警状态,再交给通知链路

告警规则评估 PromQL 表达式,并持续跟踪表达式返回的标签集。Prometheus 判断每个告警是等待还是触发,Alertmanager 接收告警信息后负责通知路由、分组、静默与抑制。排查时应分别追踪这些环节:规则显示正在触发,是规则状态的证据,不能直接证明某个人已经收到消息。把状态和送达混为一谈,会使漏通知问题很难定位。 时间线来自原文合成规则实验:稳定的队列身份、每分钟评估与 for: 2m。采用默认恢复行为,没有配置 keep_firing_for。历史本地 promtool 结果保持不变,本轮没有重新执行。分组、抑制和送达仍需独立证据。 表达式返回元素就会激活告警候选项 表达式返回的每个元素代表一个活动告警候选项。queue_depth > 5 这样的比较会过滤不满足条件的数值;但加上 bool 修饰符之后,不满足条件的比较仍会留下数值为零的元素。告警规则依据元素是否存在来激活候选项,因此误用该修饰符,可能让数值已经为零的结果也进入告警流程。这是查询看似合理、规则却持续激活的常见原因。 标签定义告警身份。服务、队列与严重程度等稳定路由维度适合放在标签中,变化中的说明值则适合放在注解中。如果把当前队列大小写进标签,每次数值变化都可能创建一个新身份,重新开始等待。把大小写进注解则可以解释现场状态,同时保留原有告警身份。需要审阅的不只是阈值,还有标签和注解承担的职责。 等待时长通过评估时刻观察 配置 for: 2m 后,一个新出现的标签集会先等待,直到它在评估中持续活动两分钟。如果规则每分钟评估一次,第一分钟首次看到异常,就可能在第三分钟进入触发状态。引擎观察的是评估结果,不是样本之间连续发生的全部物理事实。因此,实际发现时间还包括等待下一次评估的时间,不能只用配置的等待时长计算最坏延迟。 省略 for 时,活动表达式可以在第一次评估就触发。这可能适合含义明确且必须立即处理的故障,但也意味着一个异常样本便足够触发。应根据失败语义主动选择,而不是由于忘记配置而意外采用默认行为。 ...

九月 1, 2027 · 2 分钟 · XB Field Notes
八块有界标签与四百个请求标识标签组成的密集网格对照;相同的九组件、二十副本系数得到 1440 与 72000 条序列。

基数是指标的预算

一个很短的指标名,背后可能对应大量存储与查询成本。Prometheus 使用指标名及完整标签集合标识一条时间序列,每出现一种新的实际标签组合,就形成另一条需要采集、建立索引、保留和查询的序列。基数预算关注的是,哪些区别值得付出这笔成本,以及服务增长时,这些区别的数量是否仍有明确上限。它不是单纯要求标签越少越好,而是要求每个维度都有可解释的用途。 原文练习是序列数量模型:八种有界组合全部出现,每个副本都有这些组合。九个经典直方图核心组件和二十副本同等放大两种方案。数量不是实测的内存或磁盘占用。 统计真正出现的组合 标签维度会扩大可能的组合数,但只有每种组合都实际出现时,各维度取值数的乘积才等于真实数量。两条路由、两种方法、两类状态最多可以形成八种组合;如果每条路由只允许一种方法,导出的组合就可能更少。因此既要计算理论上界,也要核对实际输出,并把实例等采集目标标签纳入服务端最终接收的序列估计。 取值集合比标签名字是否好听更重要。标准化路由可以把同一类订单操作归在一起,而包含每个订单编号的原始路径会持续增加取值。请求编号、时间戳、用户标识以及任意查询字符串,都可能随着事件量增长。开发者把标签叫作路由,并不意味着它真的有界;还要检查真实流量中的值,尤其是错误路径、未匹配路径和很少出现的边界请求,避免正常测试掩盖高基数来源。 把指标表示方式一起算进去 对于每组实际标签,普通计数器通常产生一条主要序列,另外可能存在伴随指标。经典直方图则会展开成多个桶、总和与计数组件。如果有六条有限桶边界,再加正无穷桶、总和及计数,每组标签就对应九条核心序列。有些客户端还会导出创建时间序列,真实清单必须把它算上,不能将用于理解原理的简化公式当成完整埋点总量。 多个副本还会增加实例维度。采集间隔决定每条序列多久写入一个样本,保留期限决定历史数据保存多久,它们与不同序列的数量不是同一概念。不断变化的标识还会制造时序更替:生产端已经消失的短命组合,历史样本仍可能继续留在存储里。因此应分别观察活跃序列、新建速度、写入量、内存和查询表现,不能拿一个固定的“每条序列占多少字节”覆盖所有配置与工作负载。 坏掉的样子 看似方便排障的标签,可能把聚合指标变成逐请求事件仓库。只在仪表盘查询中去掉该标签,无法撤销已经发生的采集和保留成本。对每个标识计算哈希,也不会消除其唯一性;截断随机标识通常只是推迟增长问题,同时引入碰撞。如果业务真正需要的是分类统计,就应选择有明确上限的类别,而不是给无限集合换一种编码方式。 也不能为了降低成本,把识别用户影响所必需的区别全部压平。保留有意义且有界的结果或服务维度,把单次请求调查交给具有相应访问控制的日志和链路记录。如果在采集过程中删除标签,还要检查一次采集内是否出现重复的最终序列,以及诊断信息是否丢失。能够修改生产端时,应优先在那里修正,再验证新标签集合,并明确旧历史序列如何随保留策略逐步退出。 快速验证 保存为 cardinality.py 并使用 Python 3 执行。人为构造的事件把路由、方法和状态的每一种组合重复五十次。一种表示只保留这些有界维度,另一种再增加唯一的请求标识。计算假定二十个副本都导出相同的组合,并使用六条有限边界的经典直方图;这个前提在代码旁明确列出,便于读者核对乘法来源。 from itertools import product routes = ["/orders/{order_id}", "/products/{product_id}"] methods = ["GET", "POST"] statuses = ["200", "500"] events = list(product(routes, methods, statuses, range(50))) bounded = {(route, method, status) for route, method, status, _ in events} with_request_id = {(route, method, status, f"request-{i}") for i, (route, method, status, _) in enumerate(events)} finite_buckets = 6 # Core classic-histogram series: finite buckets, +Inf, _sum, _count. # Optional _created series are deliberately excluded from this calculation. series_per_combination = finite_buckets + 3 replicas = 20 bounded_series = len(bounded) * series_per_combination * replicas request_id_series = len(with_request_id) * series_per_combination * replicas assert len(events) == 400 and len(bounded) == 8 assert len(with_request_id) == 400 assert bounded_series == 1440 and request_id_series == 72000 print(f"observed label sets: bounded={len(bounded)}, request_id={len(with_request_id)}") print(f"core classic-histogram series: bounded={bounded_series}, request_id={request_id_series}") print(f"multiplier={request_id_series // bounded_series}x") 四百条事件在有界表示下被聚合为八种组合,带请求标识时则保留四百种。扩展到二十个副本后,两者分别产生一千四百四十条与七万二千条核心直方图序列,相差五十倍。示例明确排除了可选创建时间序列和其他指标族,因此输出是给定假设下的序列数量模型,不是对 Prometheus 内存或磁盘占用进行过真实压测的结果。 ...

七月 21, 2027 · 1 分钟 · XB Field Notes
已知流量条显示 1% 错误,区域放大显示东部 50% 错误;灰色区域标记南部未知,覆盖仍不完整。

看板是总结,不是放大镜

一张有用的看板,应该帮助读者决定接下来检查什么。它把大量观测归纳成少数几个问题:用户操作是否成功,影响发生在哪里,资源是否正在逼近限制。但归纳不能抹掉重要区别。全局成功率仍然很好看时,一个区域可能已经严重失败;面板空白也可能是遥测缺失,而不是服务处于安静且健康的状态。看板的价值在于减少重新推理的负担,同时保留做判断必需的上下文。 给页面安排清楚的阅读顺序 先展示服务结果和观测覆盖,再给出流量、延迟、错误以及有关资源的饱和信息。如果区域、操作类型或依赖的差异会改变下一步行动,就提供对应下钻入口。变更标记和诊断资料链接也应放在相关视图附近。曲线同时变化可以帮助选择调查方向,但仅凭发生时间接近,还不能证明某次部署或某个依赖导致了事故,仍需进一步验证真实链路。 每个面板应该回答明确的问题,但不要求只放一个指标。队列长度与配置上限使用相同单位,并列比较就很有意义;把不同单位放进复杂双轴图,则容易制造没有根据的视觉关系。应直接说明单位、聚合窗口、统计总体以及阈值含义,让读者不必凭记忆猜测坐标表示毫秒、秒、零到一比例还是百分数。含义清楚通常比增加面板数量更重要。 保留请求权重和观测覆盖 计算服务错误比例时,应使用相同窗口中的全部不良事件数除以全部符合条件的事件数。直接平均区域百分比,会让很安静的区域与繁忙区域获得相同权重,这已经换成另一种统计量,不是无关紧要的显示方式。即使全局值按请求正确加权,当小范围用户仍属于服务承诺的一部分时,也需要展示能定位影响的区域或操作细分。 新鲜度同样决定统计范围。应展示最近一次成功观测的时间,或者明确的覆盖信号,并约定何时把陈旧数据视为未知。业务看板选择的新鲜度阈值,与存储系统处理时序失效的机制是不同概念。把缺口一律补零,可能让已经不可用的采集器表现成完美错误率或空队列。缺失状态应被保留下来,并提供下一步排查采集链路或服务本身的方向。 坏掉的样子 自动缩放的纵轴可能把微小变化放大成重大事故,过宽的固定范围又可能掩盖真正越过阈值的变化。应根据运维问题选择刻度,让需要比较的面板保持一致。平均延迟还会藏起慢请求群体,需要合适的分布与观测数量作为补充;不能直接平均不同实例已经计算出的高分位数,再把它当成整个服务的尾部延迟。 外观漂亮的页面也可能只是一些互不相关的快照。时区、过滤条件、刷新频率和聚合窗口不同,会妨碍连贯比较。共享变量应有明确默认值和可见范围,尤其要让读者知道当前是否查看生产环境以及哪些区域。应拿区域故障、低流量和观测缺失来检查看板,如果三种场景都表现为绿色,就说明页面在压缩信息时丢掉了做决策所需的关键事实。 加权 1% 总体值、东部 50% 错误和南部覆盖未知需要同时可见。这是模拟聚合值,不是真实看板页面的验证结果。 快速验证 将代码保存为 dashboard.py 并用 Python 3 执行。它基于一个完整六十秒窗口中的模拟计数,计算可以用于面板展示的值。两个区域有新鲜观测,另一个区域既缺少数值,最近观测时间也较旧。九十秒阈值是示例看板的业务规则,并不是 Prometheus 默认行为;验证这部分聚合逻辑不需要安装 Grafana,也不会操作任何现有仪表盘。 import json from fractions import Fraction # Counts cover the same completed 60-second interval. rows = [ {"region": "west", "requests": 9800, "errors": 0, "age_seconds": 10}, {"region": "east", "requests": 200, "errors": 100, "age_seconds": 10}, {"region": "south", "requests": None, "errors": None, "age_seconds": 240}, ] fresh = [r for r in rows if r["age_seconds"] <= 90 and r["requests"] is not None and r["errors"] is not None] missing = [r["region"] for r in rows if r not in fresh] requests = sum(r["requests"] for r in fresh) errors = sum(r["errors"] for r in fresh) regional = {r["region"]: Fraction(r["errors"], r["requests"]) for r in fresh} known_ratio = Fraction(errors, requests) unweighted = sum(regional.values()) / len(regional) assert known_ratio == Fraction(1, 100) assert regional["east"] == Fraction(1, 2) and unweighted == Fraction(1, 4) assert missing == ["south"] print(json.dumps({ "coverage": "incomplete", "fresh_requests_per_second": round(requests / 60, 3), "fresh_error_percent": float(known_ratio * 100), "regional_error_percent": {k: float(v * 100) for k, v in regional.items()}, "wrong_unweighted_percent": float(unweighted * 100), "unknown_regions": missing, }, indent=2)) 新鲜观测合计一万次请求、一百次错误,对应每秒约一百六十六点六六七次请求,以及按请求加权后的百分之一错误率。然而流量较小的东部区域有一半请求失败。若直接平均两个区域的百分比,会错误地把合并请求错误比例写成百分之二十五。正确方式是让百分之一的总体值与区域细分共同出现,而不是挑选某一个数字来遮住另一个事实。 ...

七月 18, 2027 · 1 分钟 · XB Field Notes
带有一个已有 cluster 标签的本地序列跨过对外通信边界。同一个 cluster 标签保留在输出序列中,同时补入两个用不同颜色表示的缺失标签。

外部标签标识 Prometheus 对外发送的数据

多个 Prometheus 向同一系统提供数据时,下游需要知道信息来自哪个采集器或环境。全局 external_labels 可以在与外部系统通信时补充这些上下文。但它不会重写每条本地存储序列,也不会覆盖对外序列中已经存在的同名标签。理解这两条边界,才能避免用错误的查询位置判断配置是否生效,也能避免误以为全局值一定压过目标值。 外部标签在相关对外边界补充缺少的名称,不重写本地存储序列,也不替换已有值。具体标签值来自源端联邦样例。历史集成执行缺口保持不变,本轮绘图不构成新的联邦、远端写入、告警送达或去重验证。 本地存储与对外身份并不相同 本地查询 up 通常看到抓取和重新标记产生的标签,外部标签 prom_region 并不一定出现。它没有出现在本地查询中,不能证明配置失败。应将适当的对外接口与本地结果比较;联邦接口提供了一种直接观察差异的方法,无需为了验证这个行为先接入远端存储系统。检查位置必须与希望验证的机制对应。 配置契约也涉及远端存储和告警等外部通信,但各条下游路径还可能有自己的重新标记与处理步骤。验证某条路径时,需要观察它实际到达的消费者。本地评估的记录规则不会因为保存了一条新序列,就自动获得所有外部标签。外部标签在相关通信边界补充,不是统一改写所有本地查询结果的机制。 序列已有的标签优先 如果目标序列已经具有 cluster="target-cluster",全局外部标签配置为 cluster="collector-cluster",不会把目标值替换掉。外部标签补充的是缺少的标签名称。这是已定义的行为,不能编造成新版本遇到冲突必定拒绝或记录错误。因此,本地指标对外导出时保留的集群值,可能不同于操作人员以为全局配置会强制设置的值。 需要区分目标身份与采集器身份时,应约定独立的采集器标签名,并检查导出器和重新标记规则是否与之冲突。联邦场景中,目标端的 honor_labels 还决定如何处理源标签与本端抓取标签的冲突。这个接收端行为,与源端如何向联邦响应补充外部标签是两件事,不能因为一侧正确便跳过另一侧检查。 稳定标签不会自动实现去重 改变全局标签值,会改变之后导出序列的身份。已经保存在下游的历史仍在旧标签下,不会被重写。修改选择器和面板时,应考虑这条历史边界。添加很多标签也不必然有益:固定标签提供上下文,不同采集器的不同取值则增加消费者需要处理的身份区别,应根据实际查询需求决定,而不是把能写的字段都写进去。 两个采集器抓取相同目标时,可能在不同副本标签下导出重复观测。直接跨副本求和,会让流量看起来翻倍。外部标签让这些副本可以被区分,却不会替下游配置高可用去重。标签也不是租户授权边界;如果存在隔离要求,应核对存储系统真实的权限和查询行为,不能仅依赖名称看起来来自不同环境。 告警重新标记发生在补充外部标签之后。因此,高可用采集器可以在发送等价告警前仅去掉副本区分字段,同时为需要它的存储链路保留该指标标签。仍须核对最终告警标签确实等价,不能把真正区分责任或环境的字段也删掉。度量副本去重与告警身份归一化属于不同路径,需要分别验收。 坏掉的样子 团队可能在本地查询中寻找外部标签,找不到便反复修改本来有效的配置。相反,也可能看到联邦输出带有预期标签,就认为远端写入和告警路由都已验证。应检查具体路径和最终标签,包括下游重新标记的影响,而不是把一个接口的结果当成全部集成已经成功的证据。目标标签与全局值相同的测试,还会掩盖真正的冲突优先级。 ...

六月 9, 2027 · 2 分钟 · XB Field Notes
原文样本出现在第零分钟。第一分钟的失效标记终止即时选择,但旧样本仍可能保留在三分钟范围内。第三分钟范围缺失成立,独立的两分钟等待在第五分钟达到触发。

Prometheus 序列失效与告警等待是不同机制

指标缺失不等于数值为零。Prometheus 会区分具有近期值的序列、已经标记失效的序列,以及找不到可用样本的选择器;告警又在查询结果之上使用自己的评估和等待规则。如果把这些机制统称为一个宽限时间,就可能漏掉被移除的目标,或者没有弄清到底缺少什么数据便宣布服务故障。准确的缺失告警首先需要准确描述观测对象。 图中是原文明示的合成样本,不是所有抓取失败或发现移除场景的统一时间表。第三分钟的 (0m,3m] 范围排除第零分钟样本;for: 2m 属于独立的告警层。历史本地 promtool 证据保持不变。真实采集、预期成员关系和通知送达仍需分别验证。 失效标记不是统一的五分钟计时器 即时选择器通常使用回看范围内最新的合格样本,默认回看范围是五分钟。失效标记会告诉引擎:在标记之后,不再让这条序列出现在即时选择结果中。它不要求旧值再保留五分钟。历史样本仍然存在于存储中,也可能继续参与范围查询;因此,即时查询已经看不到某条序列,与历史窗口里完全没有样本,并不是同一件事。 具体采集路径会影响行为。导出器不再输出某个指标、目标从服务发现消失,以及导出器自己提供样本时间戳,不一定遵循相同的时间过程。显式时间戳和失效跟踪配置也会改变结果。在依据抓取周期推测消失时刻之前,应先确认导出器使用哪条路径。可配置的回看范围本质上是查询选择规则,不是能够统一判定所有服务故障的计时器。 先确定要询问哪一种缺失 absent(selector) 在选择器没有任何元素时返回一;只要仍有匹配数据,即使数值为零,也返回空向量。absent_over_time(selector[3m]) 则询问所选三分钟范围内是否存在样本。失效标记之前的真实样本,可能继续留在这个范围内,使范围缺失表达式保持为空,直到该样本移出窗口。这不是查询互相矛盾,而是问题的时间范围不同。 告警规则的 for 又是一层机制:相关标签集必须在连续的评估中持续由表达式返回,经过等待时长后才进入触发状态。它不会配置序列失效,也不会延长查询回看范围。把三分钟缺失窗口再配上额外等待时间,会进一步延后通知,评估节奏和采集时机也影响最终延迟。选择这些参数时,应从允许的发现时间反推,而不是只把它们当作降噪旋钮。 坏掉的样子 absent(up{job="api"}) 不是抓取失败告警。若已配置目标仍存在,只是 up=0,选择器仍有元素,缺失条件便不成立。与零比较才能检查这个场景。但如果目标彻底从发现清单消失,只检查零又可能在其序列消失之后漏报。对目标是否应当存在有要求时,需要另行监测预期成员关系,不能让一个表达式混合承担所有职责。 宽泛的选择器也不会自动列举所有缺少的成员。一个副本仍有数据时,对整个任务求 absent 仍为空,即使其他几个预期副本已消失。可以匹配明确已知的身份,或者将观测序列与权威清单比较。标签被改名同样可能使旧匹配条件看起来像发生故障,而进程其实正在以新身份正常工作,所以规则与标签变更需要一起审阅。 快速验证 保存为 stale.py,运行 python3 stale.py。环境需要已经安装的 promtool,可以从 PATH 找到,或用 PROMTOOL 指定其可执行文件路径。此前记录的执行使用 Python 3.12.14 与 promtool 3.14.0;本轮文章复核没有重新运行该引擎。真实查询引擎在临时目录中评估合成样本,不启动服务器、监听端口或容器,也不联系外部服务。 ...

二月 3, 2027 · 2 分钟 · XB Field Notes