仪表盘整片变空的那一天
一句话总结
Envoy 的统计名称是一种一个字符串中包含多重含义的结构(cluster.<이름>.upstream_rq_total,占位符为集群名称)。导出到 Prometheus 时,这个字符串会被拆分为指标名称和标签,拆分规则可以自己添加。而且名称就是接口,哪怕只改一个单词,使用它的仪表板也会变空。
为什么需要它
代理里发生了什么,通过日志也能知道,但日志随着每个请求多一行,用来询问“现在整体怎么样”代价很高。统计则正好相反——它不会随请求数增长,从一开始就以汇总好的数字保存。
问题在于这些数字超过五百个。默认配置下就是如此,集群一多,每个集群还要再增加几十个。所以处理统计的工作,不在于“有什么”,而在于如何找到需要的、如何去掉不需要的。
工作原理
名称的三个分支。名称的第一个单词说明了这个数字是关于什么的。
| 前缀 | 关于什么的数字 | 代表 |
|---|---|---|
cluster.<이름>. |
上游一侧(占位符为集群名称) | upstream_rq_total、membership_healthy |
http.<stat_prefix>. |
该 HTTP 连接管理器所处理的请求一侧 | downstream_rq_5xx |
listener.<주소>. |
套接字一侧(占位符为地址) | downstream_cx_total |
同一个请求会在三个地方分别被统计。所以当三个数字对不上时,差异就是线索——监听器上已经建立了连接,而 http. 一侧的请求数很少,就说明只建立了连接却没有发送请求;http. 一侧的 5xx 增加了,而 cluster. 一侧的请求数没有变化,就说明请求连上游都没有到达。
种类不同。计数器(counter)是累积值,只会增加;gauge 是当前状态;直方图(histogram)是分布。管理端口的 /reset_counters 正如其名,只会重置计数器。gauge 是“现在有几台健康的服务器”这样的当前状态,没有什么可重置的。
取出的方法。/stats 可以加上 ?filter=<정규식>(占位符为正则表达式)和 ?format=json,两者可以一起使用。用 Prometheus 抓取时使用 /stats/prometheus,名称会在这里被拆分。cluster.good.upstream_rq_total 会变成一个指标名称,加上一个包含集群名称的标签。拆分规则 Envoy 默认内置了多条,也可以通过 stats_config.stats_tags 添加——如果在集群名称前加了团队名称作为前缀,就可以只把它提取为标签,在仪表板上按团队汇总查看。
去掉的方法。用 stats_config.stats_matcher 设置包含列表或排除列表。这里有一个重要的性质——被排除的统计不是在界面上被隐藏,而是根本不会被记录。之后需要了再恢复,这期间的值也不存在。
在现场相遇的样子
仪表板整个变空的那天。有人把 stat_prefix 改成了更易读的名称。这对流量没有任何影响,所以部署悄无声息地过去了,几天后有人问“这张图原来就是这样的吗”。这并不是值掉到了 0,而是时间序列本身消失了,对于不把“无数据”当作故障处理的告警,连响都没响。名称就是接口——修改之前,先找出使用它的一方。
时间序列过多的情况。在有几百个集群的代理中,统计数量会达到几万个。首先喊救命的是 Prometheus 一侧的存储成本,此时要动的不是采集周期,而是导出的列表。
把直方图当作计数器来读的情况。像请求耗时这样的值,只看一个平均值,几乎总是显得没问题。因为慢请求数量少,几乎不会让平均值有所变化。直方图就是为此而存在的,该看的不是平均值,而是上端的百分位数。管理端口的文本输出中会同时给出百分位数,但没有记录值时也会这样显示,所以必须区分“没有值”和“为 0”。
“重置了,值却没有变。”这是把 gauge 当成了计数器。如果养成区分这两种类型的习惯,就根本不会出现这个问题。
官方文档:Statistics overview · Administration interface
下一项实验要做什么
确认同一个请求会在三个分支的名称下分别被统计,用 filter 和 format=json 只取出需要的内容,并观察 Prometheus 输出中名称是如何被拆分的。然后依次尝试:/reset_counters 只对计数器生效,添加标签规则来生成标签,用排除列表去掉统计;最后只改一个单词 stat_prefix,亲眼看到旧名称的统计整体消失。