云原生网关这一层的可观测性,最容易出现的一种情况是:网关本身跑得好好的,业务侧却反馈"接口偶尔很慢",你去查日志,日志里只有一行 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 消耗、请求时长分布、流式响应的首字延迟这些都会成为新的关键指标。这部分的具体指标名各版本差异较大,需要去实际的指标端点里确认,我这里就不写死了,思路是一样的:先量基数,再定面板,最后配告警。
第四是告警的降噪。现在四类告警还是偏"单点触发",故障时会同时响很多条。下一步想做告警的聚合和抑制,比如网关整体不可用时,抑制掉单个路由的延迟告警,只保留一条根因告警。这个用告警系统的抑制规则能实现,关键是把依赖关系理清楚。
我在实际使用中最大的体会是,可观测性这件事,搭起来只是开始,真正产生价值是在你养成"出事先看面板"的习惯之后。工具做得再好,如果排障的时候还是凭感觉猜,那这些图就只是墙上的装饰。反过来,哪怕只有三个图,只要团队形成了"看数据说话"的习惯,排障效率的提升是立竿见影的。所以如果让我给一个建议,我会说:别追求一步到位把四层全搭完,先把指标和面板跑起来,让团队用两周,用出感觉了再往下补日志和链路,这个顺序比什么都重要。