在前面的文章中,我们已经讨论了 Service 如何在四层完成负载均衡。但现实中,绝大多数面向用户的流量都是 HTTP/HTTPS,需要基于域名、路径、请求头做路由。Kubernetes 从 1.1 版本引入了 Ingress 资源来解决这个问题,然而围绕它的争议从未停止:注解泛滥、语义模糊、跨实现不兼容。
本文将从 Ingress 资源的设计出发,逐一拆解 NGINX、Traefik、Envoy、HAProxy 四大流派的架构差异,深入它们的热更新机制与 TLS 终止策略,最后解释为什么 Ingress API 注定要被 Gateway API 取代。
一、从 Service 到 Ingress:为什么需要七层入口
四层暴露的局限
通过 NodePort 或 LoadBalancer
类型的
Service,可以将集群内部的服务暴露到外部。但这两种方式有明显的局限性:
- NodePort:端口范围有限(默认 30000-32767),每个服务占用一个端口,无法共享 80/443。
- LoadBalancer:每个 Service 创建一个云负载均衡器,成本高昂,且只能做四层转发。
当你有 50 个微服务都需要对外暴露 HTTP 接口时,不可能为每个服务分配一个独立的负载均衡器。你需要一个统一的七层入口,根据 Host 和 Path 将请求分发到不同的后端。
Ingress 的基本模型
Ingress 资源定义了一组从集群外部到集群内部 Service 的 HTTP(S) 路由规则:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /web
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80这个资源本身只是一段声明,它不会自己工作。真正处理流量的是 Ingress Controller –一个运行在集群中的反向代理,持续监听 Ingress 资源的变化,并将其转换为具体的代理配置。
Ingress 资源与 Ingress Controller 的关系
| 概念 | 角色 | 类比 |
|---|---|---|
| Ingress 资源 | 声明式路由规则 | DNS 记录 |
| Ingress Controller | 实际执行路由的代理 | DNS 服务器 |
| IngressClass | 指定使用哪个 Controller | DNS 提供商 |
一个集群可以同时运行多个 Ingress Controller,通过
IngressClass 区分。
二、Ingress 资源的设计与局限
最小公约数问题
Ingress API 试图用一个通用的资源定义覆盖所有七层路由需求,这导致它只能取各个代理实现的”最小公约数”。核心 spec 只支持:
- 基于 Host 的路由
- 基于 Path 的路由(Exact / Prefix)
- TLS 终止
- 默认后端
而实际生产中常见的需求,如速率限制、请求重写、会话保持、跨域配置、流量镜像、金丝雀发布,全部依赖 annotations。
注解泛滥
以 NGINX Ingress Controller 为例,其支持的 annotations 超过 100 个:
metadata:
annotations:
# 速率限制
nginx.ingress.kubernetes.io/limit-rps: "100"
# 请求体大小
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# 会话保持
nginx.ingress.kubernetes.io/affinity: "cookie"
# 自定义请求头
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Request-ID: $req_id";
# 跨域
nginx.ingress.kubernetes.io/enable-cors: "true"
# 超时
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"这带来了几个严重问题:不可移植(迁移到 Traefik 时所有
annotations
要重写)、无类型校验(map[string]string,拼写错误不会有提示)、安全风险(configuration-snippet
允许注入任意 NGINX 指令)、版本管理困难(废弃的 annotation
可能静默失效)。
pathType 的歧义
即使是核心 spec 中的
pathType,不同实现的行为也不一致。ImplementationSpecific
本质上是承认了标准化的失败:同一段 Ingress 定义在 NGINX 和
Traefik 上可能产生不同的路由行为。
缺失的功能
Ingress v1 在设计上还缺少以下关键能力:请求头路由、流量拆分(金丝雀)、TCP/UDP 路由、跨命名空间后端、请求/响应头修改、委托路由。这些缺失迫使每个 Controller 都发展出自己的 CRD 来弥补,进一步加剧了生态碎片化。
三、主流 Ingress Controller 架构对比
NGINX Ingress Controller
NGINX Ingress Controller 有两个主要版本,容易混淆:
| 特性 | kubernetes/ingress-nginx(社区版) | nginxinc/kubernetes-ingress(官方版) |
|---|---|---|
| 维护者 | Kubernetes 社区 | NGINX Inc. (F5) |
| 数据面 | OpenResty (NGINX + Lua) | 原生 NGINX / NGINX Plus |
| 配置方式 | Go 模板生成 nginx.conf + Lua 动态路由 | Go 模板生成 nginx.conf |
| 动态上游 | Lua 模块 balancer_by_lua 避免 reload |
NGINX Plus API (商业版) |
| CRD | 无(纯 annotations) | VirtualServer / VirtualServerRoute |
| 商业支持 | 无 | NGINX Plus 订阅 |
社区版的架构核心是一个 Go 控制器进程:
kube-apiserver
|
| watch Ingress/Secret/Service/Endpoints
v
ingress-controller (Go)
|
| 1. 内存中构建 nginx.conf 模板
| 2. 写入 /etc/nginx/nginx.conf
| 3. 发送 SIGHUP 或执行 nginx -s reload
v
nginx worker processes
|
| 处理 HTTP/HTTPS 请求
v
upstream pods
社区版通过 Lua 模块实现了部分”免 reload”能力。当只有
Endpoints 变化时,balancer_by_lua
可以动态更新上游列表,无需触发 NGINX reload。但当 Ingress
规则本身变化(如新增域名、修改路径)时,仍然必须执行全量
reload。
Traefik
Traefik 采用了完全不同的架构思路。它是一个用 Go 编写的原生反向代理,不依赖外部进程(不像 NGINX Ingress 那样 Go 控制器 + NGINX 数据面的双进程模型):
kube-apiserver
|
| watch Ingress/IngressRoute/Service/Endpoints/Secret
v
traefik (single Go binary)
|
| Provider 机制:Kubernetes / Docker / File / Consul ...
| 路由规则直接更新内存中的 router 表
|
| EntryPoint -> Router -> Middleware Chain -> Service -> LoadBalancer
v
upstream pods
Traefik 的核心优势:
1. 自动服务发现
Traefik 支持多种 Provider,可以同时从 Kubernetes、Docker、Consul 等来源发现服务,无需手动配置:
# Traefik 静态配置
providers:
kubernetesIngress:
ingressClass: traefik
kubernetesCRD: {}2. Middleware 链
Traefik 使用 IngressRoute CRD 和 Middleware 资源,将横切关注点显式建模为可复用的组件:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
spec:
rateLimit:
average: 100
burst: 50
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-prefix
spec:
stripPrefix:
prefixes:
- /api
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: app-route
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.example.com`) && PathPrefix(`/api`)
kind: Rule
middlewares:
- name: rate-limit
- name: strip-prefix
services:
- name: api-svc
port: 80
weight: 100
tls:
certResolver: letsencrypt3. 内存热更新
由于路由表和配置完全在 Go 进程内存中管理,Traefik 的配置更新不需要任何进程重启或 reload 操作。路由变更是原子性的内存操作,不会中断任何现有连接。
Envoy-based:Contour 与 Emissary-ingress
Envoy Proxy 本身不是 Ingress Controller,而是一个高性能的 L7 代理。多个项目基于 Envoy 构建了 Kubernetes Ingress Controller,其中最具代表性的是 Contour 和 Emissary-ingress(原 Ambassador)。
Contour 的架构:
kube-apiserver
|
| watch Ingress/HTTPProxy/Secret/Service/Endpoints
v
contour (Go, control plane)
|
| 通过 xDS gRPC API 下发配置
| (RDS / CDS / EDS / SDS)
v
envoy (data plane)
|
| 基于 xDS 配置处理请求
v
upstream pods
xDS 是 Envoy 的动态配置协议族,包含多个发现服务:
| xDS | 全称 | 作用 |
|---|---|---|
| RDS | Route Discovery Service | 路由配置 |
| CDS | Cluster Discovery Service | 上游集群(对应 K8s Service) |
| EDS | Endpoint Discovery Service | 端点列表(对应 Pod IP) |
| SDS | Secret Discovery Service | TLS 证书 |
| LDS | Listener Discovery Service | 监听器配置 |
这种架构的核心优势在于 控制面与数据面完全分离。Envoy 通过 gRPC 流式接收配置更新,不需要读取文件、重启进程或 reload:
// Contour 中 xDS 服务器的简化逻辑
func (s *Server) StreamRoutes(stream RouteDiscoveryService_StreamRoutesServer) error {
for {
select {
case <-s.routeUpdated:
snapshot := s.cache.GetSnapshot()
if err := stream.Send(snapshot.Routes); err != nil {
return err
}
case <-stream.Context().Done():
return nil
}
}
}Contour 提供了 HTTPProxy CRD,解决了 Ingress
的多个痛点:
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: app-proxy
spec:
virtualhost:
fqdn: app.example.com
tls:
secretName: app-tls
includes:
- name: api-routes
namespace: api-team
conditions:
- prefix: /api
routes:
- conditions:
- prefix: /web
services:
- name: web-svc
port: 80
weight: 90
- name: web-svc-canary
port: 80
weight: 10注意 includes 字段:它允许将
/api 前缀的路由权限委托给 api-team
命名空间,这是 Ingress
资源无法实现的跨命名空间路由委托。
Emissary-ingress 的定位更偏向 API
Gateway,除了基本路由外,还内置了认证、速率限制、流量镜像等功能。它同样基于
Envoy 数据面,使用自己的 CRD
体系(Mapping、Host、TLSContext)。
HAProxy Ingress
HAProxy Ingress Controller 基于 HAProxy 构建,HAProxy 以高性能和低延迟著称,特别适合对延迟敏感的场景:
kube-apiserver
|
| watch Ingress/Secret/Service/Endpoints/ConfigMap
v
haproxy-ingress (Go controller)
|
| 1. 生成 haproxy.cfg
| 2. 通过 Master Worker 模式或 SO_REUSEPORT 平滑重载
v
haproxy (data plane)
|
| 处理 HTTP/HTTPS 请求
v
upstream pods
HAProxy Ingress 的重载机制比 NGINX 更优雅。在
seamless-reload 模式下,新 HAProxy 进程通过
SO_REUSEPORT 绑定同一端口,旧进程通过 Unix
socket
将已有连接的文件描述符传递给新进程后优雅退出,连接无感知切换。而
NGINX reload 的旧 worker 等待超时后会强制关闭,长连接和
WebSocket 可能被中断。
四大方案选型参考
| 维度 | NGINX Ingress | Traefik | Contour (Envoy) | HAProxy Ingress |
|---|---|---|---|---|
| 学习曲线 | 低(NGINX 生态成熟) | 低(文档友好) | 中(需理解 xDS) | 中 |
| 性能(RPS) | 高 | 中高 | 高 | 极高 |
| 延迟(P99) | 低 | 中 | 低 | 极低 |
| 热更新 | 部分(Lua) | 完全 | 完全(xDS) | 近乎完全 |
| 社区活跃度 | 极高 | 高 | 高 | 中 |
| Gateway API | 支持 | 支持 | 支持 | 部分支持 |
| 适用场景 | 通用、生态丰富 | 自动发现、多源 | 大规模、服务网格 | 极致性能 |
四、Ingress Controller 的内部实现
控制循环核心
所有 Ingress Controller 都遵循相同的控制循环模式,差异在于配置生成和数据面更新的方式:
Watch Phase Reconcile Phase Apply Phase
+---------+ +--------------+ +----------+
| Informer| ----> | Diff/Merge | ----> | Reload |
| (watch) | | (生成配置) | | (更新数据面)|
+---------+ +--------------+ +----------+
^ |
| |
+---- kube-apiserver <-------------------+
(Ingress/Service/Endpoints/Secret)
以 kubernetes/ingress-nginx 为例,其控制循环的关键代码路径:
// pkg/ingress/controller/controller.go 简化逻辑
func (n *NGINXController) syncIngress(interface{}) error {
// 1. 从 informer cache 获取所有 Ingress 资源
ings := n.store.ListIngresses()
// 2. 合并所有 Ingress 为内部配置模型
pcfg := n.getConfiguration(ings)
// 3. 检查配置是否真的发生了变化
if n.runningConfig.Equal(pcfg) {
return nil
}
// 4. 判断是否需要全量 reload
if onlyEndpointsChanged(n.runningConfig, pcfg) {
// 只更新 Lua shared dict,无需 reload
n.configureDynamically(pcfg)
} else {
// 渲染 nginx.conf 模板并执行 reload
n.OnUpdate(pcfg)
}
n.runningConfig = pcfg
return nil
}事件合并与去抖
在大规模集群中,Pod 频繁扩缩容会产生大量 Endpoints 变更事件。如果每个事件都触发一次配置更新,Controller 会陷入频繁 reload 的风暴。因此,几乎所有实现都引入了事件合并(batching)和去抖(debouncing)机制:
// 简化的去抖逻辑
func (n *NGINXController) Start() {
syncCh := make(chan Event, 1024)
// Informer 回调将事件推入 channel
n.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { syncCh <- Event{Type: Add, Obj: obj} },
UpdateFunc: func(old, cur interface{}) { syncCh <- Event{Type: Update, Obj: cur} },
DeleteFunc: func(obj interface{}) { syncCh <- Event{Type: Delete, Obj: obj} },
})
// 主循环:合并一个时间窗口内的所有事件
debounceTimer := time.NewTimer(1 * time.Second)
for {
select {
case <-syncCh:
debounceTimer.Reset(1 * time.Second)
case <-debounceTimer.C:
n.syncIngress(nil)
}
}
}不同 Controller 的默认去抖间隔:
| Controller | 默认去抖间隔 | 可配置 |
|---|---|---|
| ingress-nginx | 1s | --sync-period |
| Traefik | 立即(内存更新无代价) | providers.kubernetesIngress.throttleDuration |
| Contour | 100ms | --contour-config |
| HAProxy | 1s | --rate-limit-update |
NGINX reload 的连接断开问题
NGINX reload 并非无代价操作。执行
nginx -s reload 时,master 进程 fork 新
worker,旧 worker 收到 SIGQUIT
信号后停止接受新连接,等待现有请求完成,超时后强制关闭。
问题在于
worker_shutdown_timeout。kubernetes/ingress-nginx
默认为 240s,在高频 reload 场景下旧 worker
进程可能堆积,消耗大量内存:
# 检查 NGINX worker 进程数量
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- \
ps aux | grep "nginx: worker"
# 如果看到大量 "is shutting down" 的 worker,说明 reload 过于频繁Envoy 的热重启与连接排空
Envoy 的方案从根本上不同。它不依赖信号驱动的 reload,而是通过 xDS API 实现配置的动态下发:
场景一:路由/端点变更(xDS 动态更新)
当路由或上游端点发生变化时,Contour 通过 xDS gRPC 流将增量配置推送给 Envoy。Envoy 在内部原子性地切换路由表和集群配置,整个过程 不涉及进程重启,不中断任何连接。
场景二:Envoy 二进制升级(热重启)
当需要升级 Envoy 自身时,使用热重启(Hot
Restart)机制:新进程通过 Unix Domain Socket
与旧进程通信,通过 SCM_RIGHTS 接管监听
socket,旧进程进入 drain
模式(停止接受新连接、继续服务现有连接、逐步关闭空闲连接),drain
超时后退出。
Envoy 的 drain 机制比 NGINX 的 shutdown 更精细:
# Envoy drain 相关配置
drain_strategy: gradual # 渐进式排空,而非立即停止
drain_timeout: 60s # 排空超时时间
parent_shutdown_time: 90s # 旧进程最终关闭时间关键区别:NGINX reload 会产生新旧 worker 共存的瞬间,可能导致连接中断;Envoy 的 xDS 更新是原子性的内存操作,热重启则有显式的连接排空协议。
五、TLS 终止与证书管理
TLS 终止的三种模式
| 模式 | 路径 | 说明 |
|---|---|---|
| TLS 终止 | Client –HTTPS–> Controller –HTTP–> Pod | Controller 解密,明文转发 |
| TLS 透传 | Client –HTTPS–> Controller –HTTPS–> Pod | 不解密,基于 SNI 四层转发 |
| TLS 重加密 | Client –HTTPS–> Controller –HTTPS–> Pod | 解密后用不同证书重新加密 |
TLS 终止是最常见的模式:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example
spec:
tls:
- hosts:
- secure.example.com
secretName: secure-tls
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-svc
port:
number: 80对于 TLS 透传,使用
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
annotation。需要注意透传模式下 Controller 无法读取 HTTP
头,只能依赖 SNI 做路由。
cert-manager 自动签发
手动管理 TLS 证书在规模化场景下不现实。cert-manager 是 Kubernetes 生态中事实上的证书管理标准:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
ingressClassName: nginx配置好 ClusterIssuer 后,只需在 Ingress 上添加
cert-manager.io/cluster-issuer: "letsencrypt-prod"
annotation,cert-manager 就会自动完成证书签发、续期和 Secret
创建:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-auto
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-svc
port:
number: 80cert-manager 的工作流程:发现带有对应 annotation 的 Ingress 后,创建 Certificate -> CertificateRequest -> Order -> Challenge;对于 HTTP-01 验证,会创建临时 Ingress 和 Pod 供 Let’s Encrypt 访问验证路径;验证通过后将证书写入 Secret;Ingress Controller watch 到 Secret 变化后加载新证书;证书过期前 30 天自动续期。
通配符证书与 DNS-01 验证
通配符证书(*.example.com)需要使用 DNS-01
验证方式:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-wildcard
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-wildcard-key
solvers:
- dns01:
cloudflare:
email: admin@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
selector:
dnsZones:
- "example.com"DNS-01 验证通过在 DNS 中创建 TXT 记录来证明域名所有权,支持签发通配符证书。cert-manager 支持 Cloudflare、Route53、Google Cloud DNS 等多种提供商。
mTLS Passthrough
在服务网格或零信任架构中,可能需要客户端与后端之间的双向 TLS(mTLS)。此时 Ingress Controller 应配置为 TLS 透传模式:
# Contour HTTPProxy 配置 TLS 透传
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: mtls-passthrough
spec:
virtualhost:
fqdn: mtls.example.com
tls:
passthrough: true
tcpproxy:
services:
- name: mtls-backend
port: 443NGINX Ingress 的 ssl-passthrough 实际上是在
stream 块中处理的,所有 passthrough
流量共享一个 NGINX server 块,通过 SNI
路由,性能和灵活性不如正常的 TLS 终止模式。
六、性能瓶颈与调优
单点瓶颈与多副本部署
Ingress Controller 默认部署为 Deployment,通常是 1-2 个副本。在流量增长后,它很容易成为整个集群的性能瓶颈:
# 查看 Ingress Controller 的资源使用
kubectl top pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
# NAME CPU(cores) MEMORY(bytes)
# ingress-nginx-controller-5d4f7b9c6-abc12 850m 512Mi多副本部署需要确保副本分散在不同节点:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
replicas: 3
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx前端 LoadBalancer 的健康检查要配置合理的超时和间隔。
Keep-Alive 连接池
Ingress Controller 与上游 Pod 之间的连接管理直接影响性能。默认情况下,NGINX 对上游的 keep-alive 连接数有限制:
# ingress-nginx 默认的 upstream keepalive 配置
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
keepalive 320; # 每个 worker 保持的最大空闲连接数
keepalive_time 1h; # 连接最大存活时间
keepalive_timeout 60s; # 空闲超时
keepalive_requests 10000; # 单连接最大请求数
}
通过 ConfigMap 调优这些参数:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
# upstream keepalive 连接数(默认 320)
upstream-keepalive-connections: "1024"
# 单连接最大请求数(默认 10000)
upstream-keepalive-requests: "100000"
# 空闲超时(默认 60s)
upstream-keepalive-timeout: "120"
# worker 进程数,auto 表示等于 CPU 核数
worker-processes: "auto"
# 单 worker 最大连接数
max-worker-connections: "65536"
# 启用 worker 间共享连接
worker-cpu-affinity: "auto"对于 Envoy(Contour),连接池和负载均衡策略同样可调:
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: tuned-proxy
spec:
virtualhost:
fqdn: high-traffic.example.com
routes:
- services:
- name: backend-svc
port: 80
protocol: h2c
loadBalancerPolicy:
strategy: WeightedLeastRequest
timeoutPolicy:
response: 30s
idle: 120s速率限制
Ingress 层的速率限制是防止后端过载的第一道防线:
# NGINX Ingress 速率限制(基于 annotation)
metadata:
annotations:
# 每秒请求数限制
nginx.ingress.kubernetes.io/limit-rps: "100"
# 突发允许量
nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
# 并发连接数限制
nginx.ingress.kubernetes.io/limit-connections: "50"
# 限速使用的 key(默认基于 IP)
nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8"Traefik 通过 Middleware 实现:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: global-rate-limit
spec:
rateLimit:
average: 200
burst: 100
period: 1s
sourceCriterion:
ipStrategy:
depth: 1
excludedIPs:
- "10.0.0.0/8"Envoy 支持全局速率限制(需要独立的 rate limit service)和本地速率限制,粒度更细。
监控指标
以 NGINX Ingress Controller 为例,关键 Prometheus 指标:
# 请求延迟分布
nginx_ingress_controller_request_duration_seconds_bucket
# 活跃连接数
nginx_ingress_controller_nginx_process_connections
# reload 次数和成功率
nginx_ingress_controller_success{controller_class="nginx"}一个实用的告警规则,发现 reload 风暴:
rate(nginx_ingress_controller_success{controller_class="nginx"}[5m]) > 10
七、为什么 Ingress API 注定会被替代
Ingress 的根本性缺陷
回顾前面的讨论,Ingress API 的问题不是实现上的,而是设计上的。表达能力不足,核心 spec 只能描述最简单的 Host + Path 路由;扩展机制是无类型、不可移植的 annotations;角色模型缺失,无法区分集群管理员和应用开发者的职责;单一资源承载过多,同时定义了监听端口、TLS、路由、后端引用;不支持非 HTTP 协议。
Gateway API 的设计哲学
Gateway API(前身为 Service API)将 Ingress 单一资源拆分为分层设计:
| 资源 | 职责 | 管理者 |
|---|---|---|
| GatewayClass | 基础设施提供者定义 | 平台团队 |
| Gateway | 监听端口 + TLS 配置 | 集群管理员 |
| HTTPRoute / TCPRoute / GRPCRoute | 路由规则 | 应用开发者 |
| ReferenceGrant | 跨命名空间引用授权 | 命名空间所有者 |
核心改进示例:
# Gateway:集群管理员定义
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: infra
spec:
gatewayClassName: envoy
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true"
---
# HTTPRoute:应用开发者定义
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: app-team
spec:
parentRefs:
- name: production-gateway
namespace: infra
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v1
headers:
- name: X-Canary
value: "true"
backendRefs:
- name: api-v2
port: 80
weight: 100
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- name: api-v1
port: 80
weight: 90
- name: api-v2
port: 80
weight: 10注意 Gateway API 原生支持了请求头匹配(直接在
matches 中指定 header 条件)、流量拆分(通过
weight
字段实现金丝雀发布)、跨命名空间(Gateway 在
infra 命名空间,Route 在 app-team
命名空间)和角色分离(Gateway 由管理员控制,Route
由开发者控制)。
迁移路线
从 Ingress 到 Gateway API 的迁移不必一步到位。大多数 Ingress Controller 已经同时支持两种 API:
# 检查集群是否安装了 Gateway API CRD
kubectl get crd | grep gateway.networking.k8s.io
# 查看可用的 GatewayClass
kubectl get gatewayclass
# ingress-nginx 从 v1.11.0 开始实验性支持 Gateway API
# Contour 从 v1.27.0 开始支持 Gateway API
# Traefik 从 v2.10 开始支持 Gateway API建议的迁移策略:在集群中安装 Gateway API CRD,新服务直接使用 HTTPRoute,现有服务逐步迁移,两种 API 可以长期共存。
关于 Gateway API 的完整设计与实践,将在下一篇文章中详细展开。
八、总结
Ingress 是 Kubernetes 七层流量管理的起点,但不是终点。本文的核心要点:
架构选型:NGINX 生态成熟适合通用场景;Traefik 自动发现能力突出;Envoy 的 xDS 架构在大规模集群中优势明显;HAProxy 在极致性能场景下表现最好。
热更新机制:NGINX 的 reload 存在连接断开风险,社区版通过 Lua 缓解了 Endpoints 变更场景;Traefik 作为单进程完全避免了这个问题;Envoy 通过 xDS 实现了真正的零中断配置更新。
TLS 管理:cert-manager 配合 Ingress Controller 可以实现证书的全自动化管理,支持 HTTP-01 和 DNS-01 两种验证方式。
性能调优:关注连接池配置、worker 进程数、速率限制,以及 Prometheus 指标监控。
演进方向:Ingress API 的设计缺陷(annotations 依赖、角色模型缺失、协议限制)使其必然被 Gateway API 取代。新项目应优先考虑 Gateway API。
在下一篇中,我们将深入 Gateway API 的设计、实现与实战,看看这个”Ingress 的继任者”如何解决本文提到的所有问题。