概述

凌晨两点,支付系统告警炸了。看了半天日志发现不是下游服务挂了,而是 API 网关的连接池打满了——请求堆在网关这一层根本没转发出去。但你的监控大盘上只有 CPU 和内存曲线,压根看不到网关层的延迟、连接数和错误率。

这不是个例。我见过太多团队把 API 网关当"高级 Nginx"用,监控只看 nginx_active_connections,出事了才去翻日志。问题是网关是所有流量的咽喉,一旦这层出问题,影响面是全局的。

这篇文章讲清楚三件事:Kong、APISIX、Envoy 各自暴露哪些指标、怎么用 Prometheus 采集、告警规则怎么设计才能在故障扩散前收到通知。不是泛泛而谈的"最佳实践",是我在生产环境踩过坑后整理出来的方案。

为什么 API 网关需要专门的监控

你可能会想:网关后面已经有服务监控了,为什么还要单独监控网关?

因为网关和后端服务看到的世界不一样。举个例子:

维度网关视角后端服务视角
请求延迟包含路由匹配、插件执行、上游转发全链路只看到自己处理那段
错误来源可能是路由不存在、插件拦截、上游超时、连接池满只知道返回了 500
连接状态能看到与上游的连接池使用率只知道自己收了多少请求
限流触发网关层限流命中了哪些规则服务端可能根本不知道被限了

简单说,网关是流量高速公路的收费站。收费站堵了,后面所有车都堵。你不在收费站装监控,等问题传导到后端服务再排查,黄花菜都凉了。

三大主流网关的指标体系对比

Kong 的指标暴露

Kong 通过 prometheus 插件暴露指标,启用方式很简单:

# 启用 Prometheus 插件(全局级别)
curl -X POST http://kong-admin:8001/plugins \
  --data "name=prometheus" \
  --data "config.per_consumer=true" \
  --data "config.status_code_metrics=true" \
  --data "config.latency_metrics=true" \
  --data "config.upstream_health_metrics=true"

启用后访问 http://kong-proxy:8001/metrics 就能拿到 Prometheus 格式的指标。Kong 的核心指标分四类:

指标名类型含义典型告警场景
kong_http_requests_totalCounter请求总数(按 service/route/status 标签)错误率突增
kong_latency_ms_bucketHistogram延迟分布(含 Kong 处理时间和上游响应时间)P99 延迟超阈值
kong_upstream_target_healthGauge上游健康状态(HEALTHY/UNHEALTHY)上游节点不健康
kong_data_plane_versionGauge数据面版本号版本不一致

其中延迟指标最关键。Kong 把延迟拆成三段:kong_latency 是 Kong 自身处理耗时(路由匹配、插件执行),upstream_latency 是上游服务响应时间,total_latency 是两者之和。分开看才能判断瓶颈在网关还是在后端。

一个踩坑经验per_consumer=true 会导致指标基数爆炸。如果你的 consumer 数量超过 1000 个,Prometheus 存储压力会急剧上升。生产环境建议先关掉这个选项,需要时按需开启。

APISIX 的指标暴露

APISIX 用 prometheus 插件,配置方式跟 Kong 类似但底层完全不同:

# APISIX prometheus 插件配置
plugins:
  - prometheus

# 通过 Admin API 启用
# 全局路由级别
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
  -H 'X-API-KEY: your-key' -X PUT -d '
{
  "uri": "/api/*",
  "plugins": {
    "prometheus": {
      "prefer_name": true
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "127.0.0.1:8080": 1
    }
  }
}'

APISIX 默认在 http://apisix-ip:9091/metrics 暴露指标,和 Kong 的区别在于 APISIX 用 etcd 做配置中心,配置变更是推送模式(watch),延迟更低。核心指标:

指标名类型含义
apisix_http_statusCounterHTTP 状态码计数(按 route/service/consumer)
apisix_bandwidthCounter上下行带宽
apisix_http_latency_bucketHistogram延迟分布
apisix_node_infoGauge节点信息(版本、启动时间)

APISIX 的一个优势是 prefer_name: true 选项,用路由名称而不是 UUID 作为标签,查询时更直观。Kong 默认用 service ID,PromQL 写出来全是 service="4e3f2a1b-..." 这种鬼东西,排障时还得去查表。

踩坑:APISIX 的 prometheus 插件不支持 per-consumer 粒度的延迟分布。如果你的业务需要按消费者分析延迟,得自己在日志层做。别等出事了才发现查不到。

Envoy 的指标暴露

Envoy 是三大网关里指标体系最丰富的,但也是最复杂的。Envoy 暴露的指标有几百个,新手容易迷失。实际生产中需要关注的核心指标:

指标名类型含义
envoy_cluster_upstream_rq_totalCounter上游请求总数
envoy_cluster_upstream_rq_2xx/4xx/5xxCounter按状态码分类的请求计数
envoy_cluster_upstream_rq_time_bucketHistogram上游响应延迟分布
envoy_cluster_circuit_breakers_default_cx_pool_openGauge连接池断路器是否打开
envoy_listener_downstream_cx_totalCounter下游连接总数
envoy_server_liveGauge服务器存活状态(1=alive)

Envoy 的指标通过 admin 接口(默认 http://envoy-ip:9901/stats)或 stats sink(Prometheus 格式 http://envoy-ip:9901/stats/prometheus)暴露。

配置 stats sink:

# Envoy 配置 - 启用 Prometheus stats sink
stats_sinks:
  - name: envoy.stat_sinks.statsd
    typed_config:
      "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink
      address:
        socket_address:
          address: 127.0.0.1
          port_value: 9125
      prefix: envoy.

# 或者用 Prometheus 格式(推荐)
admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

Envoy 指标的坑在于基数管理。Envoy 默认给每个 cluster、每个 endpoint 都打标签,如果你的路由数量多(比如几百个 API 路径),指标基数会爆炸。解决办法是用 stats_config 过滤掉不需要的标签:

stats_config:
  stats_tags:
    - tag_name: cluster_name
      regex: "^cluster\\.((.+?)\\.)"
  stats_matcher:
    inclusion_list:
      patterns:
        - prefix: "cluster."
        - prefix: "listener."
        - prefix: "server."
        - prefix: "http."

这样只保留 cluster、listener、server、http 相关的指标,砍掉一堆用不上的内部统计。

Prometheus 采集配置

Kong 采集

# prometheus.yml - Kong 采集配置
scrape_configs:
  - job_name: 'kong'
    metrics_path: /metrics
    static_configs:
      - targets:
          - 'kong-gateway:8001'  # Kong Admin API 端口
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: 'kong-prod-01'

Kong 3.x 之后 Admin API 和 Proxy 可以分端口,确保 metrics_path 指向正确的端口。Kong 2.x 的 /metrics 端点默认在 Admin API(8001)上。

APISIX 采集

# prometheus.yml - APISIX 采集配置
scrape_configs:
  - job_name: 'apisix'
    metrics_path: /apisix/prometheus/metrics
    static_configs:
      - targets:
          - 'apisix:9091'
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: 'apisix-prod-01'

注意 APISIX 的 metrics 端口默认是 9091,路径是 /apisix/prometheus/metrics,不是标准的 /metrics。配错了 Prometheus 会一直报 404。

Envoy 采集

# prometheus.yml - Envoy 采集配置
scrape_configs:
  - job_name: 'envoy'
    metrics_path: /stats/prometheus
    static_configs:
      - targets:
          - 'envoy-proxy:9901'

Envoy 的 admin 端口默认 9901,Prometheus 格式的指标路径是 /stats/prometheus。如果你的 Envoy 跑在 Kubernetes 里,建议用 Pod Monitor 自动发现:

# Kubernetes PodMonitor(kube-prometheus-stack)
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: envoy-gateway
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: envoy-gateway
  podMetricsEndpoints:
    - port: admin
      path: /stats/prometheus
      interval: 15s

核心告警规则设计

这部分是最实用的。我按"故障发现优先级"排了五层告警,从 P0 到 P4,你可以直接抄到自己的告警规则文件里。

第一层:网关存活(P0)

groups:
  - name: gateway-availability
    rules:
      # 网关进程存活
      - alert: GatewayDown
        expr: up{job=~"kong|apisix|envoy"} == 0
        for: 1m
        labels:
          severity: critical
          team: sre
        annotations:
          summary: "网关 {{ $labels.instance }} 不可达"
          description: "Prometheus 已无法采集 {{ $labels.instance }} 的指标,网关可能已宕机"

      # 网关 5xx 错误率(1分钟内超过 5%)
      - alert: GatewayHighErrorRate
        expr: |
          sum(rate(kong_http_requests_total{status=~"5.."}[1m])) by (service)
          /
          sum(rate(kong_http_requests_total[1m])) by (service)
          > 0.05          
        for: 2m
        labels:
          severity: critical
          team: sre
        annotations:
          summary: "Kong 网关 5xx 错误率超 5%"
          description: "服务 {{ $labels.service }} 的 5xx 错误率当前为 {{ $value | humanizePercentage }}"

为什么 for: 2m 而不是立刻告警?因为瞬间毛刺(比如 GC 停顿、连接重置)可能让错误率短暂飙高,1 分钟后自行恢复。2 分钟的持续条件能过滤掉 80% 的噪音。

第二层:延迟异常(P1)

  - name: gateway-latency
    rules:
      # Kong P99 延迟超过 500ms
      - alert: GatewayHighLatency
        expr: |
          histogram_quantile(0.99,
            sum(rate(kong_latency_ms_bucket[5m])) by (service, le)
          ) > 500          
        for: 5m
        labels:
          severity: warning
          team: sre
        annotations:
          summary: "Kong 网关 P99 延迟超 500ms"
          description: "服务 {{ $labels.service }} 的 P99 延迟当前为 {{ $value }}ms"

      # 上游延迟占比过高(Kong 自身延迟 < 总延迟的 20% 说明瓶颈在上游)
      - alert: GatewayUpstreamLatencyDominant
        expr: |
          histogram_quantile(0.95,
            sum(rate(kong_latency_ms_bucket{type="upstream"}[5m])) by (service, le)
          )
          /
          histogram_quantile(0.95,
            sum(rate(kong_latency_ms_bucket{type="total"}[5m])) by (service, le)
          )
          > 0.8          
        for: 10m
        labels:
          severity: info
          team: sre
        annotations:
          summary: "上游延迟占比超 80%,瓶颈在后端服务"
          description: "服务 {{ $labels.service }} 的上游延迟占比 {{ $value | humanizePercentage }},建议排查后端服务"

这条 GatewayUpstreamLatencyDominant 告警是我加的。很多团队只看总延迟,看到延迟高了就去找网关的问题,折腾半天发现是后端慢。这个告警把延迟拆开,上游占比超 80% 就说明瓶颈不在网关,直接把排查方向指向后端。实测能省一半的定位时间。

第三层:上游健康(P2)

  - name: gateway-upstream-health
    rules:
      # 上游节点不健康
      - alert: GatewayUpstreamUnhealthy
        expr: kong_upstream_target_health{state="UNHEALTHY"} == 1
        for: 1m
        labels:
          severity: warning
          team: sre
        annotations:
          summary: "上游目标 {{ $labels.target }} 不健康"
          description: "上游 {{ $labels.upstream }} 的目标 {{ $labels.target }} 当前状态为 UNHEALTHY"

      # Envoy 集群断路器打开
      - alert: EnvoyCircuitBreakerOpen
        expr: envoy_cluster_circuit_breakers_default_cx_pool_open == 1
        for: 30s
        labels:
          severity: critical
          team: sre
        annotations:
          summary: "Envoy 集群 {{ $labels.cluster_name }} 连接池断路器已打开"
          description: "集群连接池已满,新请求被拒绝"

断路器打开意味着上游已经扛不住了。这种情况通常伴随着雪崩效应——一个服务慢了拖垮连接池,导致其他正常服务的请求也连不上。这条告警触发后应该立即检查上游服务状态和连接池配置。

第四层:流量异常(P3)

  - name: gateway-traffic-anomaly
    rules:
      # 请求量骤降(可能路由配置有问题或上游全部挂了)
      - alert: GatewayTrafficDrop
        expr: |
          sum(rate(kong_http_requests_total[5m])) by (service)
          <
          sum(rate(kong_http_requests_total[5m] offset 1h)) by (service) * 0.3          
        for: 10m
        labels:
          severity: warning
          team: sre
        annotations:
          summary: "服务 {{ $labels.service }} 流量下降超 70%"
          description: "当前请求量比 1 小时前下降 {{ $value }}"

      # 请求量突增(可能是被刷了或正常流量高峰)
      - alert: GatewayTrafficSpike
        expr: |
          sum(rate(kong_http_requests_total[5m])) by (service)
          >
          sum(rate(kong_http_requests_total[5m] offset 1h)) by (service) * 3          
        for: 5m
        labels:
          severity: info
          team: sre
        annotations:
          summary: "服务 {{ $labels.service }} 流量增长超 200%"
          description: "当前请求量比 1 小时前增长 {{ $value }} 倍,请确认是否为正常流量高峰"

流量突降比突增更值得警惕。突增可能只是业务高峰,但突降几乎一定出事了——要么路由配置被改坏了,要么上游全挂了。

第五层:资源水位(P4)

  - name: gateway-resource
    rules:
      # Kong 数据面连接数过高
      - alert: KongHighConnections
        expr: |
          sum by (instance) (
            kong_nginx_http_connections_total{state="active"}
          ) > 10000          
        for: 5m
        labels:
          severity: warning
          team: sre
        annotations:
          summary: "Kong 实例 {{ $labels.instance }} 活跃连接数超 10000"
          description: "当前活跃连接数 {{ $value }},可能需要扩容"

      # APISIX etcd 连接异常
      - alert: APISIXEtcdUnreachable
        expr: apisix_etcd_reachable == 0
        for: 30s
        labels:
          severity: critical
          team: sre
        annotations:
          summary: "APISIX 无法连接 etcd"
          description: "APISIX 节点 {{ $labels.instance }} 无法连接 etcd,配置变更将无法同步"

etcd 连不上是个要命的故障。APISIX 所有配置都存在 etcd 里,连不上意味着路由规则无法更新、新配置无法下发。虽然已有的配置还在内存里缓存着,暂时不影响流量转发,但你已经失去了对网关的控制权。

Grafana 仪表盘设计

仪表盘布局原则

一个好的 API 网关监控大盘应该让运维一眼看到三个答案:流量正不正常?延迟高不高?有没有报错?

我建议用从上到下四行布局:

┌─────────────────────────────────────────────┐
│  第 1 行:QPS 总量、错误率、P99 延迟、活跃连接  │  ← 一眼看全局
├─────────────────────────────────────────────┤
│  第 2 行:各服务 QPS 对比 + 错误率对比          │  ← 谁有问题
├─────────────────────────────────────────────┤
│  第 3 行:延迟分布热力图 + 延迟分位数趋势       │  ← 延迟详情
├─────────────────────────────────────────────┤
│  第 4 行:上游健康状态 + 断路器状态 + 连接池    │  ← 底层状态
└─────────────────────────────────────────────┘

核心 Panel 配置

第 1 行:全局概览

# 总 QPS(按网关实例聚合)
sum(rate(kong_http_requests_total[5m]))

# 错误率
sum(rate(kong_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(kong_http_requests_total[5m]))

# P99 延迟
histogram_quantile(0.99,
  sum(rate(kong_latency_ms_bucket[5m])) by (le)
)

# 活跃连接数
sum(kong_nginx_http_connections_total{state="active"})

第 3 行:延迟热力图

Grafana 的 Heatmap panel 很适合展示延迟分布。把 Histogram bucket 配置好,能直观看到延迟从哪个时间点开始漂移:

# 延迟热力图数据源
sum(rate(kong_latency_ms_bucket[5m])) by (le, service)

Heatmap 能让你一眼看到"延迟不是均匀涨上来的,而是有一批请求突然变慢了"——这种模式用折线图根本看不出来,但用热力图特别明显。我第一次用热力图排查延迟问题就发现了有个 service 的 P50 正常但尾部延迟在每天 14:00 定期飙高,最后查出是后端服务在跑定时任务占满了线程池。

第 4 行:上游健康表格

用 Table panel 展示每个上游目标的健康状态:

kong_upstream_target_health

加一个 Value Mappings,把 1 映射成红色 UNHEALTHY,0 映射成绿色 HEALTHY,比看数字直观得多。

三种网关监控的对比与选型建议

维度KongAPISIXEnvoy
指标暴露方式Prometheus 插件Prometheus 插件Admin API / Stats Sink
指标丰富度中(够用)中(够用)高(几百个指标)
延迟分维度Kong + 上游 + 总计总计为主非常细(按 cluster/route)
标签可读性默认 UUID,需额外配置prefer_name 用路由名需手动配置 stats_filter
连接池监控Nginx 层指标Nginx 层指标内置断路器指标
配置存储PostgreSQL / CassandraetcdxDS(Istio/Kubernetes)
指标基数风险per_consumer 开启后爆炸路由多时中等默认就高,必须过滤

选型建议:

  • Kong:团队偏保守、已有 Kong 运维经验、指标需求不复杂。Kong 的插件生态成熟,但 per_consumer 粒度监控要做好基数管理
  • APISIX:追求动态配置、对配置变更延迟敏感。etcd 的 watch 机制让配置秒级生效,但 etcd 本身也要做好监控和 HA
  • Envoy:深度使用 Service Mesh(Istio)、需要精细化的流量观测。Envoy 指标体系最完整但学习曲线最陡,适合有专职 SRE 的团队

生产级踩坑实录

坑 1:Kong 指标基数爆炸

去年我们有个业务方接入了 2000+ 个 consumer,网关开了 per_consumer=true。结果 Prometheus 存储从每天 5GB 涨到 47GB,查询也慢了。因为每个 consumer × 每个 route × 每个 status code 都会产生一个时序,2000 × 50 × 5 = 50 万条时序。

解决:关掉 per_consumer,需要 consumer 级别分析时从网关日志里查。如果非要细粒度指标,用 consumer_label_filter 只保留 Top N 的 consumer。

坑 2:APISIX 配置变更没同步到监控

APISIX 的 Prometheus 插件是在路由级别配置的。有一次同事加了个新路由但忘了挂 prometheus 插件,新路由的流量完全没被监控到,直到有用户反馈这个 API 偶尔超时才发现。

解决:在 CI/CD 里加了一道检查——所有新路由必须包含 prometheus 插件配置,否则流水线拦截。另外在 Grafana 上加了一个"未监控路由检测"面板,定期对比 APISIX Admin API 里的路由列表和 Prometheus 里有指标的路由列表,差集就是漏配的。

坑 3:Envoy stats endpoint 被 OOM kill

Envoy 默认暴露所有统计指标,包括大量内部计数器。路由数量多了以后 /stats/prometheus 的响应体能有几十 MB,Prometheus 每次 scrape 都让 Envoy 内存涨一截,最终被 OOM kill。

解决:用 stats_matcher 只暴露需要的指标前缀(cluster、listener、http、server),响应体从 47MB 降到 3MB。同时调大 scrape interval 从 15s 到 30s,降低采集频率。

坑 4:告警阈值一刀切

我们最初给所有服务都设了 P99 > 500ms 的告警。结果有些批量接口正常响应就要 2-3 秒,天天告警。SRE 习惯了告警噪音后,真正出事的时候反而忽略了。

解决:按服务类型分组设阈值。在线交易类 P99 > 200ms 告警,查询类 P99 > 800ms 告警,批量类 P99 > 5s 才告警。在 Prometheus 里用 service label 做条件分支:

# 按服务类型设置不同延迟阈值
- alert: GatewayHighLatencyOnline
  expr: |
    histogram_quantile(0.99,
      sum(rate(kong_latency_ms_bucket{service=~"order-.*|payment-.*"}[5m])) by (service, le)
    ) > 200
    and on(service)
    kong_latency_ms_count{service=~"order-.*|payment-.*"} > 0    
  for: 5m
  labels:
    severity: warning

- alert: GatewayHighLatencyBatch
  expr: |
    histogram_quantile(0.99,
      sum(rate(kong_latency_ms_bucket{service=~"report-.*|export-.*"}[5m])) by (service, le)
    ) > 5000
    and on(service)
    kong_latency_ms_count{service=~"report-.*|export-.*"} > 0    
  for: 10m
  labels:
    severity: warning

坑 5:APISIX etcd 单点故障导致配置丢失

APISIX 所有配置依赖 etcd。有一次 etcd 集群一个节点磁盘满了,APISIX 虽然能继续转发流量(内存里有缓存的配置),但所有新配置都无法下发。更糟糕的是 Prometheus 的 apisix_etcd_reachable 指标显示正常——因为 APISIX 连的是另一个 etcd 节点,那个节点虽然活着但数据已经不一致了。

解决:除了监控 apisix_etcd_reachable,还要监控 etcd 集群本身的健康状态(etcd_server_has_leaderetcd_mvcc_db_total_size_in_bytesetcd_server_proposals_failed_total),从 etcd 侧做独立检查。别只信网关报告的"我能连上 etcd"。

自动化巡检脚本

光有被动告警不够,主动巡检能提前发现隐患。这是一个检查网关关键指标的脚本:

#!/usr/bin/env python3
"""API 网关健康巡检脚本
通过 Prometheus API 查询网关关键指标,输出巡检报告。
"""
import requests
import sys
from datetime import datetime

PROMETHEUS_URL = "http://prometheus:9090"

# 巡检项配置:指标名 -> 查询语句 -> 阈值 -> 告警级别
CHECKS = [
    {
        "name": "网关存活",
        "query": 'up{job=~"kong|apisix|envoy"}',
        "expect": 1,
        "severity": "P0"
    },
    {
        "name": "5xx 错误率",
        "query": """
            sum(rate(kong_http_requests_total{status=~"5.."}[5m]))
            / sum(rate(kong_http_requests_total[5m]))
        """,
        "threshold": 0.01,
        "severity": "P1",
        "compare": "gt"
    },
    {
        "name": "P99 延迟",
        "query": """
            histogram_quantile(0.99,
                sum(rate(kong_latency_ms_bucket[5m])) by (le)
            )
        """,
        "threshold": 500,
        "severity": "P2",
        "compare": "gt"
    },
    {
        "name": "上游不健康节点数",
        "query": 'count(kong_upstream_target_health{state="UNHEALTHY"} == 1)',
        "threshold": 0,
        "severity": "P2",
        "compare": "gt"
    },
    {
        "name": "活跃连接数",
        "query": 'sum(kong_nginx_http_connections_total{state="active"})',
        "threshold": 8000,
        "severity": "P3",
        "compare": "gt"
    },
]


def query_prometheus(query):
    """执行 PromQL 查询"""
    resp = requests.get(
        f"{PROMETHEUS_URL}/api/v1/query",
        params={"query": query},
        timeout=10
    )
    resp.raise_for_status()
    data = resp.json()
    if data["status"] != "success":
        raise ValueError(f"查询失败: {data}")
    return data["data"]["result"]


def run_checks():
    """执行所有巡检项"""
    print(f"\n{'='*60}")
    print(f"API 网关健康巡检 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"{'='*60}\n")

    issues = []
    for check in CHECKS:
        try:
            results = query_prometheus(check["query"])
            if not results:
                print(f"[SKIP] {check['name']}: 无数据")
                continue

            if check.get("expect") is not None:
                value = float(results[0]["value"][1])
                status = "OK" if value == check["expect"] else "FAIL"
                print(f"[{status}] {check['name']}: {value}")
                if status == "FAIL":
                    issues.append((check["severity"], check["name"], value))
            elif check.get("threshold") is not None:
                value = float(results[0]["value"][1])
                if check.get("compare") == "gt":
                    exceeded = value > check["threshold"]
                else:
                    exceeded = value < check["threshold"]
                status = "OK" if not exceeded else "WARN"
                print(f"[{status}] {check['name']}: {value:.2f} (阈值: {check['threshold']})")
                if exceeded:
                    issues.append((check["severity"], check["name"], value))
        except Exception as e:
            print(f"[ERR] {check['name']}: {e}")
            issues.append(("P0", check["name"], str(e)))

    print(f"\n{'='*60}")
    if issues:
        print(f"发现 {len(issues)} 个问题:")
        for sev, name, val in issues:
            print(f"  [{sev}] {name}: {val}")
    else:
        print("所有巡检项正常")
    print(f"{'='*60}\n")

    return len(issues) == 0


if __name__ == "__main__":
    ok = run_checks()
    sys.exit(0 if ok else 1)

这个脚本挂在 cron 里每小时跑一次,结果输出到运维群。比起被动等告警,主动巡检能在指标"开始变差但还没触发告警阈值"的时候就发现问题。比如活跃连接数从 3000 缓慢涨到 7000,还没到 10000 的告警阈值,但巡检脚本会标黄提示你"连接数在涨,该看看了"。

总结

API 网关监控不是"装个 Prometheus 插件就完了"。核心经验有三条:

指标采集要分层。别只看总量指标,把延迟拆成网关处理时间和上游响应时间,把错误率按状态码分类,把连接数按状态区分。分开看才能快速定位瓶颈在网关还是在后端。实测中,把上游延迟占比作为一个独立告警条件,能把故障定位时间砍掉一半——因为它直接告诉你"别在网关上浪费时间了,去查后端"。

告警阈值要分服务。不同类型的服务延迟特征天差地别,在线交易和批量导出用同一个阈值只会制造噪音。我见过最离谱的配置是所有服务统一 P99 > 100ms 告警,结果一个报表导出接口天天告警,SRE 最后把告警群设成了免打扰——然后真正出事的时候没人看到。

配置存储组件必须独立监控。不管你用 Kong+PostgreSQL 还是 APISIX+etcd,存储组件挂了网关不会立刻死,但你会失去对网关的控制权。别只信网关报告的"我能连上存储",要从存储侧独立验证。etcd 的 server_has_leader 和 PostgreSQL 的 pg_is_in_recovery 这类指标比网关侧的可达性检查可靠得多。

最后提一句仪表盘设计。好的监控大盘不是堆图表数量,是让你在 5 秒内回答"系统正不正常"。四行布局——全局概览、服务对比、延迟详情、底层状态——是我反复迭代后的方案,比最初堆了二十几个 panel 的版本好用得多。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Kong Prometheus Plugin 官方文档 — Kong Inc., Prometheus 指标暴露插件的使用说明与配置参数
  2. Apache APISIX Prometheus Plugin — Apache APISIX, Prometheus 插件文档及指标说明
  3. Envoy Statistics 文档 — Envoy Proxy, 统计指标体系概览与配置方法
  4. 终极指南:Kong API网关性能监控的6个核心指标与告警实战 — CSDN, Kong 监控核心指标分析与告警规则设计
  5. Envoy Gateway 监控与运维:10个关键指标和最佳实践 — CSDN, Envoy Gateway 监控指标详解
  6. Prometheus 告警实战:写 alerting rules、用 Alertmanager 做路由分组与抑制 — CSDN, Prometheus 告警规则编写与 Alertmanager 路由配置