☰
Higress云原生网关可观测性实战:指标、日志、链路与告警闭环
2026/11/4 5:21:37 网站建设 项目流程

云原生网关这一层的可观测性,最容易出现的一种情况是:网关本身跑得好好的,业务侧却反馈"接口偶尔很慢",你去查日志,日志里只有一行 200;你去查上游服务,上游服务说自己没问题;最后把锅推给网络。这种扯皮之所以反复发生,根子不在排障能力,而在于网关这一层的数据没有被完整地采集、存储和关联起来。我这次做的事情,就是在 Higress 这套云原生网关上,把监控面板从零搭起来,用四个实操任务把指标、日志、链路、告警这条全链路可观测性跑通。Higress 基于 Envoy 做数据面,天然带着大量细粒度的运行时统计,问题在于这些数据默认是"埋着的",不采集、不建模、不成面板,等于没有。这篇文章适合三类人看:正在用 Higress 或类似 Envoy 系网关、想补齐监控的运维同学;需要给网关做容量评估和 SLO 报表的 SRE;以及只想搞清楚"一个网关面板到底该看哪几个图"的开发同学。我会把每一步的配置、参数怎么算出来的、为什么这么选,都摊开讲清楚,你可以直接抄作业,也可以只挑其中一段用。

1. 先想清楚:网关可观测性到底要解决什么问题

1.1 三层数据的分工与边界

很多人一上手就去装 Grafana 找现成模板,导进来一堆图,红红绿绿很好看,真出事的时候一个都用不上。我踩过这个坑,所以这次先把数据分层想明白再动手。

网关这一层的数据,本质上是三类,各有各的不可替代性。**指标(Metrics)**回答的是"有没有异常、异常在哪个维度",它的价值在于聚合和趋势:每秒多少请求、P99 延迟曲线、5xx 占比、上游连接池打满没有。指标的问题是粒度粗,它只能告诉你"某个路由的错误率涨了",但告诉不了你"是哪一次请求、带了什么 header、上游返回了什么"。

**日志(Logs)**回答的是"具体这一次请求发生了什么"。Envoy 的 access log 里,只要你字段配得够全,请求方法、路径、状态码、上游地址、耗时分解、请求 ID 全都有。日志的代价是量大、存储贵、查询慢,所以它必须做字段治理和采样,不能无脑全存。

**链路(Traces)**回答的是"时间花在哪个环节"。一个请求进来,经过路由匹配、限流插件、认证插件、上游连接、上游处理、响应回写,链路能把每个 span 的耗时铺开。链路最贵,所以要按采样率来,不能全采。

这三层不是三选一,而是互相跳转的关系:指标发现异常 → 点开面板定位到具体路由和时间窗 → 跳到日志查具体请求 → 从日志里的 trace id 跳到链路看耗时分布。我这次的四个实操任务,就是按这个跳转链条来切分的:任务一指标准入,任务二面板建模,任务三日志接入,任务四链路与告警闭环。

为什么按这个顺序?因为指标链路的成本最低、见效最快,先把"有没有问题"这个问题解决掉;日志和链路是往下钻的工具,没有指标先圈定范围,海量日志里你根本不知道该看哪一分钟。顺序反过来做,大概率是搭了一堆东西,最后一个都没用起来。

注意:不要把网关可观测性和上游服务的可观测性混成一件事。网关能看到的是"链路边缘"的信息,它能告诉你上游慢,但告诉不了你上游为什么慢。定位到上游之后,要能跳到上游自己的面板,这个跳转设计比多画十个图更有价值。

1.2 四个实操任务的前置条件盘点

动手前先把家底盘清楚,不然后面每一步都会卡。我用的是一个标准的三件套环境:Kubernetes 集群、已经部署好的 Higress(控制面和数据面都在跑,网关 Service 前面挂着业务流量)、以及一套独立的可观测性栈(Prometheus + Grafana + Loki + 一个 trace 后端)。如果你用的是托管版,指标端点可能已经帮你开好了,那就跳过任务一的开启动作,直接从抓取配置开始。

需要确认的几件事,我用一张表列出来,你可以照着核一遍:

检查项怎么确认不满足会怎样
网关 Pod 的指标端点可达进 Pod 或从集群内 curl 指标路径抓取直接失败,后面全空
Pod 标签是否有稳定可用的选择器kubectl get pod --show-labels服务发现抓不到目标,或抓到一堆无关 Pod
网关是否已有 ServiceMonitor 或抓取配置查 Prometheus 的 targets 页面重复抓取,指标翻倍,聚合全错
控制面自身的指标是否暴露查控制面 Pod 端口配置下发失败类的故障无法预警
日志输出是 stdout 还是文件看部署清单里的日志配置采集侧配错,日志一条收不到
是否已有 trace 后端和采集器查采集器 Deployment任务四要临时补一套,时间成本翻倍

这张表里最容易被忽略的是第三行。我遇到过一次,网关侧的抓取配置由平台团队统一注入,我又在自己的 Prometheus 里手写了一份,结果同一个 Pod 被两个 job 抓,sum(rate(...))出来的 QPS 正好是真实值的两倍,差点按这个数据去做扩容决策。核对办法很简单,去 targets 页面看同一个实例是不是出现了两次,或者看count by (__name__)({__name__=~"envoy_http_downstream_rq_total"})的 series 数是不是异常偏大。

还有一点,标签规划要在动手前定死。至少要保证三个维度可用:网关实例(哪个 Pod/哪个节点)、路由或域名(哪条规则)、上游服务(打到哪个后端)。这三个维度决定了你后面所有面板的下钻路径。如果 Envoy 吐出来的原始指标里没有你想要的维度,就得靠 relabel 补,或者靠日志补,这个决定越早做越省事。

1.3 面板设计的"黄金信号"取舍

画面板之前先定指标集,不然就是无限加图。我按四个黄金信号来做取舍:流量、错误、延迟、饱和度。网关这一层还要再加一个特殊的:配置状态,也就是控制面下发的配置是否成功、版本是否一致。这一项经常被漏掉,但它恰恰是网关类故障里最隐蔽的一类——数据面还在跑旧配置,流量看着正常,新规则就是不生效。

取舍的原则是:每个信号只保留"能驱动动作"的图。什么叫能驱动动作?看到这张图,你能明确决定"要不要扩容""要不要回滚""要不要限流"。做不到这一点的图,再好看也删掉。我最终的面板控制在两屏以内,第一屏是总览(四个黄金信号 + 配置状态),第二屏是下钻(按路由、按上游、按状态码细分)。这个结构后面第三节会展开。

2. 任务一:打通指标采集链路

2.1 指标端点确认与端口规划

Higress 的数据面是 Envoy,指标来自 Envoy 的 stats 子系统,通过管理端口以文本格式暴露。默认情况下这个端点不一定对外,需要确认两件事:Pod 内实际监听的端口是哪个,以及 Service 有没有把它映射出来。我习惯的做法是不依赖 Service,直接用 Pod 服务发现抓,这样少一层转发,也少一个可能配错的环节。

先确认端点。进到网关 Pod 里,用 curl 打一下指标路径,看返回是不是一大段以# HELP开头的文本。返回正常说明端点通,返回 404 说明路径不对,返回连接拒绝说明端口不对或者监听地址被限制在 127.0.0.1。第三种情况最常见,Envoy 的管理端口默认可能只绑本地回环,这时候要么改监听地址,要么走 sidecar 转发,要么让平台侧开一个专门的 metrics 端口。这一步不要猜,一定要实测,我见过有人照着一篇两年前的文章配了端口,结果那版本早换了。

端点确认之后看指标基数。执行一条命令统计当前暴露的指标行数,如果超过十万行,就要提前规划采样和裁剪,不然 Prometheus 那边内存会被吃掉一大块。Envoy 的指标里有一大类是 per-cluster、per-listener、per-route 的细粒度统计,这些是指标基数爆炸的主要来源,后面 2.3 会讲怎么裁。

提示:先在测试环境把端点跑通再上生产。生产环境里随便进 Pod 执行命令,有的团队是不允许的,提前问清楚有没有跳板机或者调试容器通道,别到现场才发现进不去。

2.2 抓取配置与标签重写

抓取配置我推荐用 Pod 服务发现,配合 relabel 做两件事:筛出正确的 Pod、补上业务需要的标签。下面这份配置是我实际在用的简化版,你可以按自己的标签体系改:

scrape_configs: - job_name: higress-gateway scrape_interval: 15s scrape_timeout: 10s metrics_path: /stats/prometheus kubernetes_sd_configs: - role: pod namespaces: names: - higress-system relabel_configs: # 只保留网关数据面 Pod,排除控制面和无关组件 - source_labels: [__meta_kubernetes_pod_label_app] regex: higress-gateway action: keep # 只保留 Running 状态的 Pod,避免抓到正在终止的实例 - source_labels: [__meta_kubernetes_pod_phase] regex: Running action: keep # 把节点信息补进来,方便按节点维度聚合 - source_labels: [__meta_kubernetes_pod_node_name] target_label: node # 用 Pod 名做实例标识,比 IP 更稳定 - source_labels: [__meta_kubernetes_pod_name] target_label: instance # 补一个集群标识,多集群场景下必须 - target_label: cluster replacement: prod-shanghai

几个细节值得说。scrape_timeout一定要小于scrape_interval,否则会出现这次抓取还没结束下次就开始了,Prometheus 会直接判定超时。我设成 10s 对 15s,留了 5s 余量。这个余量不是随便给的,要看端点的实际响应时间:如果指标量大,序列化本身就要好几秒,余量给小了会周期性丢点,表现为曲线上规律性的缺口,很难排查。

instance用 Pod 名而不是 IP,是因为 Pod 重建后 IP 会变,用 IP 做实例标识会导致面板上的实例列表不断堆积历史垃圾。用 Pod 名也有代价,滚动更新时 Pod 名会变,短时间面板上会出现新旧两个实例,这个是正常的,看总量的时候用sum聚合掉就行。

标签这块还有一个坑:不要在原标签上做覆盖写。我见过有人在 relabel 里把job改成业务名,结果和别的 job 冲突,导致告警规则里按 job 匹配的表达式全部失效。要加业务维度,就新加一个标签,比如biz_cluster,别动 Prometheus 的内置标签。

2.3 抓取参数计算与指标基数控制

这一段是很多人跳过但最关键的部分。抓取间隔和指标基数是两个互相牵制的参数,直接决定了你的 Prometheus 要吃掉多少内存和磁盘。

先说基数。Envoy 的指标是指标名 + 标签的组合,每个组合是一条时间序列(series)。假设你的集群有 200 条路由规则、每个路由关联 3 个上游集群、网关有 6 个副本,那么仅envoy_cluster_upstream_rq_total这一类指标,series 数大约是 200 × 3 × 6 = 3600 条。Envoy 的 per-cluster 指标有几十个,光这一块就能到十万级 series。再加上 per-listener、per-http-conn-manager 的统计,数字会更吓人。

我的做法是先量后裁。部署好抓取之后,在 Prometheus 里查一下prometheus_tsdb_head_series,看总共多少条。同时用topk(20, count by (__name__)({__name__=~".+"}))看哪些指标贡献了最多 series。通常是 per-route 和 per-cluster 的那批细粒度指标占大头。

裁剪用metric_relabel_configs,按指标名正则丢掉不需要的。原则是:你面板和告警里用不到的,全部丢掉。这一步一定要在抓取侧做,而不是在查询侧过滤,因为在查询侧过滤只是不显示,存储和内存照样吃掉。假设你丢掉了 60% 的指标,series 从 20 万降到 8 万,Prometheus 的内存占用大概是线性下降的。

metric_relabel_configs: # 丢掉不需要的直方图分桶,只保留关键分位 - source_labels: [__name__] regex: 'envoy_.*_bucket' action: drop # 丢掉监听器级别的重复统计 - source_labels: [__name__] regex: 'envoy_listener_.*' action: drop # 丢掉内部管理接口的统计 - source_labels: [__name__] regex: 'envoy_cluster_.*_internal.*' action: drop

这里要注意,直方图分桶丢掉之后,histogram_quantile就没法用了,P99 只能改用 Envoy 自带的汇总值或者日志侧计算。所以"丢桶"这个动作要和你面板上用什么算法算分位数联动决定,不能拍脑袋。我这次保留了分桶,因为延迟分位数是网关面板的核心图,宁可多存一点。真正丢的是那些名字里带internal、admin、wasm的运维类统计,这些基本不用于业务告警。

存储容量估算也给个公式,方便你提前规划磁盘。每天采样点总数 = series 数 × (86400 / 抓取间隔)。以 8 万 series、15 秒间隔算,每天 4.6 亿个采样点。按每点压缩后约 1 到 2 字节估算,一天的原始存储大约 0.5 到 1 GB。保留 30 天就是 15 到 30 GB,加上索引开销翻一倍左右。这个量级单机 Prometheus 完全扛得住,但如果你的 series 数到了百万级,就要考虑分片或者换长期存储方案了。

# 抓取是否健康:返回 1 表示正常 up{job="higress-gateway"} # 抓取耗时,接近 timeout 就要警惕 scrape_duration_seconds{job="higress-gateway"} # 抓取到的样本数,异常波动说明端点或指标基数变了 scrape_samples_scraped{job="higress-gateway"}

这三条要放到一个专门的面板上,叫"采集健康度"。它的作用是:当你的业务面板出现空白或者断点时,一眼能判断是业务真的没流量,还是采集链路自己坏了。这个区分极其重要,我有一次半夜被告警叫起来,业务指标全掉到零,紧张了二十分钟才发现是 Prometheus 的抓取配置被误改了。从那以后这条健康度面板就一直放在最显眼的位置。

3. 任务二:Grafana 面板从零搭起

3.1 面板结构与变量设计

面板我不建议一上来就堆图,先把结构定下来。我的做法是两层:第一层"总览",五个区块,每个区块最多两张图;第二层"下钻",按路由和上游两个维度展开。这个结构参考的是排障时的实际浏览路径——先看整体有没有事,再看是哪个维度的事,最后看具体是哪个实体的事。

变量设计是面板好不好用的分水岭。我配了三个变量:cluster(集群)、instance(网关实例)、route(路由/域名)。前两个用 label_values 从指标里查,route这个比较麻烦,因为 Envoy 的 per-route 指标不一定带路由名标签,很多时候只有 route 的内部 ID。如果标签不理想,有两个办法:一是在 relabel 阶段做映射,把 ID 换成可读名;二是干脆放弃在指标侧做路由维度,改用日志侧聚合。我这次选了第一种,在网关配置里给路由加了可读的统计前缀,这样指标里直接带可读名,面板上不用做任何转换。

变量的一个重要细节是要设置默认值和"全部"选项。默认值设成当前主集群,不要设成 All,因为 All 会把所有集群的 series 都拉出来,面板打开慢且图上的线全糊在一起。同时变量之间要有级联关系,选了集群之后实例列表只显示该集群的,这个用 Grafana 的 query 变量加 label 过滤就能做到。

提示:面板的刷新间隔设成 30 秒到 1 分钟就够了,不要设 5 秒。网关面板上多数图用的是 1 分钟以上的 rate 窗口,刷新再快也看不出新东西,只会把你的 Prometheus 打满。

3.2 核心 PromQL 与图表配置

下面这几条是我面板上的主力查询,都验证过可用。先看流量:

# 网关总 QPS,按状态码分类 sum by (response_code_class) ( rate(envoy_http_downstream_rq_xx{job="higress-gateway"}[1m]) ) # 总请求速率 sum(rate(envoy_http_downstream_rq_total{job="higress-gateway"}[1m]))

第一个查询里的response_code_class是 Envoy 的聚合维度,通常是 2、3、4、5 这样的类别,够看趋势了。如果指标名在你的版本里不一样,去端点输出里 grep 一下downstream_rq就能找到对应的。这里rate窗口用 1 分钟,和抓取间隔 15 秒是 4 倍关系,是比较稳的比例,窗口太小会因为采样抖动导致曲线毛刺,太大又会迟钝。

错误率我拆成两层看:网关自身产生的错误(比如路由匹配失败、限流拒绝)和上游返回的错误。这两类错误的处置动作完全不同,前者要改网关配置,后者要推动上游。混在一起看,你永远不知道该找谁。

# 上游 5xx 比例 sum(rate(envoy_cluster_upstream_rq_xx{envoy_response_code_class="5"}[1m])) / sum(rate(envoy_cluster_upstream_rq_total[1m])) # 网关本地拒绝(限流、认证失败等)比例 sum(rate(envoy_http_downstream_rq_xx{envoy_response_code_class="4"}[1m])) / sum(rate(envoy_http_downstream_rq_total[1m]))

延迟图我用分位数曲线加一条均值线。只看均值会被长尾骗,只看 P99 又会因为少量请求抖动而误报,两条一起看,能判断是"整体变慢"还是"少数请求极慢"。分位数用直方图算:

histogram_quantile(0.99, sum by (le, route) ( rate(envoy_http_downstream_rq_time_bucket{job="higress-gateway"}[5m]) ) )

这里的le是直方图的分桶边界标签,sum by (le, route)这个写法是为了在按路由聚合的同时保留分桶信息,少写一个 le 就会算出错误的数值,这是新手最常见的错误之一。另外rate窗口用 5 分钟,是因为直方图本身采样密度低,窗口太短会导致分位数计算不稳定,曲线上蹿下跳。

饱和度这块看三个东西:上游连接池使用率、活跃连接数、内存。连接池打满是网关侧最典型的"看起来没报错但请求就是慢"的原因。

# 上游连接池使用率 sum by (cluster_name) (envoy_cluster_upstream_cx_active) / sum by (cluster_name) (envoy_cluster_upstream_cx_max)

这个比值超过 0.8 我就开始关注,超过 0.95 基本就是排队了。要注意cx_max如果是 0 或者不存在,除出来会是 NaN 或者无穷大,面板上要加一个clamp_max或者在查询里过滤掉,不然图上会出现难看的断点。

配置状态那块图比较特殊,看的是控制面下发是否成功、数据面加载的配置版本是否和控制面一致。这类指标通常来自控制面而不是数据面,指标名各版本差异较大,我就不写死具体名字了,你去控制面端点里 grep 一下config或者push相关的统计,一般能找到下发次数、失败次数、最后下发时间这几个。我面板上放的是"最近一次配置下发距今时长",超过 10 分钟没有新下发(同时有配置变更)就说明有问题。

3.3 图表选型与可读性打磨

图表的选型比查询写得好不好更影响可读性。我总结了几条自己的规矩:

时间序列类的,超过 10 条线就不要画折线图了,改用热力图或者 Top N 柱状图。比如按路由看 QPS,200 条路由画在一张图上就是一团毛线,改成topk(10, ...)的柱状图,问题立刻清晰。分位数用折线,但只画 P50、P95、P99 三条,不要画 P999,网关这一层的 P999 受极端个案影响太大,日常看没有意义,真要查再去下钻。

错误率用堆叠面积图,把 4xx 和 5xx 分开堆,这样一眼能看出错误结构的变化。饱和度用仪表盘或者阈值着色的单值面板,配红黄绿三档,比折线图直观得多。

单位一定要设。延迟单位设成秒并且开自动缩放,QPS 单位设成 reqps,比率类设成 percentunit。这些看起来是小事,但单位不对的图会让人误判一个数量级。我见过有人把毫秒当秒配,一条 200ms 的曲线看起来像 200 秒,紧急扩容了一轮,扩容完发现根本没这个必要。

还有就是面板要写注解。Grafana 的 annotation 功能可以接一个数据源,把发布、配置变更、扩缩容事件标在时间轴上。这个功能的价值在于:当你在图上看到一条突刺,时间轴上正好有个发布标记,排障路径直接从"猜"变成"验证"。我现在的做法是把发布系统的事件写进一个轻量数据源,Grafana 定时拉。这条做起来很便宜,收益极大。

4. 任务三:访问日志接入与字段治理

4.1 日志格式选型与采集路径

指标只能告诉你"有异常",日志才能告诉你"异常长什么样"。Higress 的访问日志来自 Envoy,输出格式可以自定义,我强烈建议用结构化的 JSON,不要用默认的文本格式。理由很直接:JSON 可以直接在日志系统里按字段过滤和聚合,文本格式你得靠正则去抠,抠出来的东西还经常因为字段顺序变化而错位。

日志字段我按用途分了三组。第一组是标识类:请求 ID、trace ID、时间戳、网关实例、路由名、上游集群。第二组是请求类:方法、路径、协议、请求体大小、请求头里的关键字段(比如客户端 IP、UA 摘要)。第三组是结果类:响应码、响应大小、总耗时、上游耗时、上游地址、重试次数、失败原因。这三组里,trace ID 和上游耗时是最容易被漏掉又最关键的,前者是跳转到链路的钥匙,后者是区分"网关慢"和"上游慢"的唯一依据。

采集路径上,如果日志走 stdout,就用 DaemonSet 类的采集器在每个节点上收,配置里按 Pod 标签过滤,只收网关命名空间的。这条路径的优点是简单、不侵入,缺点是采集器和业务容器抢节点资源,大流量下要注意采集器的资源限制设够。如果日志写文件,就用 sidecar 或者节点上的采集器读挂载卷,这样能配上轮转策略,避免日志把磁盘写满。我这次用的是 stdout 路径,因为集群里已经有统一的采集器了,复用最省事。

采集器配置里有两个参数必须调:批量大小和发送间隔。批量太小会导致请求数暴涨,批量太大又会有延迟。我的经验值是每批 1 到 2 MB、发送间隔 3 到 5 秒,在日志延迟和采集器压力之间取个平衡。日志从产生到可查的延迟,我用这个配置大约在 5 到 10 秒,排障够用了。

注意:日志里的请求头和 Cookie 一定要做脱敏。网关日志天然会带上认证头、会话标识这类敏感字段,直接落盘是合规风险。我是在网关的日志格式配置里就把这些字段排除掉,而不是在采集侧做过滤,因为采集侧过滤意味着敏感数据已经经过了网络和缓冲区。

4.2 字段治理与查询建模

日志接进来之后不要急着用,先做字段治理。治理的目标是让"从指标跳到日志"这个动作能一次成功。具体要做三件事。

第一件,统一字段名。指标里叫route,日志里就不能叫route_name,否则跳转时要写两套映射,早晚出错。我们内部定了一套字段字典,网关、上游服务、前端埋点都用同一套命名,跳转链接是模板化的,改一处全生效。这件事的投入产出比非常高,虽然一开始要花半天对齐。

第二件,控制字段基数。日志字段里如果有 URL 全路径、用户 ID 这类高基数字段,日志系统的索引会迅速膨胀。我的做法是路径只保留路由级前缀(把路径参数归一化掉),用户标识只保留哈希后的前几位用于关联,不保留原始值。归一化这一步最好在网关侧用日志格式化插件做,不要在采集侧做,因为采集侧做归一化意味着要维护一套正则,网关配置改了正则就失效。

第三件,建立指标到日志的跳转。Grafana 里配数据链接,从时间序列面板点一个点,直接跳到日志系统,带上时间窗、路由名、实例名作为过滤条件。这个配置起来就是几行 JSON,但用起来体验完全不一样——原来要手动复制时间戳去日志系统里查,现在一次点击就到。

日志侧我常用的查询有三类。按 trace ID 精确查单次请求,这是排障的终点;按路由 + 状态码 + 时间窗聚合,看错误集中在哪些路径;按上游耗时排序取 Top N,找出最慢的那批请求看共同点。第三类最容易出成果,我靠这个查出过一次上游连接复用配置不当导致的问题:Top 慢请求全部集中在某几个上游实例上,而那几个实例的 IP 恰好落在同一个节点,最后定位到是节点间的网络路径有问题。

{ "route": "api-order-v2", "response_code": 502, "upstream_host": "10.x.x.x:8080", "duration_ms": 30012, "upstream_time_ms": 30000, "retry_count": 2, "trace_id": "b7c3f1a2e8d94f60" }

上面这条日志是个典型样本:总耗时 30 秒,上游耗时 30 秒,说明时间全花在上游;重试了 2 次,说明上游有间歇性失败;502 加上重试,指向的是上游连接建立失败而不是业务逻辑错误。只看指标的话,你只能看到这个路由的错误率涨了,看不到这些细节。

4.3 日志成本与保留策略

日志是最容易失控的成本项。我见过一个中等流量的网关,日志一天写入几个 TB,原因就是全字段记录加上没有采样。

控制成本有四个手段,按效果排序:减少字段、降低保留期、采样、压缩。减少字段是最直接的,日志里那些从来不查的字段(比如完整的请求头、完整的响应头)直接砍掉,体积能降一半以上。保留期我分成三档:错误日志保留 30 天,慢请求日志保留 14 天,正常请求日志保留 3 天。这个分档的依据是"不同日志的查询时间窗不同"——出问题之后你几乎不会去查三天前的正常请求日志。

采样要谨慎。错误日志一定不能采样,慢请求日志也不能采样,只有正常请求的日志可以按比例采样。我一般是 10% 到 20%,同时保证采样是按 trace ID 哈希做的,这样同一条链路上的日志要么都采要么都不采,不会出现链路断片。

提示:日志量突然翻倍,先别急着加存储,去看是不是某个上游返回了大量错误导致重试日志暴涨。重试日志的放大效应很明显,一次失败重试 3 次就是 4 条日志,指标上只体现为一次失败。

5. 任务四:链路追踪与告警闭环

5.1 采样率推导与追踪接入

链路追踪是最贵的一层,所以采样率必须算着来,不能拍脑袋设 100%。

先算流量。假设网关峰值 QPS 是 3000,平均每个请求产生 8 个 span(网关入口、若干插件、上游入口、上游内部若干段、出口),那么全采的话每秒 24000 个 span。按每个 span 平均 500 字节算,每秒 12 MB,一天接近 1 TB。这个量级对多数团队来说是不可接受的。

按 1% 采样算,一天约 10 GB,这个量级就比较友好了。1% 够不够用?关键看你要用它做什么。如果只是"看看链路长什么样""定位某一次慢请求",1% 完全够,因为慢请求往往会触发额外的采样策略。如果要做统计意义上的分析(比如"某类请求的平均 span 数分布"),1% 的样本量在高峰期每秒 30 个 trace,一天两百多万条,统计上足够了。

我的实际配置是基础采样 1% + 尾部采样补充:基础采样保证日常有样本;慢请求(耗时超过阈值的)和错误请求全部保留。尾部采样需要在采集器侧做,因为它要等整条链路结束才能判断,配置上比头部采样复杂一些,但效果值得——你真正关心的那批请求 100% 有链路,不关心的那批只留 1%。

接入配置上,网关侧要指定 trace 后端类型和采样率,然后由采集器统一接收和转发。这里有个细节:下游服务要能继续这个 trace,也就是网关注入的 trace 上下文要被上游服务透传。如果上游服务不认这个上下文,链路在网关之后就断了,你会看到一个只有入口 span 的"半截链路"。判断办法很简单,看链路里有没有上游服务的 span,没有就是透传没配好。

5.2 告警规则设计与阈值推导

告警是整个链路的收口。面板是给人看的,告警是给系统看的,两者用的指标应该一致,但阈值逻辑不同:面板要宽,告警要严。

我设了四类告警,都跑过一段时间调过阈值:

第一类,可用性告警。上游 5xx 比例超过 5%,持续 3 分钟。为什么是 5%?因为网关侧的正常 5xx 基线通常在 0.1% 以下,5% 意味着有实质性故障。为什么持续 3 分钟?因为 1 分钟窗口太敏感,滚动发布时的短暂错误也会触发;5 分钟又太迟钝。3 分钟是我试了几轮之后觉得最平衡的值。

第二类,延迟告警。P99 延迟超过基线值的 2 倍,持续 5 分钟。这里用"基线倍数"而不是固定阈值,因为不同路由的延迟差异巨大,一个固定值要么对小路由太宽松,要么对大路由天天误报。基线可以取过去 7 天的同时段中位数,这个在查询里用 offset 就能实现。

# 延迟相对基线告警 histogram_quantile(0.99, sum by (le, route) (rate(envoy_http_downstream_rq_time_bucket[5m])) ) > 2 * histogram_quantile(0.99, sum by (le, route) (rate(envoy_http_downstream_rq_time_bucket[5m] offset 7d)) )

第三类,饱和度告警。上游连接池使用率超过 90%,持续 2 分钟。这类告警的特点是"来得快",一旦打满,请求会立刻排队,延迟曲线瞬间起飞,所以要给更短的持续时间和更高的优先级。

第四类,采集健康度告警。抓取目标 down 超过 1 分钟,或者抓取样本数相比前一天同期下降超过 50%。这类告警是"元告警",保护的是监控本身。它经常被忽略,但没有它,你的所有业务告警都建立在一个可能已经瞎了的基础上。

告警规则的写法上,我坚持两条:必须有 summary 和 description,写清楚"哪个路由、什么现象、建议动作";必须配 runbook 链接,指向具体的排查步骤。这两条看起来是形式,实际上是逼着你在写告警的时候就想清楚"收到这条告警的人该干什么"。想不清楚的告警,说明它不该存在。

5.3 从告警到根因的三段式排查路径

链路搭完之后,实际的排障流程变成三段,我把它固化成了一张速查表,贴在值班文档里:

阶段看什么典型结论下一步
第一段:圈范围总览面板的四个黄金信号哪类信号异常、从什么时间开始切到下钻面板
第二段:定实体按路由/上游/实例拆分的曲线集中在某个路由,或所有路由都异常路由问题查配置,全局问题查资源
第三段:找证据日志按 trace ID 精确查 + 链路耗时分布网关慢、上游慢、还是网络慢分别对应改配置、推上游、查网络

三段式的好处是每一步都有明确的产出,不会出现"看了一圈不知道看什么"的情况。我在值班时按这个流程走,多数问题能在十分钟内定位到具体实体。

举个实际的例子。有一次告警说某路由 5xx 涨到 8%。第一段,看总览,确认是错误信号异常,延迟正常——说明不是慢导致的超时,是直接失败。第二段,下钻按上游拆,发现只有一个上游实例在报错,其他实例正常。第三段,日志按这个上游地址过滤,发现全是连接被拒绝,链路显示 span 在建立连接阶段就结束了。结论很清晰:那个上游实例的健康检查有问题,流量还在往上打。处理动作是把那个实例从上游摘掉,然后通知上游团队查进程状态。

整个过程没用到什么高级技巧,靠的就是三层数据能连起来、能互相跳转。这也是我一直强调的:可观测性的价值不在于数据多,而在于数据之间能对话。一个能跳转的简单面板,胜过十个孤立的漂亮图表。

6. 踩坑实录与常见问题速查

6.1 高频问题与排查方法

这套东西搭下来,遇到的问题不少,我挑几个有代表性的整理成表,你遇到类似现象可以先对着查:

现象最可能的原因排查动作
面板全空,但网关有流量抓取目标没匹配上,或指标路径错查 Prometheus targets 页面,看目标是否 UP
QPS 曲线是真实值的整数倍多个 job 重复抓取同一实例查同一实例的 series 是否重复出现
延迟分位数曲线剧烈抖动rate 窗口太短,或直方图分桶太少把窗口从 1m 调到 5m,检查分桶配置
分位数算出明显不合理的值sum by漏了le标签检查聚合表达式是否保留 le
日志一条都收不到采集器标签选择器不匹配,或日志走文件而采集器读 stdout对比采集器配置和 Pod 实际标签
日志延迟很大采集器批量参数过大,或下游写入慢调小批量大小和发送间隔
链路只有入口 span上游服务没有透传 trace 上下文抓一次请求看上游收到的头里有没有 trace 标识
配置变更后监控没反应数据面还在跑旧配置对比控制面版本和数据面版本,看看最后下发时间
告警反复触发又恢复阈值太贴近基线,或持续时间太短拉长时间窗看基线波动范围,重设持续时长
Prometheus 内存持续上涨指标基数失控查贡献 series 最多的指标名,做 relabel 裁剪

这张表里我特别想展开说两个。

延迟曲线抖动那个。一开始我用 1 分钟窗口算 P99,曲线像心电图一样,上蹿下跳。我以为是真实波动,还专门去查了业务侧,结果业务侧一切正常。后来想明白了:直方图的分位数计算依赖每个桶里的样本数,1 分钟窗口内某些高延迟桶的样本数可能只有个位数,样本太少导致分位数估计方差极大。把窗口拉到 5 分钟、同时确认分桶边界覆盖了实际延迟范围之后,曲线立刻平滑了。这个问题的本质是"分位数是统计量,统计量需要足够的样本",不是数据本身的问题。

配置版本不一致那个。这个坑更隐蔽。现象是新加的一条路由规则怎么都不生效,但监控面板上一切正常,没有任何错误。我一开始怀疑是规则写错了,反复检查配置语法,没问题。后来在面板上加了一个"配置下发时间"的图,发现数据面最后加载配置的时间停在了半小时前——也就是控制面一直在下发,数据面一直没接住。最终定位是数据面和控制面之间的通信有问题,不是配置本身的问题。这件事之后,我把"配置版本一致性"作为了一个独立的监控项,并且给它配了告警。

6.2 几条从实操里换来的经验

第一条,先建面板再看告警,顺序别反。告警阈值必须建立在你对曲线形态有直观认识的基础上。我见过有人照着社区文章的阈值直接配,结果那个阈值在他们业务形态下天天误报,最后整个告警群被静音,真出事的时候没人看。正确做法是先跑一到两周的面板,把基线摸清楚,再定阈值。

第二条,监控自身的资源要单独规划。Prometheus、日志采集器、trace 采集器这些组件,如果和业务混跑在同一批节点上,大促的时候它们和业务抢资源,可能导致监控数据大面积缺失——而这恰恰是你最需要监控的时候。条件允许的话,给监控栈单独划节点,或者至少给足资源请求和限制。

第三条,面板不要一次性堆满。我第一版面板做了二十多个图,同事打开之后第一句话是"这看哪个"。后来砍到两屏、十个图以内,反而人人都能用。删图的判断标准就是前面说的:这张图能不能驱动一个具体动作。

第四条,每个图都要能回答"谁该看"。运维看的图、开发看的图、管理看的图,关注点完全不同。运维关心实例和资源,开发关心路由和错误详情,管理关心 SLO 达成率。混在一张面板上,谁都觉得不好用。我现在是按角色分面板,公共部分抽成一个可复用的 row,各自的面板引用它。

第五条,定期做一次"断网演练"。就是把某条数据链路手动掐断,看告警会不会响、面板上会不会出现明显的异常提示。这个演练暴露过一个问题:指标链路断了,面板上只是曲线停止更新,看起来和"流量归零"一模一样,值班的人根本分不清。后来我给所有关键面板加了一条"数据新鲜度"的检查,数据超过一定时间没更新就显示告警色,这个改进非常值。

6.3 后续可以继续扩展的方向

这套链路跑通之后,还有几个方向值得往下走,我按投入产出比排了序。

第一是把 SLO 真正落到面板上。现在面板上是经验和阈值,下一步可以定义明确的 SLO(比如"月度可用性 99.9%""P99 延迟低于 500ms"),然后用错误预算的方式呈现。错误预算消耗速度这个指标特别适合放在总览的最上面,它直接回答了"现在这个月还剩多少余量可以折腾"。

第二是把网关指标和上游指标打通。现在两侧是割裂的,网关看到上游慢,要切到另一个系统去看上游。理想状态是在一张面板上,上半部分是网关视角,下半部分是上游视角,时间轴对齐。这个需要两侧的标签体系统一,工作量不小,但一旦做成,排障效率会再上一个台阶。

第三是把 AI 网关相关的统计维度补上。如果网关承担了大模型请求的代理,那 token 消耗、请求时长分布、流式响应的首字延迟这些都会成为新的关键指标。这部分的具体指标名各版本差异较大,需要去实际的指标端点里确认,我这里就不写死了,思路是一样的:先量基数,再定面板,最后配告警。

第四是告警的降噪。现在四类告警还是偏"单点触发",故障时会同时响很多条。下一步想做告警的聚合和抑制,比如网关整体不可用时,抑制掉单个路由的延迟告警,只保留一条根因告警。这个用告警系统的抑制规则能实现,关键是把依赖关系理清楚。

我在实际使用中最大的体会是,可观测性这件事,搭起来只是开始,真正产生价值是在你养成"出事先看面板"的习惯之后。工具做得再好,如果排障的时候还是凭感觉猜,那这些图就只是墙上的装饰。反过来,哪怕只有三个图,只要团队形成了"看数据说话"的习惯,排障效率的提升是立竿见影的。所以如果让我给一个建议,我会说:别追求一步到位把四层全搭完,先把指标和面板跑起来,让团队用两周,用出感觉了再往下补日志和链路,这个顺序比什么都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询