一、目标:从 Ingress 迁移到可分工的 Gateway API
本文在 Kubernetes 上使用 Cilium 实现 Gateway API,完成 GatewayClass、共享 Gateway、HTTPRoute、TLS、跨命名空间授权、权重灰度和 Hubble 排障。Gateway API 把基础设施所有者、集群运营者和应用开发者的职责拆开,比单一 Ingress 注解更易治理。
Internet -> LoadBalancer -> Gateway (infra namespace)
|-- HTTPS listener
|-- HTTP -> HTTPS redirect
|
+----------------+----------------+
v v
HTTPRoute shop HTTPRoute api
90% stable / 10% canary header routing
| |
Services / Pods <---- Cilium policy --+
|
Hubble flows
二、版本与前置检查
Gateway API 与 Cilium 支持矩阵会变化,先记录 Kubernetes、Cilium、Gateway API CRD 和内核状态。不要直接把示例版本当作生产固定值;以官方兼容矩阵选定并冻结。
kubectl version
cilium version
cilium status --wait
kubectl get crd gateways.gateway.networking.k8s.io \
httproutes.gateway.networking.k8s.io
kubectl get gatewayclass
若尚未安装 Gateway API CRD,使用官方 Standard channel 清单并校验来源。当前官方入门文档给出 v1.6.1 示例;上线时应再次核对。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.1/standard-install.yaml
kubectl wait --for=condition=Established crd/gateways.gateway.networking.k8s.io --timeout=120s
三、启用 Cilium Gateway API
Helm 值名称必须与所用 Cilium 版本一致。安装前保存当前值和 release manifest,升级采用 --atomic。
helm get values cilium -n kube-system -o yaml > cilium-values-before.yaml
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--reuse-values \
--set gatewayAPI.enabled=true \
--atomic --timeout 10m
cilium status --wait
kubectl -n kube-system get pods -l k8s-app=cilium -o wide
kubectl get gatewayclass -o yaml
kubectl describe gatewayclass cilium
GatewayClass Accepted=True 才能继续。若控制器未识别 CRD,检查 CRD 版本、Cilium 配置和 operator 日志,而不是反复重建 Gateway。
四、部署后端与命名空间
将基础设施和应用分离。示例创建 gateway-system、shop、api,服务端口使用具名端口,探针确保只向 ready Pod 路由。
apiVersion: v1
kind: Namespace
metadata: {name: gateway-system}
---
apiVersion: v1
kind: Namespace
metadata: {name: shop}
labels:
shared-gateway-access: "true"
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: shop-stable, namespace: shop}
spec:
replicas: 3
selector: {matchLabels: {app: shop, track: stable}}
template:
metadata: {labels: {app: shop, track: stable}}
spec:
containers:
- name: app
image: registry.example/shop:1.4.0
ports: [{name: http, containerPort: 8080}]
readinessProbe:
httpGet: {path: /readyz, port: http}
periodSeconds: 5
为 stable 和 canary 分别创建 Service,避免 selector 混淆版本。
五、TLS Secret 与共享 Gateway
Listener 与证书边界
证书 Secret 必须与 Gateway 同命名空间。生产使用 cert-manager 或受控证书流程;示例不提交私钥。
kubectl -n gateway-system create secret tls shop-example-tls \
--cert=tls.crt --key=tls.key --dry-run=client -o yaml | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public-gateway
namespace: gateway-system
spec:
gatewayClassName: cilium
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: shop-example-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
kubectl apply -f gateway.yaml
kubectl -n gateway-system get gateway public-gateway -w
kubectl -n gateway-system describe gateway public-gateway
关注 Accepted、Programmed、listener 的 ResolvedRefs。只有对象存在不代表数据面已就绪。
六、HTTP 到 HTTPS 重定向
重定向单独建立 HTTPRoute 并只绑定 http listener,避免影响 HTTPS 业务规则。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: redirect-https, namespace: shop}
spec:
parentRefs:
- name: public-gateway
namespace: gateway-system
sectionName: http
hostnames: ["shop.example.com"]
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
kubectl apply -f redirect.yaml
curl -I http://shop.example.com/catalog
验收 Location、状态码和查询参数是否保留。
七、主路由与权重灰度
用足够样本验证权重
将 stable/canary 权重设置为 90/10。权重是相对值,不保证小样本严格比例;需要足够请求量并按响应 release 标识统计。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: shop, namespace: shop}
spec:
parentRefs:
- name: public-gateway
namespace: gateway-system
sectionName: https
hostnames: ["shop.example.com"]
rules:
- matches:
- path: {type: PathPrefix, value: /}
backendRefs:
- name: shop-stable
port: 80
weight: 90
- name: shop-canary
port: 80
weight: 10
kubectl -n shop get httproute shop -o yaml
kubectl -n shop describe httproute shop
for i in $(seq 1 200); do curl -sk https://shop.example.com/version; done | sort | uniq -c
发布阈值要由错误率、P99 和业务指标共同决定,不能只看流量比例。
八、基于 Header 的精确测试流量
在权重灰度前,可让带测试 Header 的请求全部进入 canary,其余继续 stable。规则按匹配优先级设计,避免 header 被公网任意用户滥用;边缘层应剥离外部伪造头。
rules:
- matches:
- headers:
- name: x-release-track
type: Exact
value: canary
backendRefs:
- {name: shop-canary, port: 80}
- backendRefs:
- {name: shop-stable, port: 80}
curl -sk -H 'x-release-track: canary' https://shop.example.com/version
curl -sk https://shop.example.com/version
九、跨命名空间后端与 ReferenceGrant
当 HTTPRoute 引用另一个命名空间的 Service,目标命名空间必须显式创建 ReferenceGrant。授权应精确到来源 group/kind/namespace 和目标 kind,不能使用宽泛授权替代治理。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata: {name: allow-shop-route, namespace: api}
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: shop
to:
- group: ""
kind: Service
name: checkout-api
backendRefs:
- name: checkout-api
namespace: api
port: 8080
缺少 ReferenceGrant 时 ResolvedRefs=False 是正确的安全行为。不要把它当作控制器 bug。
十、CiliumNetworkPolicy 最小流量
Gateway 路由成功不等于网络策略自动放行。先观察实际源身份,再编写只允许网关数据面访问应用端口的策略。
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: {name: shop-ingress, namespace: shop}
spec:
endpointSelector:
matchLabels: {app: shop}
ingress:
- fromEntities: [ingress]
toPorts:
- ports:
- {port: "8080", protocol: TCP}
不同 Cilium 版本和部署模式下网关身份表达可能不同,必须先用 Hubble 验证,而不是盲抄 selector。
十一、用 Hubble 分层排障
先判断控制面,再观察数据面
按 DNS/TCP/TLS/HTTP/后端顺序定位。Hubble 能看到流量是否被策略丢弃、目标身份和 L7 状态。
cilium hubble enable
hubble status
hubble observe --namespace shop --protocol http --since 10m
hubble observe --verdict DROPPED --since 10m
hubble observe --to-pod shop/shop-stable-xxxxx --follow
kubectl -n gateway-system get gateway public-gateway -o jsonpath='{.status.addresses}'
kubectl -n shop get httproute shop -o jsonpath='{.status.parents[*].conditions}'
kubectl -n shop get endpointslice -l kubernetes.io/service-name=shop-stable
curl -vk --resolve shop.example.com:443:GATEWAY_IP https://shop.example.com/readyz
典型映射:Gateway 未 Programmed 检查控制器和地址;Route 未 Accepted 检查 parentRef/hostname/listener;ResolvedRefs=False 检查 Service/端口/ReferenceGrant;503 检查 EndpointSlice 和 readiness;timeout 且 Hubble DROPPED 检查策略。
十二、观测、容量与高可用
监控 Gateway 请求数、4xx/5xx、上游连接失败、P95/P99、TLS 握手、每后端权重实际比例、Cilium dropped flows、Envoy/Cilium 内存和连接数。压测必须包含长连接、HTTP/2 和异常上游。
hey -z 5m -c 100 https://shop.example.com/catalog
kubectl -n gateway-system get pods -o wide
kubectl -n kube-system top pods -l k8s-app=cilium
确认 Gateway 对应数据面有足够副本、PodDisruptionBudget 和跨节点分布。云 LoadBalancer 的健康检查与客户端源 IP 语义也要验证。
十三、发布与回滚
先部署 canary Service,再创建 header 路由,验证后从 1%/5%/10% 逐级提高。每级持续一个足够覆盖峰值的窗口。回滚只需把 canary 权重设为 0 或删除精确匹配规则,不应先删除 canary Pod,否则进行中请求会中断。
kubectl -n shop patch httproute shop --type=merge -p '{"spec":{"rules":[{"backendRefs":[{"name":"shop-stable","port":80,"weight":100},{"name":"shop-canary","port":80,"weight":0}]}]}}'
kubectl -n shop rollout status deploy/shop-stable --timeout=180s
变更 GatewayClass、CRD 或 Cilium 版本前导出所有 Gateway API 对象,并验证升级/回滚兼容性。
十四、官方资料与验收
- Kubernetes Gateway API Introduction、API Types 与规范。
- Gateway API HTTP routing、TLS、traffic splitting、ReferenceGrant 指南。
- Cilium Gateway API 与 Hubble 官方文档。
- Gateway API Conformance 报告:核对 Cilium 版本实际支持特性。
验收必须证明:TLS 链正确;HTTP 重定向正确;Route 全部 Accepted/ResolvedRefs/Programmed;跨命名空间只有显式授权可达;灰度比例与指标一致;策略拒绝可由 Hubble 定位;移除 canary 不影响 stable;所有变更可由声明式清单复现。