Appearance
Gateway 与 Ingress
一个 Web 服务要通过域名从外部访问时,最容易想到的是给 Service 配 LoadBalancer。一个 Service 一个云负载均衡,十个 Service 就是十个 LB——成本、管理和证书配置很快就会失控。七层入口要做的事情是把域名和路径作为分发依据,让一个入口把请求路由到不同 Service,而不是每个 Service 单独暴露。
Ingress 最早承担了这个角色,把域名、路径、证书、后端 Service 写成 K8s 资源对象。但每个 Ingress 控制器的功能差异靠 annotation 表达——同一个 rewrite-target 注解,nginx ingress 和 traefik 的配置方式完全不同。换控制器意味着重写所有 Ingress 规则的注解部分。另外 Ingress 只有一个对象,平台管理员和开发者都在上面改,没有操作边界。
Gateway API 是社区对这个问题的回应。它用标准 CRD 替代注解——域名、路径、Header 匹配、权重分流都写成 HTTPRoute 里的字段,不再依赖控制器私有注解。同时拆成三个对象实现角色分离:基础设施团队管 GatewayClass 和 Gateway,开发者只写 HTTPRoute。
| 对象 | 谁维护 | 做什么 |
|---|---|---|
| GatewayClass | 平台/基础设施 | 声明用哪个控制器(Envoy Gateway、Traefik 等) |
| Gateway | 平台/基础设施 | 声明入口实例——监听哪些端口、提供哪些协议的证书 |
| HTTPRoute | 开发者/应用团队 | 声明路由规则——域名、路径、Header、权重、后端 Service |
Ingress API 仍然是稳定的,但官方已将其标为 frozen,新功能全部进入 Gateway API。社区维护的 kubernetes/ingress-nginx 已于 2026 年 3 月退役——不等于所有 NGINX 控制器失效,F5 NGINX Ingress Controller、商业 NGINX、HAProxy、Traefik、Envoy Gateway 都是独立项目。
入口链路的位置
Ingress 和 Gateway 都不是 Nginx 或 Envoy 本身。它们是 K8s 资源对象,描述"域名、路径、证书、后端 Service"这类规则。真正接收和转发请求的是控制器 Pod。
去掉入口组件时,外部请求要么直接打到 NodePort(每个 Service 一个端口),要么每个 Service 配一个云 LB——两种方式都很难统一做 TLS、限流、日志和灰度。
Gateway API 拆成三层后:
平台管理员创建 GatewayClass 和 Gateway 之后,开发者只关心 HTTPRoute——域名怎么配、请求往哪个 Service 发、权重怎么分。不需要知道 Gateway 监听在哪个端口、证书放在哪个 Secret。
Gateway API 对象模型
安装 Gateway API CRD 和控制器之后,第一步是声明用哪个控制器:
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controllerGatewayClass 创建后只是声明"集群里有这个控制器可用",本身不会创建任何 Pod 或监听端口。一个集群可以有多个 GatewayClass,对应不同控制器——比如同时有 Envoy Gateway 和 Traefik。
第二步是创建 Gateway,定义入口实例和监听端口:
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: gateway-system
spec:
gatewayClassName: envoy
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same # 默认值,只允许同 namespace 的 HTTPRoute 绑定Gateway 创建后,控制器会在集群里部署对应的数据面 Pod(Envoy Proxy 或 Traefik 实例),并开始监听声明的端口。allowedRoutes.namespaces.from 默认是 Same——只接受同 namespace 的 HTTPRoute。设成 All 后,任意 namespace 的 HTTPRoute 都可以绑定这个 Gateway,这时要配合 RBAC 限制谁能创建 HTTPRoute。
验证 Gateway 是否就绪:
bash
kubectl get gateway -A
# STATUS 列显示 Programmed=True 说明控制器已接受并下发配置第三步是创建 HTTPRoute,声明路由规则。先从一个最简单的规则开始——单域名、单 Service、无 TLS:
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web
namespace: demo
spec:
parentRefs:
- name: main-gateway
namespace: gateway-system
hostnames:
- web.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web
port: 80parentRefs 声明这个 HTTPRoute 绑定到哪个 Gateway。不写 namespace 时默认同 namespace。path.type 支持 PathPrefix(前缀匹配)、Exact(精确匹配)和 RegularExpression(正则),绝大多数场景 PathPrefix 够用。
apply 之后验证路由状态:
bash
kubectl get httproute web -n demo
kubectl get httproute web -n demo -o yaml | grep -A40 status:从外部验证:
bash
curl -H 'Host: web.example.com' http://<gateway-ip>/-H 'Host:' 模拟浏览器发送的 Host 头。Gateway 用 Host 头匹配 hostnames,所以在 DNS 配好之前可以用这个方式测试路由是否生效。
HTTPRoute 的 status.conditions 反映了路由是否被 Gateway 接受:
| Conditions | 含义 | 用户看到什么 | 排查入口 |
|---|---|---|---|
Accepted=False | 路由没被 Gateway 接受 | 请求 404 或连接拒绝 | kubectl describe httproute,看 parentRefs、namespace 权限、listener 是否匹配 |
ResolvedRefs=False | 后端 Service 或 Secret 解析失败 | 请求 503 | kubectl get svc 确认后端存在,Secret 名称和 namespace 正确 |
Programmed=False | 控制器还没把规则下发到数据面 | 请求可能返回旧规则或 404 | kubectl describe gateway 看 Gateway 自身状态 |
Accepted、ResolvedRefs、Programmed 三者全部 True 之后,路由才算真正生效。
逐层叠加
在基础路由上叠加金丝雀——同一个域名按权重把流量分给两个 Service:
yaml
rules:
- backendRefs:
- name: web-v1
port: 80
weight: 90
- name: web-v2
port: 80
weight: 10不加 Header 匹配或 Cookie 亲和时,权重是随机分配——每次请求独立决策,不保证同一用户的连续请求落在同一版本。
再叠加 TLS——在 Gateway 的 listener 上加证书引用:
yaml
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: example-com-tls
kind: Secret
allowedRoutes:
namespaces:
from: Samemode: Terminate 表示 TLS 在 Gateway 终止,后端 Pod 走 HTTP。证书来自 example-com-tls 这个 Secret。Secret 必须在 Gateway 所在的 namespace。
完整的请求链路:
Ingress 的现状
| 项目 | 现状 |
|---|---|
| Ingress API | 稳定 API,无移除计划,但已 frozen |
| 新功能 | 进入 Gateway API |
| ingress-nginx | kubernetes/ingress-nginx 已于 2026 年 3 月退役 |
| 老集群 | 现有 Ingress 仍能工作,但控制器维护状态需要单独确认 |
从 Ingress 迁到 Gateway API
迁移前先盘点现有资源:
bash
kubectl get ingress -A
kubectl get ingress -A -o yaml | grep "nginx.ingress.kubernetes.io"第二条命令把集群里所有 Ingress 的注解全部拉出来——注解是迁移的核心工作量,大部分路由规则本身可以直接翻译。
Ingress 路由规则到 Gateway API 的对应关系:
| Ingress 字段 | Gateway API 对象 |
|---|---|
ingressClassName | GatewayClass / Gateway |
rules.host | HTTPRoute.spec.hostnames |
paths.path | HTTPRoute.rules.matches.path |
backend.service | HTTPRoute.rules.backendRefs |
tls.secretName | Gateway.listener.tls.certificateRefs 或控制器约定 |
注解比路由规则麻烦。迁移时需要逐个对照:
| 注解能力 | Gateway API 替代方式 |
|---|---|
| rewrite-target | HTTPRoute filter 或控制器扩展 |
| canary / canary-weight | HTTPRoute backendRefs weight |
| rate limiting | 控制器插件、外部网关或 Service Mesh |
| auth / oauth2 | 控制器扩展或外部认证服务 |
| proxy-body-size / proxy-read-timeout | 控制器特定字段或 policy CRD |
没有控制器能 100% 覆盖所有常用注解。选型之前把现有集群的注解全部拉出来,和候选控制器的能力做对照,比看功能列表有用。
迁移按入口链路拆分:低风险域名先迁,保留旧 Ingress 作为回退路径。先迁纯路由(域名→Service),再迁带 rewrite 的,最后迁带 TLS、鉴权和限流的。每条路由迁完后用 curl -H 'Host:' 分别验证新 Gateway 和旧 Ingress,确认行为一致。
控制器选型
| 控制器 | 特点 |
|---|---|
| Envoy Gateway | Gateway API 原生方向,社区活跃 |
| Traefik | 上手轻,Gateway API 支持积极 |
| HAProxy Ingress/Gateway | 传统负载均衡能力强 |
| Cilium Gateway | 和 Cilium/eBPF 网络栈结合 |
| 云厂商 Gateway/LB Controller | 云上和 SLB/ALB/NLB 集成 |
| F5 NGINX Ingress Controller | NGINX 生态,和社区 ingress-nginx 不同项目 |
选型关注点:Gateway API 覆盖了哪些版本来支持、现有注解有没有对应的替代方式、访问日志和指标的数据格式、CRD 运维复杂度、和现有 CNI 的配合、社区维护活跃程度。