01 Nginx Ingress 网关简介
在 Kubernetes 集群中,我们通常使用 “Nginx Ingress” 实现集群南北向流量的代理转发,Nginx Ingress 基于集群内 Ingress 资源配置生成具体的路由规则。Ingress 资源负责对外公开服务的管理,一般这类服务通过 HTTP 协议进行访问。通过 Nginx Ingress + Ingress 资源可以实现以下场景:
一、通过 Nginx Ingress 将来自客户端的全部流量转发给单一 Service。
图:Nginx Ingress 工作模式介绍
二、通过 Nginx Ingress 实现更复杂的路由转发规则,将来自单一绑定 IP 地址的所有流量根据 URL 请求路径前缀转发给不同的 Service。
图:基于 URL 请求路径的转发
三、根据 HTTP 请求头部携带的 Host 字段——通常由访问的域名决定,将来自单一绑定 IP 地址的流量分发给不同后端 Service,实现基于名称的虚拟主机(Name-based Virtual Hosting)能力。
图:基于 Host 请求头的转发
通常,围绕 Nginx Ingress 网关监控场景,我们通常会关注两类核心指标数据:
1.工作负载资源
即 Nginx Ingress Controller Pod 的负载情况,当 CPU 、内存等资源水位处于饱和或过载,会导致集群对外服务不稳定。针对“工作负载监控”,一般建议关注 “USE” 指标,即:使用率(Utilization)、饱和度(Saturation)、错误率(Errors)。对此,阿里云 Prometheus 监控提供了预置性能监控大盘,可参考《工作负载性能监控组件接入》[1]完成数据采集与大盘创建。
2.入口请求流量
包括集群范围全局的流量、某个 Ingress 规则转发的流量、某个 Service 的流量,以及对应的成功率/错误率、延迟,乃至请求来源的地址、设备等信息的分析与统计。针对“入口请求流量监控”,一般建议关注 “RED” 指标,即:请求速率(Rate)、请求失败数(Errors)、请求延迟(Duration)。可通过本文最佳实践实现接入。
02 Nginx Ingress 网关监控实现方式
基于 Exporter 指标
Kubernetes 基于开源 Nginx 实现的 Nginx Ingress 发行版一大特色是其每个进程都扮演着 Exporter 角色,实现遵循 Prometheus 协议格式的自监控指标,如:
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="200"} 2.401964e+06
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="304"} 111
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="308"} 553545
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="404"} 55
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="499"} 2
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontend",status="500"} 64
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontendproxy",status="200"} 59599
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontendproxy",status="304"} 15
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontendproxy",status="308"} 15709
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="my.otel-demo.com",ingress="my-otel-demo",method="GET",namespace="default",path="/",service="my-otel-demo-frontendproxy",status="403"} 235
nginx_ingress_controller_requests{canary="",controller_class="k8s.io/ingress-nginx",controller_namespace="kube-system",controller_pod="nginx-ingress-controller-6fdbbc5856-pcxkz",host="e-commerce.
使用开源或阿里云 Prometheus Agent 配合服务发现策略即可完成指标抓取与上报,通过 PromQL 实现分析、告警配置,或通过 Grafana 实现指标数据可视化展现。但这种监控实现方式在生产实践中存在不少问题。
问题 1:暴露太多不实用的 Histogram 指标
对生产或测试集群中的 Nginx Ingress 进行一次抓取,会发现它所展现的指标清单中,Histogram 类型指标占据非常多数量,Histogram 指标一般以 <metric_name>_bucket命名,配合 <metric_name>_count 和 <metric_name>_count 一起使用。并且,其中包含常见分析不会使用的指标, 如:
- nginx_ingress_controller_request_size_bucket:对每个请求体大小的分桶采样;
- nginx_ingress_controller_bytes_sent_bucket:对每个响应体大小的分桶采样。
默认情况下,如果不在 Prometheus 的 metric_relabel_configs 采集配置中执行 drop 操作,这些指标都会被抓取、上报,占用大量带宽与存储资源。
问题 2:Pull 模式拉取太多不活跃的时间线
当第一个问题遇到 Prometheus Agent 的 Pull 模式,情况变得更加糟糕,如果某个访问频率不那么高的微服务,历史只要发生过一次请求,那么与它有关的所有时间线会在 Nginx Ingress 暴露的指标清单中一直出现。在每个抓取周期中,被不停采集、上报,资源浪费加剧。
这个现象背后的本质问题是一个计数器类型指标在观察周期内无变化时,如何避免上报?我们发现通过 Pull 模式很难落地一个好的解决方案,后文会介绍新的思路。
问题 3:Ingress Path 不可扩展、下钻
一般体现 HTTP 流量的监控指标,URL Path 是个很难处理的对象,如果直接将每个请求的 URL Path 加入到指标标签作为分析用途,将产生可怕的“维度爆炸