推送,别轮询
唯一会主动找上你的数据源,以及它改变了你被允许相信什么的两件事
Michel Wijnberg
到目前为止,本系列里的每一个数据源都是 Osprey 主动跑去询问的对象。一个 GRE 记录器加入某个区域, 向它索取一份数据库。一次 SNMP 遍历向某台设备索取一张表。问、等、收、再来一遍。
只有 BMP 是自己找上门来的。路由器向监控站开一条 TCP 会话然后开始说话,而且只要还有话可说就一直 说下去(RFC 7854)。没有人在轮询任何东西。
这听上去纯粹更好,而在某一个维度上确实如此。但在另外两个维度上,它改变了你有资格得出什么结论, 而那两件事都比”快”更有意思。
自己找上门来换来了什么
轮询有一个采样间隔,而采样间隔具备采样间隔的一切性质。任何在一个周期之内发生又翻转回去的事情, 就你的数据而言就等于没有发生过。一条在两次遍历之间抖动了两次的路由,看起来就像一条从未动过的路由。
被推送的数据流没有周期。一条撤销之所以到达,是因为确实发生了一次撤销,而它上面的时间戳是路由器的, 不是轮询器的。
对 BGP 而言,这件事比对其他大多数东西都更重要,因为 BGP 变动的频率远高于拓扑。用五分钟去遍历一张 全表,既极其昂贵,又对一切短命的东西视而不见。“这个前缀是什么时候变的”这个问题,只有在推送模型下 才有一个值得信任的答案。
代价之一:沉默不再意味着任何东西
现在说轮询白拿到手的那部分。
一个发出询问却什么也没收到的轮询器,非常清楚发生了什么:它问了,什么也没收到。这个”缺席”本身就是 一次带时间戳的观测,而且可以重试。
一个什么也没听见的监听者没有这种奢侈。无论是网络安静,还是会话已经消失,线路上的沉默都是同一副样子, 而在别的什么东西告诉你之前,后一种情况与前一种无从分辨。
这里具体的危险值得点名,因为它会把你本能想去看的那个指标反转过来。如果你把一条掉线的会话当作”没有 变化”,那么你已经听不到消息的那个 peer,就成了报告里最稳定的 peer。屏幕上最安静的那一行,正是 你已经失去的那一条。任何按变动率排序的排名,都会把你的盲区顶到最上面,还给它贴上”最健康的关系” 的标签。
第十二篇讲了 Osprey 对此的做法,也就是把会话中断记录为一次监控 空白,而不是记录为大规模撤销,这里就不重复了。对本系列而言,要点更窄:推送把解读沉默的责任从 协议转移到了你身上。 轮询模型把那个答案直接交给你,而你根本不必去想它。
代价之二:你在读别人的对话
第二项代价更微妙,而且会产生真实的解析缺陷。
BMP 并不给你一条 BGP 会话。它给你的是别人那条会话上的消息副本,包装之后转发过来。你并不在场参与 那次协商,而 BGP 消息并不自描述:你该如何解码一条 UPDATE,取决于双方 peer 在各自的 OPEN 里达成了 什么。
显而易见的做法是把上下文重建出来。Peer Up 携带了双方的 OPEN 消息,所以你可以读出协商结果并在整条 会话期间记住它。Osprey 就是这么做的。
然后,之后每一条消息的 per-peer 头里都带着一个 A 标志,指示被封装的 PDU 是否使用了传统的双字节 AS_PATH 格式(RFC 7854 §4.2)。它是逐消息的,所以解析器会依据它来解码这一条消息,而不是从 Peer Up 的上下文里去假定格式。
这两者可能互相矛盾。矛盾发生时,Osprey 依据标志来解码,并把这处出入记录下来,而不是悄悄把它抹平; 而这里的推理值得说出来,因为它可以推广:能力协商记录的是两台路由器约定要做什么,而标志描述的是这个 导出器对这一条消息实际做了什么。当”关于某项约定的证据”与”关于手头这串字节的证据”相冲突时,正在被 解析的是手头这串字节。
关于 Add-Path 也有一处对应的微妙之处,形状完全相同。被封装的 NLRI 是否携带路径标识符,取决于一次 带方向的协商,而适用哪个方向又取决于正在被监控的是哪个 RIB:默认的 Adj-RIB-In,还是 RFC 8671 增补的 Adj-RIB-Out,而后者由同一个 per-peer 头里的另一个标志来指示。所以解析器先读那个标志,再据此决定该 相信哪一次协商。同一个字段、两种读法,而做出选择的是一个位;如果你本身就是这条会话的当事方,你根本 不会去查它。
当你轮询一台设备时,这些问题一个都不会出现。你是在一个自己就是一等当事方的协议里,向一台设备询问 它自己。BMP 把路由器自己的视角递给你,同时拿走了它被协商出来的那个上下文;而每一个用来重建这个 上下文的字段,都是承重的。
两种采集方式落在同一张表里,差别就在两列上。BMP 那几行最近一次出现是在一天前,这并不是故障: 那些会话没有任何变化,而推送型来源在没有变化时就是沉默的。SNMP 那几行写着刚刚,是因为有人 主动去问了一次。同样的一句断言,背后是两种截然不同的相信理由,而其中只有一种,沉默本身也是 信息。
那份你不开口就永远不会来的报告
最后一件事,也是最小、最恼人的一件。
BMP 定义了 Statistics Report:按 peer 的周期性计数器,其中包括拒绝计数和 treat-as-withdraw 计数, 它们告诉你某个 peer 的策略正在丢弃什么。极好的数据。正是做一张记分卡所需要的。
它们不会到达。
不是因为解析器有缺陷,也不是因为导出器坏了。IOS 就是不会发送 Statistics Report,除非有人告诉它一个
上报周期,而且它没有默认值。如果 BMP 服务器配置下没有 stats-reporting-period,这种消息类型就永远
不会出现在线路上,而每一个按 peer 的统计字段都会永远空着,任何地方都不会报错。
实验网络就这样跑了一段时间,而那些空列的诚实读法不是”这个 peer 什么都没拒绝”,而是”从来没有人让 这台路由器开口说”。修复是在六台路由器上加一行配置,并写进 startup-config,免得一次重载悄悄把这次 测量撤销掉。
这是一个很小的运维细节,却有一个普遍的形状:一个推送型数据源只能告诉你它被配置去发送的东西。 在轮询里,是你控制何时提出问题。在推送里,你只能观测到导出器被设定去发出的东西,而一条没有被发送的 消息,与一条从未被生成的消息无从分辨。
要点
推送更好,而推送不是免费的。
对于像 BGP 这样变动飞快的东西,你获得了唯一值得信任的那条时间线;而你交出去的,是轮询从来不曾逼你 去想的两件事:沉默曾经是一次观测,以及你曾经是自己所读的那场对话的当事方。
这两件事在这套代码里都体现为一些细小而具体的用心。一次被记录为空白的会话中断。一个取自标志而非取自 能力协商的 AS_PATH 宽度。一列空着、而且知道自己为什么空着的统计数据。它们没有一个称得上巧妙。它们 全都是同一句承认:一条自己找上门来的消息,关于它自身的缺席,能告诉你的比你自己提出的一个问题更少。
本系列到此结束。如果说有一条贯穿始终的线索,那就是:这个产品里的每一个答案,其价值恰好等于它能为 “这个答案是怎么得来的”给出的那份交代。