痛点:Ingress 越用越力不从心
你是否遇到过这些场景:
- 一个集群跑了 3 种 Ingress Controller(Nginx、Traefik、ALB),annotation 写法完全不同,运维人员换个组就得重新学
- 想做流量灰度(90%→v1,10%→v2),Ingress 的 annotation hack 方式既丑陋又不可移植
- 平台团队想统一管控入口流量策略,但 Ingress 把「基础设施配置」和「应用路由」混在一个对象里,权限没法分
Kubernetes Ingress API 诞生于 2015 年,设计之初只考虑了 HTTP 反向代理这一种场景。十年过去,它的 spec 字段加了一堆 annotation 扩展,本质上变成了"各厂商自定义字段的垃圾桶"。
Gateway API 是 Kubernetes SIG-Network 推出的 Ingress 继任者,2023 年 10 月 GA,2026 年已被 Istio、Envoy Gateway、Cilium、Nginx Gateway Fabric、AWS Gateway API Controller 等主流方案全面支持。
方案:Gateway API 的 3 层分离模型
Gateway API 的核心设计哲学是角色分离:
| 层级 | 资源 | 负责人 | 职责 |
|---|---|---|---|
| 基础设施层 | GatewayClass |
平台/基础设施团队 | 选择哪种 Controller(如 Envoy、Cilium) |
| 集群入口层 | Gateway |
集群管理员 | 定义监听端口、TLS 证书、允许哪些 namespace 接入 |
| 应用路由层 | HTTPRoute / GRPCRoute / TCPRoute |
开发团队 | 配置路径匹配、流量拆分、Header 改写 |
这意味着:
- 开发只管写 HTTPRoute,不用关心底层是 Envoy 还是 Nginx
- 平台团队通过 Gateway 的 allowedRoutes 控制哪些 namespace 能挂载路由,实现多租户隔离
- 迁移 Controller 时,只换 GatewayClass,路由规则零改动
实操步骤
Step 1:安装 Gateway API CRD 和 Controller
Gateway API 的 CRD 需要单独安装(不随 K8s 版本内置):
# 安装 Gateway API v1.2 标准 CRD(含实验性频道支持 TCPRoute 等)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/experimental-install.yaml
# 验证 CRD 已就绪
kubectl get crd | grep gateway
# 预期输出:
# gatewayclasses.gateway.networking.k8s.io
# gateways.gateway.networking.k8s.io
# httproutes.gateway.networking.k8s.io
# grpcroutes.gateway.networking.k8s.io
选一个 Controller 部署,以 Envoy Gateway(CNCF 毕业项目)为例:
# Helm 安装 Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.3.0 \
-n envoy-gateway-system --create-namespace
# 确认 GatewayClass 自动注册
kubectl get gatewayclass
# NAME CONTROLLER ACCEPTED
# eg gateway.envoyproxy.io/gatewayclass True
Step 2:创建 Gateway 并配置 TLS
# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: prod-gateway
namespace: infra
spec:
gatewayClassName: eg
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
namespace: infra
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true" # 只有打了此标签的 namespace 能接入
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same # HTTP 只允许同 namespace
kubectl apply -f gateway.yaml
# 获取 Gateway 分配的 LB 地址
kubectl get gateway prod-gateway -n infra -o jsonpath='{.status.addresses[0].value}'
Step 3:编写 HTTPRoute 替代 Ingress 规则
原 Ingress(要被替代的):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 8080
等效 HTTPRoute(标准化、可移植):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: app-team
spec:
parentRefs:
- name: prod-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: app-v1
port: 8080
weight: 90
- name: app-v2
port: 8080
weight: 10 # 原生流量拆分,无需 annotation hack
流量灰度、Header 路由、请求改写全部是一等公民:
rules:
- matches:
- headers:
- type: Exact
name: x-canary
value: "true"
backendRefs:
- name: app-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Request-Source
value: gateway
backendRefs:
- name: app-v1
port: 8080
避坑指南
1. CRD 版本与 Controller 版本不匹配
现象: Gateway 创建后 status 一直是 Programmed: False
原因: 安装了 v1.2 的 CRD 但 Controller 只支持 v1.1
解决: 始终查看 Controller 文档确认兼容的 Gateway API 版本。用以下命令快速排查:
kubectl describe gateway prod-gateway -n infra | grep -A5 "Conditions"
2. 跨 namespace 引用 Secret/Service 需要 ReferenceGrant
现象: HTTPRoute 引用其他 namespace 的 backend 报 RefNotPermitted
原因: Gateway API 默认禁止跨 namespace 引用,需显式授权
解决:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-infra-tls
namespace: infra # Secret 所在 namespace
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: infra
to:
- group: ""
kind: Secret
3. 旧 Ingress 与 Gateway API 并行期的 DNS 切换
现象: 切换后部分流量丢失
最佳实践:
- 先让两套共存,Gateway API 分配新 LB IP
- DNS 做加权切换(如 Route53 weighted routing),逐步将流量从旧 Ingress LB 迁移到新 Gateway LB
- 确认新链路指标正常后,再删除旧 Ingress 资源
- 全程用 kubectl get httproute -A 检查路由 Accepted/ResolvedRefs 状态
总结
| 对比项 | Ingress | Gateway API |
|---|---|---|
| 角色分离 | ❌ 全混在一起 | ✅ 3 层解耦 |
| 流量拆分 | annotation hack | 原生 weight 字段 |
| 多协议支持 | 仅 HTTP/HTTPS | HTTP/gRPC/TCP/UDP/TLS |
| 跨厂商可移植 | 依赖 annotation | 标准 spec 字段 |
| 多租户安全 | 无内置机制 | allowedRoutes + ReferenceGrant |
行动建议:
- 新集群直接用 Gateway API,不要再写 Ingress
- 存量集群制定 6 个月迁移计划,从非核心服务开始切换
- Controller 选型优先考虑 Envoy Gateway(CNCF 生态、社区活跃)或 Cilium Gateway(如果已用 Cilium CNI)
- Ingress 资源短期不会被 K8s 移除,但已进入维护模式,不会再加新特性
Gateway API 是 Kubernetes 流量管理的确定性方向,越早迁移,越早摆脱 annotation 地狱。