Kubernetes Gateway API生产迁移与流量治理
Kubernetes Ingress 解决了“把 HTTP 服务暴露出去”的基础问题,但复杂路由、跨团队权限、流量切分和实现差异往往依赖控制器私有注解。Gateway API 将基础设施、监听器和应用路由拆成不同资源,目标不是简单替换 YAML,而是让平台团队与应用团队之间的职责边界更清楚。
本文面向已经运行 Ingress、准备建设统一入口或需要灰度流量治理的团队。示例使用稳定 API 字段;控制器支持范围、扩展字段和安装版本应以所选实现的兼容矩阵为准。迁移必须先在测试集群验证,不应直接删除现有 Ingress。
一、先理解资源模型
| 资源 | 常见负责人 | 主要职责 |
|---|---|---|
| GatewayClass | 平台团队 | 选择控制器和入口实现 |
| Gateway | 集群或网络团队 | 地址、端口、协议、TLS和可接入范围 |
| HTTPRoute | 应用团队 | 域名、路径、请求匹配、后端和流量权重 |
| GRPCRoute | 应用团队 | gRPC方法匹配和后端路由 |
| ReferenceGrant | 资源拥有者 | 明确允许跨命名空间引用 |
一个 Gateway 只能引用一个 GatewayClass,但可连接多个 Route。监听器通过 allowedRoutes 控制哪些命名空间能够挂载路由;Route 再通过 parentRefs 选择具体 Gateway 或监听器。这种双向约束比“所有人都改同一份入口配置”更适合多团队环境。
Gateway API 是规范,不是数据平面。只有安装了支持它的控制器,资源状态才会真正转化为负载均衡器、代理或云网络配置。
二、迁移前建立清单
先导出现有 Ingress,不要凭印象迁移:
kubectl get ingress -A -o yaml > ingress-backup.yaml
kubectl get ingressclass
kubectl get secret -A --field-selector type=kubernetes.io/tls
kubectl get svc,endpointslice -A
逐项记录:
- 域名、路径、后端 Service 和端口;
- TLS Secret 所在命名空间、证书续期方式;
- 重写、跳转、限流、认证、白名单和会话保持注解;
- WebSocket、gRPC、长连接和大文件上传;
- 默认后端、错误页和健康检查;
- 真实客户端 IP、代理头和访问日志;
- 公网、内网、独享和共享入口;
- DNS TTL、负载均衡地址及回滚入口。
最容易遗漏的是控制器私有注解。应建立“原功能—Gateway API标准字段—实现扩展—暂不支持”的映射表。没有等价能力的功能不能假装已经迁移,必须保留旧入口、调整架构或等待实现支持。
三、先验证控制器与一致性
选择实现时至少比较:
- 支持的 Gateway API 版本和 Standard/Experimental 通道;
HTTPRoute、GRPCRoute、ReferenceGrant等资源的一致性等级;- 是否支持云负载均衡、裸机地址池或现有数据平面;
- TLS终止、流量权重、重写、镜像、超时和重试能力;
- 指标、日志、Trace、配置下发状态和数据平面健康;
- 升级策略、回滚方式和多副本高可用;
- 与现有 Ingress 控制器能否并行运行。
不要只看 CRD 能否创建。资源被 API Server 接收,不代表控制器已经接受配置。生产验收必须检查 status.conditions 中的 Accepted、Programmed、ResolvedRefs 等条件。
四、建立一个最小入口
下面示例将基础设施和业务路由分离到两个命名空间。类名应替换为实际控制器提供的 GatewayClass。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public-gateway
namespace: gateway-infra
spec:
gatewayClassName: production
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.example.com"
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: public
为允许接入的业务命名空间增加标签,再创建 Route:
kubectl label namespace app-prod gateway-access=public
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders
namespace: app-prod
spec:
parentRefs:
- name: public-gateway
namespace: gateway-infra
sectionName: http
hostnames:
- orders.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: orders-service
port: 8080
验证时不要只访问首页:
kubectl get gateway -n gateway-infra public-gateway -o yaml
kubectl get httproute -n app-prod orders -o yaml
kubectl describe gateway -n gateway-infra public-gateway
kubectl describe httproute -n app-prod orders
再使用指定 Host 的请求验证 DNS 切换前的入口:
curl -v --resolve orders.example.com:80:<GATEWAY_IP> \
http://orders.example.com/api/health
五、TLS与HTTP跳转
TLS终止通常属于 Gateway 监听器,由平台团队管理证书引用:
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-example-com
HTTP跳转应显式定义 Route,而不是假设存在默认行为:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-to-https
namespace: app-prod
spec:
parentRefs:
- name: public-gateway
namespace: gateway-infra
sectionName: http
hostnames: ["orders.example.com"]
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
port: 443
statusCode: 301
如果证书 Secret 与 Gateway 不在同一命名空间,应使用实现支持的跨命名空间方式并配置 ReferenceGrant。不要为了省事给控制器开放读取所有 Secret 的权限。
六、流量切分与灰度发布
backendRefs.weight 可表达权重路由:
rules:
- backendRefs:
- name: orders-v1
port: 8080
weight: 90
- name: orders-v2
port: 8080
weight: 10
权重不是精确的逐请求计数,短时间、小样本或长连接下比例可能明显偏离。灰度验收应按足够长时间窗口观察请求量、错误率、延迟和业务指标,并确认控制器对会话保持、HTTP/2连接和重试的处理方式。
生产灰度建议按以下顺序:
- 新版本保持零生产流量,先做就绪探测和内部验证;
- 切入 1%~5% 流量,观察技术和业务指标;
- 逐步提升到 10%、25%、50%和100%;
- 每一级设置最短观察时间与停止条件;
- 回滚只调整权重,不在事故期间同时修改镜像、路由和证书。
七、跨命名空间引用
跨命名空间是权限边界,不应默认放开。若 app-prod 的 HTTPRoute 需要引用 shared-services 中的 Service,应由目标命名空间创建授权:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-orders-route
namespace: shared-services
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: app-prod
to:
- group: ""
kind: Service
name: shared-api
授权应尽量限定来源命名空间、资源类型和目标名称。定期清理已经不存在的 Route 对应授权,避免长期积累隐形访问路径。
八、从Ingress分阶段迁移
阶段1:并行准备
- 安装 Gateway API CRD 和目标控制器;
- 保留原 Ingress 控制器与现有 DNS;
- 用测试域名验证入口、证书和日志;
- 对照注解映射表补齐能力差异。
阶段2:影子验证
- 使用临时域名或
curl --resolve访问新入口; - 对相同接口执行功能、性能和安全验证;
- 对比响应头、客户端IP、超时、上传大小和错误页;
- 检查 Route 状态与控制器日志。
阶段3:小流量切换
- 降低 DNS TTL,但不要在切换瞬间才修改;
- 先迁移低风险域名或少量流量;
- 保留旧入口地址和完整回滚步骤;
- 观察 DNS缓存、连接排空和证书握手。
阶段4:完成与清理
- 生产验证通过后再移除旧 Ingress;
- 清理无用注解、负载均衡器和安全组;
- 更新架构图、值班手册和故障演练;
- 保留迁移记录和关键指标对比。
九、观测与故障定位
建议为入口建立四层证据:
| 层级 | 关键证据 |
|---|---|
| API对象 | Accepted、Programmed、ResolvedRefs、事件 |
| 控制器 | 配置解析、下发失败、证书与权限错误 |
| 数据平面 | 请求量、状态码、连接、TLS、上游延迟 |
| 业务服务 | EndpointSlice、健康检查、应用错误和依赖 |
常用检查:
kubectl get gateway,httproute -A
kubectl get gateway -A -o wide
kubectl describe httproute -n app-prod orders
kubectl get endpointslice -n app-prod -l kubernetes.io/service-name=orders-service
kubectl get events -A --sort-by=.lastTimestamp
典型问题包括:
Accepted=False:监听器不允许该命名空间或主机名不匹配;ResolvedRefs=False:Service、端口、Secret或跨命名空间授权错误;- Gateway有地址但无法访问:安全组、防火墙、负载均衡健康检查或数据平面异常;
- 返回404:Host、路径、监听器和Route附着关系不一致;
- 返回503:后端无可用Endpoint、端口错误或就绪探测失败;
- TLS失败:证书链、SAN、Secret格式或引用权限错误。
十、回滚设计
迁移前必须写明回滚触发器:错误率、P99、TLS失败率、关键业务成功率或入口可用性达到什么阈值时回滚。回滚动作应单一且可预测:
- 停止继续扩大流量;
- 将 DNS 或上游负载均衡指回旧入口;
- 保留新资源,不在事故期间立即删除;
- 等待连接和 DNS缓存收敛;
- 比较两条入口的日志和指标,确认根因后再重试。
如果迁移期间同时改变证书、域名、应用版本和网络策略,回滚将很难定位问题。一次迁移尽量只改变入口控制面。
十一、上线检查清单
- [ ] 已完成所有 Ingress 注解和特殊行为清单;
- [ ] 目标控制器支持所需资源和功能等级;
- [ ] Gateway与Route状态条件全部符合预期;
- [ ] HTTP、HTTPS、重定向、上传、WebSocket和gRPC均已验证;
- [ ] TLS证书续期、私钥权限和到期告警已验证;
- [ ] 跨命名空间引用使用最小范围ReferenceGrant;
- [ ] 灰度权重、观察窗口、停止条件和回滚均已演练;
- [ ] 入口指标、访问日志、控制器日志和业务监控能够关联;
- [ ] DNS TTL、旧入口保留时间和连接排空策略明确;
- [ ] 值班手册记录常见状态条件与排障命令。