K8s Ingress 到 Gateway API 的迁移:不止是换个 API 对象

发布时间:2026/7/16 21:44:18
K8s Ingress 到 Gateway API 的迁移:不止是换个 API 对象 K8s Ingress 到 Gateway API 的迁移不止是换个 API 对象一、一个 Ingress 对象被十个团队同时修改YAML 冲突比代码冲突还难解传统 Ingress 的问题不是功能少而是所有权模型混乱Ingress 只有一个对象但路由规则来自不同团队前端团队想加一个/app路由后端团队想改超时安全团队想加 WAF 规则但 Ingress 不支持只好在 Annotations 里塞一个 Ingress 上有 37 个 Annotations没人敢删任何一个Gateway API 改了什么把单体 Ingress 拆成了关注点分离的角色模型。二、Ingress vs Gateway API 架构对比角色分离模型GatewayClass - 集群管理员 集群有哪些 Ingress Controller 可用 Gateway - 平台管理员 部署一个网关实例监听 443 端口 HTTPRoute - 应用开发者 我的服务路由规则是什么 ReferenceGrant - 安全管理员 允许 A 命名空间的路由引用 B 命名空间的服务三、从 Ingress 迁移到 Gateway APIIngress迁移前# 旧 IngressAnnotation 地狱 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress annotations: nginx.ingress.kubernetes.io/proxy-body-size: 10m nginx.ingress.kubernetes.io/proxy-read-timeout: 60 nginx.ingress.kubernetes.io/ssl-redirect: true nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/cors-allow-origin: * nginx.ingress.kubernetes.io/rate-limiting: 100r/s nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8 cert-manager.io/cluster-issuer: letsencrypt-prod # ... 还有 20 个 Annotation spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: app-tls rules: - host: app.example.com http: paths: - path: /api pathType: Prefix backend: service: name: api-service port: number: 8080 - path: / pathType: Prefix backend: service: name: frontend-service port: number: 3000Gateway API迁移后# 1. Gateway - 平台管理员负责 apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: app-gateway namespace: infra # 平台命名空间 spec: gatewayClassName: nginx listeners: - name: https port: 443 protocol: HTTPS hostname: *.example.com tls: mode: Terminate certificateRefs: - name: app-tls allowedRoutes: namespaces: from: All # 接受所有命名空间的路由 --- # 2. HTTPRoute - 前端团队 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: frontend-routes namespace: frontend-team spec: parentRefs: - name: app-gateway namespace: infra hostnames: - app.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: frontend-service port: 3000 filters: - type: ResponseHeaderModifier responseHeaderModifier: add: - name: X-Frame-Options value: DENY - name: X-Content-Type-Options value: nosniff timeouts: request: 30s --- # 3. HTTPRoute - 后端团队 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api-routes namespace: backend-team spec: parentRefs: - name: app-gateway namespace: infra hostnames: - app.example.com rules: - matches: - path: type: PathPrefix value: /api backendRefs: - name: api-service port: 8080 filters: - type: RequestHeaderModifier requestHeaderModifier: set: - name: X-Request-ID value: $req_id - type: ExtensionRef extensionRef: group: networking.gloo.solo.io kind: RateLimitConfig name: api-ratelimit timeouts: request: 60s backendRequest: 30s --- # 4. 流量拆分灰度发布 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api-canary namespace: backend-team spec: parentRefs: - name: app-gateway namespace: infra hostnames: - api.canary.example.com rules: - matches: - path: type: PathPrefix value: /api/v2 backendRefs: - name: api-service-v2 port: 8080 weight: 10 # 10% 流量到新版本 - name: api-service port: 8080 weight: 90 # 90% 流量到旧版本迁移策略脚本# migrate_ingress.py - 辅助迁移工具 import yaml import sys from typing import Dict, Any class IngressToGatewayMigrator: 将 Ingress YAML 转换为 Gateway API YAML def __init__(self, gateway_name: str, gateway_namespace: str): self.gateway_name gateway_name self.gateway_namespace gateway_namespace def convert(self, ingress_yaml: str) - Dict[str, str]: 返回 {filename: yaml_content} ingress yaml.safe_load(ingress_yaml) results {} spec ingress.get(spec, {}) annotations ingress[metadata].get(annotations, {}) for rule in spec.get(rules, []): host rule.get(host, *) for path_rule in rule.get(http, {}).get(paths, []): route_name self._generate_route_name(host, path_rule) http_route { apiVersion: gateway.networking.k8s.io/v1, kind: HTTPRoute, metadata: { name: route_name, namespace: ingress[metadata][namespace], labels: { migrated-from: ingress, original-ingress: ingress[metadata][name], } }, spec: { parentRefs: [{ name: self.gateway_name, namespace: self.gateway_namespace, }], hostnames: [host], rules: [{ matches: [{ path: { type: path_rule.get(pathType, Prefix), value: path_rule.get(path, /), } }], backendRefs: [{ name: path_rule[backend][service][name], port: path_rule[backend][service][port][number], }], }] } } # 转换 Annotations 为 filters filters self._convert_annotations(annotations) if filters: http_route[spec][rules][0][filters] filters results[f{route_name}.yaml] yaml.dump( http_route, default_flow_styleFalse ) return results def _convert_annotations(self, annotations: dict) - list: 将常见 Nginx Ingress Annotations 转换为 Gateway filters filters [] # CORS if nginx.ingress.kubernetes.io/cors-allow-origin in annotations: filters.append({ type: ResponseHeaderModifier, responseHeaderModifier: { add: [{ name: Access-Control-Allow-Origin, value: annotations[ nginx.ingress.kubernetes.io/cors-allow-origin ], }] } }) # Rewrite if annotations.get( nginx.ingress.kubernetes.io/rewrite-target, ) /: filters.append({ type: URLRewrite, urlRewrite: { path: { type: ReplacePrefixMatch, replacePrefixMatch: /, } } }) # Timeout timeout annotations.get( nginx.ingress.kubernetes.io/proxy-read-timeout ) if timeout: filters.append({ type: RequestTimeout, requestTimeout: f{timeout}s, }) return filters def _generate_route_name(self, host: str, path_rule: dict) - str: service path_rule[backend][service][name] path path_rule.get(path, /).replace(/, -).strip(-) return f{service}-{path or root} # 使用 if __name__ __main__: migrator IngressToGatewayMigrator( gateway_nameapp-gateway, gateway_namespaceinfra, ) with open(sys.argv[1]) as f: ingress_yaml f.read() results migrator.convert(ingress_yaml) for filename, content in results.items(): with open(foutput/{filename}, w) as f: f.write(content) print(fGenerated: output/{filename})四、迁移陷阱与架构权衡陷阱为什么危险怎么避免Gateway Controller 不支持所有特性Annotation 功能丢失先做功能对照表确认覆盖Gateway 只有一个 → 单点所有路由挂在一个网关下多 Gateway按业务域拆分Route 重复匹配同一个请求匹配多个 Route使用 hostnames path 精确匹配跨命名空间引用未授权Route 引用了不该引用的 Service必须创建 ReferenceGrant证书管理变成 Gateway 的职责cert-manager 集成方式变了使用 cert-manager Gateway 集成团队不熟悉新 API迁移后误操作先写 K8s RBAC 限制团队只能改 HTTPRoute是否应该迁移应该迁移的场景一个 Ingress 被多个团队修改所有权冲突Annotations 超过 10 个功能越来越复杂需要灰度发布按权重拆分流量需要跨命名空间路由需要 ReferenceGrant不需要迁移的场景小团队只有一个服务一个简单路由规则Ingress Controller 还没支持 Gateway API检查兼容性清单团队 K8s 还在 1.24 以下Gateway API v1 需要 1.25双轨运行策略不需要一次性迁移。Gateway API 和 Ingress 可以共存部署 Gateway Gateway Controller新服务直接用 HTTPRoute旧服务逐步迁移每次迁移一条路由规则全部迁移完成后删除旧 Ingress五、总结Gateway API 不是升级版的 Ingress而是换了所有权模型Ingress一个对象谁都能改 → 混乱Gateway APIGateway平台管 HTTPRoute团队管→ 分离三个迁移决策Gateway 怎么分按环境prod/staging分还是按业务域分Annotation 替换了吗所有 Annotation 都有对应的 Filter 吗跨命名空间访问安全吗ReferenceGrant 配了吗一句话Gateway API 解决了 Ingress 最根本的问题——一个对象被所有人改迟早出事故。