土法炼钢兴趣小组的算法知识备份

Ingress 控制器:从 Nginx 到 Envoy,七层流量入口全解

目录

在前面的文章中,我们已经讨论了 Service 如何在四层完成负载均衡。但现实中,绝大多数面向用户的流量都是 HTTP/HTTPS,需要基于域名、路径、请求头做路由。Kubernetes 从 1.1 版本引入了 Ingress 资源来解决这个问题,然而围绕它的争议从未停止:注解泛滥、语义模糊、跨实现不兼容。

本文将从 Ingress 资源的设计出发,逐一拆解 NGINX、Traefik、Envoy、HAProxy 四大流派的架构差异,深入它们的热更新机制与 TLS 终止策略,最后解释为什么 Ingress API 注定要被 Gateway API 取代。

Ingress Controller 架构对比与请求路径

一、从 Service 到 Ingress:为什么需要七层入口

四层暴露的局限

通过 NodePortLoadBalancer 类型的 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 只支持:

而实际生产中常见的需求,如速率限制、请求重写、会话保持、跨域配置、流量镜像、金丝雀发布,全部依赖 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: letsencrypt

3. 内存热更新

由于路由表和配置完全在 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 体系(MappingHostTLSContext)。

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: 80

cert-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: 443

NGINX 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 的继任者”如何解决本文提到的所有问题。


By .