4. Nginx Ingress Controller 工程实践

Nginx Ingress Controller(社区版,下称 ingress-nginx)是 Kubernetes 中最成熟的七层入口实现之一。它由两部分组成:一组以 Deployment/DaemonSet 形式运行的 nginx 进程(数据面),以及一个监听 Ingress、Service、Endpoint、Secret 等资源变化的控制器(控制面)。控制器把 Ingress 规则翻译成 nginx.conf,再通过 reload 让数据面生效。理解这条“资源 → 配置 → reload → 流量”的链路,是后续所有注解调优的基础。

一、安装与最小可用配置

官方推荐用 Helm 安装,便于后续升级与参数管理。以下为示例参数,实际版本号请以官方仓库为准:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.replicaCount=2 \
  --set controller.metrics.enabled=true \
  --set controller.podAnnotations."prometheus\.io/scrape"=true \
  --set controller.service.type=LoadBalancer

安装后先确认三件事:Pod 是否 Ready、Service 是否拿到外部地址、准入 Webhook 是否正常。准入 Webhook 用于校验 Ingress 注解合法性,若它不可用,新建 Ingress 会被拒绝,这是排障时最容易被忽略的一环。

kubectl -n ingress-nginx get pods,svc
kubectl -n ingress-nginx get validatingwebhookconfiguration

一个最小 Ingress 示例,把 demo.example.com 的流量转发到后端 Service:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /app
        pathType: Prefix
        backend:
          service:
            name: demo-svc
            port:
              number: 80

二、注解调优:从全局到单条规则

ingress-nginx 的调优分三层:Controller 级(ConfigMap ingress-nginx-controller,影响所有 Ingress)、Ingress 级(注解,影响该 Ingress 下所有规则)、以及部分注解可作用于单条 path。常见调优方向包括:

  • 超时与缓冲:proxy-connect-timeout、proxy-read-timeout、proxy-body-size。上传大文件时若返回 413,通常是 proxy-body-size 默认 1m 所致。
  • 连接复用:upstream-keepalive-connections 与 upstream-keepalive-timeout,减少到后端的握手开销。
  • 负载均衡:load-balance 可选 round_robin、least_conn、ewma、ip_hash。长连接场景下 least_conn 往往比默认轮询更均衡。
  • 会话保持:affinity: cookie 配合 session-cookie-name,适合有状态会话但未做外部存储的服务。

调优后不要只看“配置是否写入”,而要看实际效果。可通过 /nginx_status 或 Prometheus 指标观察 nginx_ingress_controller_requests、request_duration_seconds 的分布变化,再决定是否继续调整。

三、灰度发布:Canary 注解

ingress-nginx 原生支持基于权重的金丝雀发布。做法是:为稳定版建一个 Ingress,为金丝雀版建另一个 Ingress,并在金丝雀 Ingress 上打 canary 注解。控制器会把两者合并到同一个 upstream,并按权重分流。

metadata:
  name: demo-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"

除权重外,还支持按请求头(canary-by-header)、按 Cookie(canary-by-cookie)分流,便于内部先验证再放量。灰度过程中应重点观察金丝雀版本的错误率与延迟,一旦异常,把权重改回 0 即可快速回滚,无需删除 Ingress。

四、重写与重定向

重写(rewrite)改变转发给后端的路径,重定向(redirect)改变返回给客户端的地址,两者语义不同,容易混用。典型场景:外部路径 /app 对应后端根路径 /,用 rewrite-target: /;而把 HTTP 强制跳 HTTPS,则用 force-ssl-redirect 或 permanent-redirect。

nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
# path: /app(/|$)(.*)

使用正则重写时必须开启 use-regex,并注意捕获组编号与 rewrite-target 中的 $1、$2 对应关系。重定向则常用 permanent-redirect(301)或 temporal-redirect(302),前者会被浏览器缓存,调试阶段建议先用 302。

五、限流与认证集成

限流通过 limit-rps、limit-rpm、limit-burst-multiplier 控制,超出阈值返回 503。它基于 nginx 的 limit_req 模块,按客户端 IP 维度生效,因此要留意 NAT 场景下大量用户共享同一出口 IP 的问题。

nginx.ingress.kubernetes.io/limit-rps: "20"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"

认证集成常见三种:Basic Auth(auth-type: basic + Secret)、外部认证(auth-url 指向认证服务,配合 auth-response-headers 透传用户信息)、以及 OAuth2 Proxy 等独立组件。Basic Auth 适合内部工具,外部认证适合与统一登录对接。

ingress-nginx 请求处理链路与关键注解作用点 客户端 HTTP/HTTPS LoadBalancer Service ingress-nginx Pod nginx 数据面 + 控制器 监听 Ingress/Secret 变化 后端 Service demo-svc:80 注解作用点(按请求处理顺序) ① 重定向 force-ssl-redirect permanent-redirect ② 认证 auth-type: basic auth-url 外部认证 ③ 限流 limit-rps / limit-rpm limit-burst-multiplier ④ 重写 rewrite-target use-regex ⑤ 灰度分流 canary-weight canary-by-header 稳定版 Ingress weight 90% 金丝雀 Ingress weight 10% 请求依次经过重定向 → 认证 → 限流 → 重写 → 灰度分流,最终到达对应后端

六、工程实践中的判断依据

把上述能力串起来,一个可复现的验证闭环是:先确认 Pod 与 Webhook 正常,再用最小 Ingress 打通链路;随后按需叠加注解,每加一类注解就观察一次指标或日志。例如开启限流后,用压测工具打到阈值以上,确认返回 503 且 nginx_ingress_controller_requests 中对应状态码计数上升;配置灰度后,用带 canary-by-header 的请求验证是否命中金丝雀版本。判断“是否生效”的依据应来自实际响应与指标,而不是配置文件里写了什么。

注解调优建议一次只改一类参数,并记录改动前后的延迟与错误率,否则很难定位是哪项配置带来了变化。
使用 permanent-redirect(301)后浏览器会长期缓存跳转结果,调试阶段优先用 302,确认无误再切换为 301。