饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kubernetes Gateway API 替代 Ingress:3 步迁移到下一代流量管理标准,附生产避坑指南

痛点: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 - 平台团队通过 GatewayallowedRoutes 控制哪些 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

行动建议:

  1. 新集群直接用 Gateway API,不要再写 Ingress
  2. 存量集群制定 6 个月迁移计划,从非核心服务开始切换
  3. Controller 选型优先考虑 Envoy Gateway(CNCF 生态、社区活跃)或 Cilium Gateway(如果已用 Cilium CNI)
  4. Ingress 资源短期不会被 K8s 移除,但已进入维护模式,不会再加新特性

Gateway API 是 Kubernetes 流量管理的确定性方向,越早迁移,越早摆脱 annotation 地狱。

您还没有登录,请登录后发表评论。