Kubernetes Ingress 实战:ingress-nginx 部署与路由规则手册
服务在 Kubernetes 里跑起来只是第一步,怎么把流量从集群外按域名和路径准确地引进来、配好 TLS、做基本的限流和灰度,是每个集群都要面对的问题。Ingress 就是解决这个问题的标准资源:它本身只是一份声明式的路由规则,真正干活的是 Ingress Controller。本文以社区最常用的 ingress-nginx 为例,把部署、四类高频规则、常用注解和排障套路一次讲清。
适用环境:Kubernetes 1.24 及以上版本,Helm 3,集群已有可用的 Service 与业务服务。无论是自建集群还是云厂商托管集群,规则写法一致,差异主要在入口 Service 的暴露方式上,下文会专门说明。
先搞清模型:Ingress 规则与 Controller 的关系
很多初学者会误以为创建了 Ingress 资源流量就能进来,其实不然。三者的关系是:
- Ingress 资源:只是一份 YAML,声明「哪个域名/路径 → 转给哪个 Service」,它自己不监听任何端口;
- Ingress Controller:一个常驻进程(本文是 ingress-nginx,底层是 OpenResty/Nginx),持续监听 Ingress 资源变化,把规则翻译成自己的配置并重新加载;
- 入口 Service:Controller 自己的 Pod 需要一个 LoadBalancer 或 NodePort 类型的 Service 暴露到集群外,外部流量先到这里,再按规则转发到业务 Service。
另外注意 ingressClassName:一个集群可以装多个 Controller(比如内外网各一套),Ingress 通过这个字段指定由谁接管。写错或漏写是最常见的「规则不生效」原因之一。
前置条件
- kubectl 可正常连接目标集群,且有在目标命名空间创建资源的权限;
- 已安装 Helm 3,并能访问 Helm Chart 仓库(离线环境需提前同步 Chart 包);
- 至少有一个已部署的业务 Service 作为转发目标,用于验证规则;
- 准备好一个测试域名,并能修改其 DNS 解析(或用 hosts 文件临时指路验证)。
第一步:用 Helm 部署 ingress-nginx
ingress-nginx 官方推荐用 Helm 安装。先添加仓库,再按集群类型选择 values。关键差异在 Controller 的 Service 如何暴露:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx --create-namespace \
-f values-ingress.yaml
values 里最需要关注的三处:
controller:
replicaCount: 2 # 生产环境至少 2 副本,避免单点
service:
type: LoadBalancer # 云上有 LB 时用 LoadBalancer;自建集群可改 NodePort 或结合 MetalLB
annotations:
# 云厂商注解差异在这里:不同云的 LB 实现靠注解区分,例如健康检查方式、带宽、后端协议等
service.beta.kubernetes.io/example-cloud-lb-type: "nlb"
config:
proxy-body-size: "50m" # 按业务调整上传体积上限,默认 1m 很容易踩坑
proxy-read-timeout: "120" # 长耗时接口按需调大,避免 504
- 云上托管集群:Service 类型选 LoadBalancer,云厂商会自动创建负载均衡实例,但健康检查路径、协议、跨可用区等行为靠各家自己的注解控制,同一份 values 换云就要换注解块;
- 自建集群:没有云 LB 时常用 NodePort + 外部 HAProxy/Nginx 转发,或装 MetalLB 模拟 LoadBalancer,Ingress 规则本身不用改;
- 多副本与亲和:副本数大于 1 时建议开启 podAntiAffinity,避免调度到同一节点。
第二步:四类常用路由规则
1. 按域名路由
最基础的形态:不同域名转发到不同 Service。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-by-host
namespace: prod
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-web
port:
number: 80
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-api
port:
number: 8080
2. 按路径路由
同一域名下按路径前缀分流。注意 pathType 的语义:Prefix 是按路径段前缀匹配(/api 不会误伤 /apixxx),Exact 是全等匹配。Ingress 的路径匹配优先级是「精确优先、最长前缀优先」,这个顺序是固定的,不要指望靠书写顺序控制。
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: app-api
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: app-web
port:
number: 80
3. TLS 证书
证书以 Secret 形式存放(type 为 kubernetes.io/tls),Ingress 通过 tls 段引用。基于 SNI,一个 Controller 可以承载多个域名的不同证书。
kubectl create secret tls example-com-tls \
--cert=fullchain.pem --key=privkey.pem -n prod
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: example-com-tls
rules:
- host: app.example.com
# ... 同上
生产环境建议用 cert-manager 自动签发和续期,手维护证书最常见的事故是过期。没有配 TLS 的域名走 HTTP,配了之后 ingress-nginx 默认会把 HTTP 强制跳 HTTPS,不需要额外写规则。
4. 重定向与重写
这两类需求靠注解实现。重定向是让浏览器跳到新地址(域名迁移、裸域名跳 www),重写是 URL 在转发给后端前内部改写(浏览器无感知),不要混用:
metadata:
annotations:
# 永久重定向:老域名整体跳新域名
nginx.ingress.kubernetes.io/permanent-redirect: "https://new.example.com$request_uri"
# 路径重写:/old-api/xxx 转发给后端时变成 /xxx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: app.example.com
http:
paths:
- path: /old-api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: app-api
port:
number: 8080
重写时 pathType 必须用 ImplementationSpecific 并在路径里写捕获组,$2 对应第二个捕获组,这是新手最容易踩的组合坑。
第三步:限流、白名单与金丝雀注解
ingress-nginx 的很多实用能力都在注解里,以下三个是高频项:
metadata:
annotations:
# 限流:单 IP 每秒请求数与突发上限,防刷接口的第一道闸
nginx.ingress.kubernetes.io/limit-rps: "20"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "3"
# 白名单:只允许指定网段访问,管理后台类服务必配
nginx.ingress.kubernetes.io/whitelist-source-range: "192.0.2.0/24,198.51.100.0/24"
金丝雀发布需要两条 Ingress:主版本一条正常写;新版本单独一条 Ingress、指向新 Service,并打上金丝雀注解。流量先按比例放 5%–10% 到新版本,观察错误率和延迟再逐步放大:
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10% 流量走新版本
# 也可按请求头切流:某个 header 等于指定值时才走新版本,适合内部先行验证
# nginx.ingress.kubernetes.io/canary-by-header: "x-canary"
# nginx.ingress.kubernetes.io/canary-by-header-value: "always"
注意金丝雀的两条 Ingress 必须 host 和 path 完全一致,且金丝雀那条不要再配 TLS 和限流等冲突注解,以主 Ingress 的为准。
验证
规则应用后按这条链路逐段确认,不要只看浏览器结果:
# 1. Ingress 已被控制器接管,ADDRESS 列有入口地址
kubectl get ingress -n prod
# 2. 查看规则汇总与后端端点
kubectl describe ingress app-by-host -n prod
# 3. 从集群内绕过外部入口,直接验证业务 Service 本身通不通
kubectl run curl-test --rm -it --image=curlimages/curl --restart=Never -- \
curl -sv http://app-web.prod.svc.cluster.local/
# 4. 走完整链路验证域名与 TLS
curl -vk https://app.example.com/ -o /dev/null
# 5. 看控制器当场日志(改完规则后必做的一步)
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50
常见问题
1. 返回 404:规则没命中。先分清是谁返回的 404:响应头里带 ingress-nginx 特征、且控制器访问日志里有这条记录,说明流量到了 Controller 但没有规则匹配——重点查 host 写错、ingressClassName 不匹配、Ingress 和 Service 不在同一命名空间。如果控制器日志里根本没有这条请求,问题在更前面(DNS、LB、NodePort 转发),不要在规则上瞎改。
2. 返回 503:规则命中了,但后端没有可用端点。这是「Service 存在但 Endpoints 为空」的典型信号:查业务 Pod 是否 Ready、Service 的 selector 与 Pod 标签是否对得上、targetPort 是否写错。用 kubectl get endpoints 一眼就能定界,503 与应用本身无关,先别去翻业务代码。
3. 返回 502:端点存在,但连接被拒绝或中途断开。说明流量到了 Pod 的 IP:端口这一层:常见原因是应用监听地址绑在 127.0.0.1、端口写错、应用启动慢还没监听、或应用处理中崩溃。抓手是直连 Pod IP 做一次 curl,再对照应用日志;同时看 Ingress 事件里有没有 upstream 相关的告警。kubectl describe ingress 输出的 Events 段和 Controller 日志里的 upstream 报错是最直接的证据链。
4. 改完规则不生效,还报配置测试失败。某个 Ingress 注解写错(比如正则不合法)可能导致整份 Nginx 配置生成失败,连带其他正常规则一起不更新。看 Controller 日志里的 configuration test failed 字样,先修复或摘掉出问题的那条 Ingress,其余规则会立即恢复。养成习惯:新规则先在测试命名空间验证,再进生产。
小结
Ingress 的排障本质是分段定界:先确认流量有没有到 Controller,再确认规则有没有命中,最后确认后端端点通不通——404、503、502 正好对应这三段。规则层面把四类高频写法(域名、路径、TLS、重定向/重写)固化成模板,灰度和限流用注解补充,日常需求基本够用。需要提前知道的是:Ingress API 已进入特性冻结,官方方向是功能更完整的 Gateway API,新集群可以评估直接上 Gateway API,老集群不必为此迁移,ingress-nginx 仍在正常维护。







