Cilium Gateway API 高级动手实验室¶
目标:通过实验理解 Cilium Gateway API 的入口流量、HTTPRoute、Header 修改、Mirror、Rewrite、Redirect、跨命名空间、gRPC、内部 L7 流量管理和灰度。
1. Lab说明¶
1.1 一次性前置初始化(推荐直接复制执行)¶
下面这段适用于本实验使用的 Cilium v1.17.0。它先安装 Gateway API CRD 和 Cilium EnvoyConfig CRD,再用 Cilium CLI 开启 Gateway API、EnvoyConfig、kube-proxy replacement 和裸机 L2 宣告。
set -euo pipefail
export CILIUM_VERSION="1.17.0"
export GATEWAY_API_VERSION="v1.4.1"
# Gateway API 标准/实验 CRD
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/standard/gateway.networking.k8s.io_gateways.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/${GATEWAY_API_VERSION}/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml"
# Cilium Gateway controller 需要的 EnvoyConfig CRD
kubectl apply -f "https://raw.githubusercontent.com/cilium/cilium/v${CILIUM_VERSION}/pkg/k8s/apis/cilium.io/client/crds/v2/ciliumenvoyconfigs.yaml"
kubectl apply -f "https://raw.githubusercontent.com/cilium/cilium/v${CILIUM_VERSION}/pkg/k8s/apis/cilium.io/client/crds/v2/ciliumclusterwideenvoyconfigs.yaml"
# 如果 GatewayClass 是手工创建的,先让 Helm release 可以接管它。
if kubectl get gatewayclass cilium >/dev/null 2>&1; then
kubectl label gatewayclass cilium \
app.kubernetes.io/managed-by=Helm --overwrite
kubectl annotate gatewayclass cilium \
meta.helm.sh/release-name=cilium \
meta.helm.sh/release-namespace=kube-system --overwrite
fi
# 必须通过 Cilium CLI 持久化,避免后续 upgrade 覆盖手工 ConfigMap/RBAC 修改。
if kubectl -n kube-system get daemonset/cilium >/dev/null 2>&1; then
cilium upgrade \
--namespace kube-system \
--version "${CILIUM_VERSION}" \
--reuse-values \
--set kubeProxyReplacement=true \
--set gatewayAPI.enabled=true \
--set envoyConfig.enabled=true \
--set l2announcements.enabled=true \
--set-string gatewayAPI.gatewayClass.create=true
else
cilium install \
--namespace kube-system \
--version "${CILIUM_VERSION}" \
--set kubeProxyReplacement=true \
--set gatewayAPI.enabled=true \
--set envoyConfig.enabled=true \
--set l2announcements.enabled=true \
--set-string gatewayAPI.gatewayClass.create=true
fi
kubectl -n kube-system rollout status deployment/cilium-operator --timeout=180s
kubectl -n kube-system rollout status daemonset/cilium --timeout=300s
kubectl wait --for=condition=Accepted=True gatewayclass/cilium --timeout=120s
kubectl get gatewayclass cilium
上面的脚本会自动判断使用
cilium install还是cilium upgrade。gatewayAPI.gatewayClass.create在 Ciliumv1.17.0Chart 中是字符串值,所以必须使用--set-string。
1.2 裸机 LoadBalancer 地址和 L2 宣告¶
Gateway 默认会创建 LoadBalancer Service。裸机集群必须同时准备 LB IPPool 和 L2 Announcement,否则集群内可能能访问 VIP,但集群外主机不会收到 ARP 响应。
下面示例精确提供 192.168.5.200-192.168.5.215。不能写成单个 192.168.5.200/28,因为 /28 会归一化为 192.168.5.192/28。
kubectl apply -f - <<'EOF'
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: lab-lb-pool
spec:
blocks:
- cidr: "192.168.5.200/29"
- cidr: "192.168.5.208/29"
---
apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
name: lab-lb-l2
spec:
serviceSelector:
matchLabels:
app: web
nodeSelector:
matchLabels:
kubernetes.io/os: linux
interfaces:
- "^ens18$"
loadBalancerIPs: true
---
apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
name: gw-lab-l2
spec:
serviceSelector:
matchLabels:
gateway.networking.k8s.io/gateway-name: cilium-gw
nodeSelector:
matchLabels:
kubernetes.io/os: linux
interfaces:
- "^ens18$"
loadBalancerIPs: true
EOF
kubectl get ciliumloadbalancerippool
kubectl get ciliuml2announcementpolicy
gw-lab-l2必须单独存在:Cilium 自动生成的 Gateway Service 标签是gateway.networking.k8s.io/gateway-name=cilium-gw,不是app=web。如果只配置lab-lb-l2,Gateway 状态可以是Programmed=True,但集群外仍可能无法访问它的 VIP。
1.3 高级网关 API 使用案例¶
本实验要掌握的核心链路:
外部客户端
-> Cilium Gateway API
-> Cilium Envoy
-> HTTPRoute / GRPCRoute 规则
-> Kubernetes Service
-> 后端 Pod
Cilium Gateway API 和传统 ingress-nginx 的关键区别:
ingress-nginx:
外部流量 -> nginx ingress controller Pod -> Service -> Pod
Cilium Gateway API:
外部流量 -> Cilium 管理的 Gateway/Envoy -> Service -> Pod
Cilium 官方文档要求 Gateway API 至少满足:
- Cilium 开启
kubeProxyReplacement=true。 - Cilium 开启 Gateway API controller。
- L7 proxy 开启,通常默认是开启的。
- Gateway API CRD 已安装。
- 默认暴露模式会创建
LoadBalancer类型 Service,所以裸金属环境需要 LB IPAM + L2 Announcements/BGP,或者使用 host network 模式。
先检查版本:
检查关键配置:
预期至少看到类似:
如果没有启用 Gateway API,不要只执行 cilium config set 或直接 patch cilium-config。请重新执行上面的“一次性前置初始化”,让 Cilium CLI 同时更新 ConfigMap、RBAC、EnvoyConfig 和 L2 Announcement 配置;否则下一次 cilium upgrade 可能会把手工修改覆盖掉。
如果 kube-proxy-replacement 不是 true,不要在生产直接改。这个属于 Cilium 安装级变更,建议在测试集群重装或按官方升级/迁移文档规划。
上面的前置初始化已经安装了与 Cilium v1.17.0 配套的 Gateway API CRD 和 EnvoyConfig CRD。
验证 CRD:
验证 Cilium GatewayClass:
如果没有 cilium 这个 GatewayClass,继续看:
kubectl -n kube-system logs deployment/cilium-operator --tail=200
kubectl -n kube-system logs daemonset/cilium --tail=200
cilium config view | grep gateway
创建实验命名空间(可重复执行):
kubectl create namespace gw-lab --dry-run=client -o yaml | kubectl apply -f -
kubectl label namespace gw-lab purpose=gw-lab --overwrite
启动 Hubble 观察。后续每个实验都建议开一个观察窗口:
2. 负载均衡器¶
2.1 部署应用程序¶
理论点:
- Gateway API 默认通过
LoadBalancerService 暴露 Gateway。 - Cilium Gateway 收到请求后转发到后端 Service。
- 后端应用最好能回显请求头、路径和 Pod 名,方便验证路由行为。
部署两个 echo 后端。这里不直接依赖外部实验文件,方便你复制执行。
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-1
labels:
app: echo-1
spec:
replicas: 2
selector:
matchLabels:
app: echo-1
template:
metadata:
labels:
app: echo-1
version: v1
spec:
containers:
- name: echo
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/k8s.gcr.io/echoserver:1.9
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-1
labels:
app: echo-1
spec:
selector:
app: echo-1
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-2
labels:
app: echo-2
spec:
replicas: 2
selector:
matchLabels:
app: echo-2
template:
metadata:
labels:
app: echo-2
version: v2
spec:
containers:
- name: echo
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/k8s.gcr.io/echoserver:1.9
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-2
labels:
app: echo-2
spec:
selector:
app: echo-2
ports:
- name: http
port: 8090
targetPort: 8080
EOF
检查:
kubectl -n gw-lab rollout status deployment/echo-1 --timeout=180s
kubectl -n gw-lab rollout status deployment/echo-2 --timeout=180s
kubectl -n gw-lab get pods,svc -o wide
本实验统一使用当前环境可拉取的 echoserver 镜像。不要改 Service 名和端口,后续实验会引用。
2.2 部署 Gateway 和 HTTPRoute¶
部署一个基础 Gateway 和 HTTPRoute:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cilium-gw
spec:
gatewayClassName: cilium
listeners:
- name: web
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-basic
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /echo
backendRefs:
- name: echo-1
port: 8080
EOF
检查 Gateway 状态:
kubectl -n gw-lab get gateway,httproute,svc
kubectl -n gw-lab describe gateway cilium-gw
kubectl -n gw-lab describe httproute echo-basic
取 Gateway 地址:
kubectl -n gw-lab wait --for=condition=Programmed=True gateway/cilium-gw --timeout=180s
GATEWAY="$(kubectl -n gw-lab get gateway cilium-gw -o jsonpath='{.status.addresses[0].value}')"
echo "${GATEWAY}"
如果 GATEWAY 为空,说明 Gateway 没拿到外部地址。裸金属环境先检查:
kubectl -n gw-lab get svc | grep cilium-gateway
kubectl get ciliumloadbalancerippool
kubectl get ciliuml2announcementpolicy
kubectl get ciliumbgpclusterconfig
有地址后访问:
kubectl -n gw-lab get svc cilium-gateway-cilium-gw -o wide
curl -sS --max-time 5 "http://${GATEWAY}/echo" | head -40
如果客户端位于集群外,Gateway VIP 还必须有 ARP 宣告:
kubectl -n gw-lab get svc cilium-gateway-cilium-gw --show-labels
kubectl get ciliuml2announcementpolicy gw-lab-l2 -o yaml
kubectl -n kube-system get lease | grep cilium-l2announce
Hubble 观察:
结论:
Gateway是入口监听器。HTTPRoute是路由规则。- Gateway 获得的外部地址通常来自
LoadBalancerService。 - Hubble 可以看到请求落到哪个后端 Pod,但不会直接告诉你 HTTPRoute 为什么选中这条规则,需要结合
kubectl describe httproute看 Conditions。
3. HTTP 标头请求修饰符¶
3.1 部署 HTTPRoute¶
理论点:
RequestHeaderModifier可以对进入后端之前的 HTTP 请求头做 add、set、remove。- 适合注入平台标识、灰度标识、请求追踪头。
- 这不是应用内逻辑,是 Gateway/Envoy 在入口层修改。
先部署一个添加请求头的路由:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: request-header-add
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /add-request-header
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-lab-added-by
value: cilium-gateway
backendRefs:
- name: echo-1
port: 8080
EOF
验证:
curl -sS --max-time 5 "http://${GATEWAY}/add-request-header" \
| grep -i -E 'x-lab-added-by|request headers' -A20
部署一个删除请求头的路由:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: request-header-remove
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /remove-request-header
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
remove:
- x-user-token
backendRefs:
- name: echo-1
port: 8080
EOF
验证:
curl -sS --max-time 5 \
-H 'x-user-token: secret-demo-value' \
"http://${GATEWAY}/remove-request-header" \
| grep -i 'x-user-token' || true
预期:后端输出里不应再看到 x-user-token。
部署一个覆盖请求头的路由:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: request-header-set
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /set-request-header
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- name: x-env
value: production
backendRefs:
- name: echo-1
port: 8080
EOF
验证:
curl -sS --max-time 5 \
-H 'x-env: test' \
"http://${GATEWAY}/set-request-header" \
| grep -i 'x-env'
预期:后端看到的 x-env 是 production。
3.2 可观测性¶
Hubble 观察 L7:
再访问:
你应该能看到 HTTP request/response 的 flow。
注意:
- Hubble 能看到 HTTP 请求路径、状态码等 L7 信息。
- Hubble 默认不一定显示所有请求头。是否记录 header 取决于 Cilium/Hubble/Envoy 观测配置。
- 请求头是否被修改,最直接的验证方式是看 echo 后端响应。
4. HTTP 响应标头重写¶
理论点:
ResponseHeaderModifier修改的是后端返回给客户端之前的响应头。- 常用于加安全头、删除内部头、统一响应标识。
部署:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: response-header-modifier
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /response-header
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: x-platform
value: cilium-gateway-api
set:
- name: x-frame-options
value: DENY
backendRefs:
- name: echo-1
port: 8080
EOF
验证响应头:
预期看到:
Hubble 观察:
结论:
- 请求头修改影响后端收到的 request。
- 响应头修改影响客户端收到的 response。
- 两者都是 Gateway API 的 HTTPRoute filter 能力。
5. HTTP 流量镜像¶
5.1 demo应用部署¶
理论点:
- Request Mirror 会把主请求复制一份给镜像后端。
- 客户端只接收主后端响应。
- 镜像后端适合做灰度观测、压测影子流量、兼容性验证。
确认 echo-1 和 echo-2 都存在:
5.2 部署镜像路由¶
部署镜像路由。主流量发给 echo-1,镜像流量发给 echo-2。
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: request-mirror
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /mirror
filters:
- type: RequestMirror
requestMirror:
backendRef:
name: echo-2
port: 8090
backendRefs:
- name: echo-1
port: 8080
EOF
验证客户端响应来自主后端:
for i in $(seq 1 5); do
curl -sS --max-time 5 "http://${GATEWAY}/mirror" | grep -E 'Hostname|host=|pod name' | head -3
done
这里只能看到 echo-1,这是正确结果。Request Mirror 不会把镜像后端的响应返回给客户端,客户端始终收到主后端 echo-1 的响应。
Hubble 观察镜像流量:
另一个终端触发:
验证主后端和镜像后端都收到 /mirror:
echo '--- primary echo-1 ---'
kubectl -n gw-lab logs -l app=echo-1 --since=2m --prefix=true \
| grep '/mirror' | tail -20 || true
echo '--- mirror echo-2 ---'
kubectl -n gw-lab logs -l app=echo-2 --since=2m --prefix=true \
| grep '/mirror' | tail -20 || true
预期两段日志都能看到:
如果只看 curl 输出,会误以为只有 echo-1,因为镜像请求的响应会被 Envoy 丢弃。
进一步按后端 Pod 观察:
kubectl -n gw-lab get pods -l app=echo-2
hubble observe --namespace gw-lab --to-service echo-2 --protocol http --follow
结论:
- Mirror 不改变客户端响应来源。
- Mirror 会增加后端请求量,生产使用前要确认镜像服务容量。
- 如果镜像后端失败,通常不应影响主请求,但仍要看具体实现和错误指标。
6. HTTP URL 重写¶
理论点:
URLRewrite可以在转发到后端前改写路径或 hostname。- 常用于把外部路径
/api改成应用内部路径/。 - Rewrite 是内部转发,不是让客户端重新请求。
部署:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: url-rewrite
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /rewrite
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /echo-rewritten
backendRefs:
- name: echo-1
port: 8080
EOF
验证:
curl -sS --max-time 5 "http://${GATEWAY}/rewrite/demo" \
| grep -E 'real path|request_uri|path|Request Information' -A10
预期:
- 客户端访问
/rewrite/demo。 - 后端看到的路径前缀变成
/echo-rewritten。
Hubble:
结论:
- Rewrite 对客户端透明。
- 如果你想让浏览器地址栏变化,用 Redirect,不用 Rewrite。
7. HTTP 流量重定向¶
7.1 部署流量重定向¶
理论点:
RequestRedirect返回 3xx 给客户端。- 客户端会重新请求新的路径、host 或 scheme。
- Redirect 和 Rewrite 最大区别:Redirect 会让客户端感知跳转。
部署基础 redirect:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-basic
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /redirect-basic
filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplaceFullPath
replaceFullPath: /echo
statusCode: 302
EOF
验证:
预期:
1. 请求:`http://${GATEWAY}/redirect-basic`
返回 302,跳转地址:`http://${GATEWAY}/echo`
2. 请求:`http://${GATEWAY}/redirect-basic/test/abc`
依然完整替换路径,跳转:`http://${GATEWAY}/echo`
7.2 重定向到新主机名和新前缀¶
部署:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-host-prefix
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /old-prefix
filters:
- type: RequestRedirect
requestRedirect:
hostname: new.example.local
path:
type: ReplacePrefixMatch
replacePrefixMatch: /new-prefix
statusCode: 302
EOF
验证:
预期:Location 指向 new.example.local/new-prefix/test。
7.3 重定向 - 新状态代码和新前缀¶
部署 301 永久跳转:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-301
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /permanent
filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /echo
statusCode: 301
EOF
验证:
7.4 重定向 - 从 HTTP 到 HTTPS 和新前缀¶
这里先只验证 HTTPRoute 的跳转响应,不要求你已经部署 HTTPS Gateway。
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-to-https
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /to-https
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
hostname: app.example.local
path:
type: ReplacePrefixMatch
replacePrefixMatch: /secure
statusCode: 302
EOF
验证:
结论:
RequestRedirect适合 HTTP 到 HTTPS、老域名到新域名、旧路径到新路径。- 生产中 301 会被客户端和浏览器缓存,灰度期间建议先用 302/307 验证。
8. 跨命名空间支持¶
8.1 ACME 的跨命名空间¶
理论点:
- Gateway API 默认重视命名空间边界。
- Route 是否能绑定 Gateway,由 Gateway listener 的
allowedRoutes控制。 - 跨命名空间引用后端 Service 时,需要目标命名空间创建
ReferenceGrant。 - ACME/cert-manager 场景经常涉及证书、Gateway、Route 跨 namespace 协作。
创建业务命名空间:
kubectl create namespace gw-app --dry-run=client -o yaml | kubectl apply -f -
kubectl label namespace gw-app purpose=gw-app --overwrite
部署后端到 gw-app:
kubectl -n gw-app apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: cross-echo
labels:
app: cross-echo
spec:
replicas: 1
selector:
matchLabels:
app: cross-echo
template:
metadata:
labels:
app: cross-echo
spec:
containers:
- name: echo
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/k8s.gcr.io/echoserver:1.9
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: cross-echo
spec:
selector:
app: cross-echo
ports:
- name: http
port: 8080
targetPort: 8080
EOF
8.2 跨命名空间网关¶
创建一个允许其它 namespace 绑定的 Gateway:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gw
spec:
gatewayClassName: cilium
listeners:
- name: web
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
EOF
在 gw-app 里创建 HTTPRoute,绑定 gw-lab/shared-gw:
kubectl -n gw-app apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: cross-route
spec:
parentRefs:
- name: shared-gw
namespace: gw-lab
rules:
- matches:
- path:
type: PathPrefix
value: /cross
backendRefs:
- name: cross-echo
port: 8080
EOF
验证:
kubectl -n gw-lab get gateway shared-gw
kubectl -n gw-app describe httproute cross-route
SHARED_GATEWAY="$(kubectl -n gw-lab get gateway shared-gw -o jsonpath='{.status.addresses[0].value}')"
echo "${SHARED_GATEWAY}"
curl -sS --max-time 5 "http://${SHARED_GATEWAY}/cross" | head -40
再做跨 namespace backendRef 实验:让 gw-lab 中的 Route 引用 gw-app 中的 Service。先创建 Route:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: route-to-gw-app-backend
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /cross-backend
backendRefs:
- name: cross-echo
namespace: gw-app
port: 8080
EOF
这时通常会因为缺少 ReferenceGrant 而不被接受。查看:
在目标 namespace gw-app 创建授权:
kubectl -n gw-app apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-gw-lab-route-to-service
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: gw-lab
to:
- group: ""
kind: Service
name: cross-echo
EOF
再次验证:
kubectl -n gw-lab describe httproute route-to-gw-app-backend
curl -sS --max-time 5 "http://${GATEWAY}/cross-backend" | head -40
结论:
allowedRoutes控制 Route 能不能绑定 Gateway。ReferenceGrant控制跨 namespace Route 能不能引用目标 Service。- 生产中建议运维管理 Gateway,业务团队管理各自 namespace 的 HTTPRoute。
9. 部署 gRPC 路由¶
9.1 部署基于 gRPC 的应用程序¶
理论点:
GRPCRoute是 Gateway API 中专门描述 gRPC 路由的资源。- gRPC 基于 HTTP/2。
- 如果通过 TLS Gateway 暴露 gRPC,需要关注 ALPN 和证书。
本实验使用官方示例思路,但为了降低复杂度,建议优先在测试环境安装 grpcurl:
如果没有:
curl -L -o /tmp/grpcurl.tar.gz https://github.com/fullstorydev/grpcurl/releases/download/v1.9.3/grpcurl_1.9.3_linux_x86_64.tar.gz
sudo tar -C /usr/local/bin -xzf /tmp/grpcurl.tar.gz grpcurl
grpcurl -version
部署 gRPC echo 服务。下面镜像如果拉取失败,请替换为你内部可用的 gRPC echo 镜像。
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: grpc-echo
labels:
app: grpc-echo
spec:
replicas: 1
selector:
matchLabels:
app: grpc-echo
template:
metadata:
labels:
app: grpc-echo
spec:
containers:
- name: grpc-echo
image: gcr.io/istio-testing/app:latest
ports:
- name: grpc
containerPort: 7070
---
apiVersion: v1
kind: Service
metadata:
name: grpc-echo
spec:
selector:
app: grpc-echo
ports:
- name: grpc
port: 7070
targetPort: 7070
appProtocol: kubernetes.io/h2c
EOF
检查:
kubectl -n gw-lab rollout status deployment/grpc-echo --timeout=180s
kubectl -n gw-lab get pods,svc -l app=grpc-echo
9.2 部署 gRPCRoute¶
这里部署明文 HTTP/2 gRPC 路由:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-route
spec:
parentRefs:
- name: cilium-gw
rules:
- backendRefs:
- name: grpc-echo
port: 7070
EOF
检查:
9.3 向后端服务发出 gRPC 请求¶
访问:
grpcurl -plaintext "${GATEWAY}:80" list || true
grpcurl -plaintext "${GATEWAY}:80" proto.EchoTestService/Echo || true
如果失败,排查:
kubectl -n gw-lab describe gateway cilium-gw
kubectl -n gw-lab describe grpcroute grpc-route
kubectl -n gw-lab get svc grpc-echo -o yaml
kubectl -n kube-system logs daemonset/cilium --tail=200 | grep -Ei 'grpc|http2|route|envoy' || true
hubble observe --namespace gw-lab --follow
注意:
- 官方 TLS gRPC 示例还涉及证书和 ALPN;如果你要做 HTTPS/gRPC,按你的 Cilium 版本确认
gatewayAPI.enableAlpn相关配置。 - gRPC 比 HTTP 更容易因为协议、TLS、ALPN、
appProtocol配置不一致而失败。
10. 内部L7 流量管理¶
10.1 部署示例应用程序¶
理论点:
- 北南向:外部客户端通过 Gateway 进集群。
- 东西向:集群内 client 访问 Service。
- Cilium 支持通过 Gateway API/GAMMA 思路让 HTTPRoute 绑定 Service,实现内部 L7 流量管理。
- Cilium 官方文档说明 GAMMA 当前主要支持 producer route,HTTPRoute 和被绑定 Service 在同一 namespace。
部署内部 client:
kubectl -n gw-lab run internal-client \
--image=curlimages/curl:8.8.0 \
--labels=app=internal-client \
-- sleep 365d
kubectl -n gw-lab get pod internal-client
先直接访问 Service:
kubectl -n gw-lab exec internal-client -- \
curl -sS --max-time 5 http://echo-1:8080/internal | head -40
10.2 部署东西 HTTPRoute¶
把 HTTPRoute 的 parentRefs 指向 Service,而不是 Gateway:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-1-service-route
spec:
parentRefs:
- group: ""
kind: Service
name: echo-1
port: 8080
rules:
- matches:
- path:
type: PathPrefix
value: /service-route
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-east-west-route
value: cilium-gamma
backendRefs:
- name: echo-1
port: 8080
EOF
检查:
10.3 验证东西向 L7 流量管理¶
访问:
kubectl -n gw-lab exec internal-client -- \
curl -sS --max-time 5 http://echo-1:8080/service-route \
| grep -i 'x-east-west-route' -A2
Hubble:
如果没有生效,检查:
cilium config view | grep -E 'gateway|envoy|l7|proxy'
kubectl -n gw-lab describe httproute echo-1-service-route
kubectl -n kube-system logs daemonset/cilium --tail=200 | grep -Ei 'gamma|httproute|envoy' || true
10.4 东西向负载均衡¶
创建一个统一 Service,选择两个版本:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: echo-all
spec:
selector:
purpose: east-west-lb
ports:
- name: http
port: 8080
targetPort: 8080
EOF
kubectl -n gw-lab label pod -l app=echo-1 purpose=east-west-lb --overwrite
kubectl -n gw-lab label pod -l app=echo-2 purpose=east-west-lb --overwrite
kubectl -n gw-lab get endpointslice -l kubernetes.io/service-name=echo-all -o wide
循环访问:
for i in $(seq 1 20); do
kubectl -n gw-lab exec internal-client -- \
curl -sS --max-time 5 http://echo-all:8080/lb \
| grep Hostname | head -1
done
结论:
- 这是 Kubernetes Service 层负载均衡。
- 如果需要 L7 权重灰度,用 HTTPRoute backendRefs 权重。
10.5 90/10 流量拆分¶
部署 Gateway 北南向 90/10 灰度:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: traffic-split-90-10
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /split
backendRefs:
- name: echo-1
port: 8080
weight: 90
- name: echo-2
port: 8090
weight: 10
EOF
验证比例:
rm -f /tmp/cilium-gw-split.txt
for i in $(seq 1 200); do
curl -sS --max-time 5 "http://${GATEWAY}/split" >> /tmp/cilium-gw-split.txt
done
grep -c 'Hostname: echo-1' /tmp/cilium-gw-split.txt || true
grep -c 'Hostname: echo-2' /tmp/cilium-gw-split.txt || true
Hubble 观察实际落点:
结论:
- Hubble 能看到请求实际去了哪个后端 Pod。
- Hubble 不会直接显示“这是 90/10 权重规则命中”,比例验证要靠后端响应或指标统计。
10.6 超时¶
理论点:
- Gateway API HTTPRoute 支持 timeout 字段的版本和 Cilium 支持情况要按你当前版本确认。
- 超时用于限制客户端请求总耗时或后端请求耗时。
- 如果你当前 CRD 不支持
timeouts字段,kubectl apply会失败或字段被拒绝。
先确认 CRD 是否支持:
如果支持,部署示例:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: timeout-demo
spec:
parentRefs:
- name: cilium-gw
rules:
- matches:
- path:
type: PathPrefix
value: /timeout
timeouts:
request: 5s
backendRequest: 2s
backendRefs:
- name: echo-1
port: 8080
EOF
验证:
kubectl -n gw-lab describe httproute timeout-demo
curl -i -sS --max-time 10 "http://${GATEWAY}/timeout" | head -30
如果你的 Gateway API CRD 不支持 timeouts,本章只记录理论,不强行实验。
10.7 小测验¶
回答下面问题,确保你真的掌握:
- Gateway 没有 ADDRESS,一定是 HTTPRoute 写错了吗?
- HTTPRoute Accepted=True,但请求 404,应该查哪些字段?
- 90/10 灰度比例应该用 Hubble 判断还是用后端响应统计判断?
RequestRedirect和URLRewrite哪个会让客户端看到 3xx?- 跨 namespace 引用后端 Service 时,
ReferenceGrant应该创建在哪个 namespace?
参考答案:
- 不一定,可能是 LB IPAM/L2/BGP/LoadBalancer 环境问题。
- 查
matches.path、hostnames、parentRefs、Gateway listener、后端 Service。 - Hubble 看实际链路,比例用后端响应或指标统计更准确。
RequestRedirect。- 目标 Service 所在 namespace。
11. 最终测试¶
11.1 题目¶
你要完成一个综合实验:
1. 创建 Gateway:gw-lab/final-gw
2. 暴露 HTTP 80
3. /final/v1 只转发到 echo-1
4. /final/canary 按 90/10 转发到 echo-1/echo-2
5. /final/rewrite/demo rewrite 到 /echo/demo
6. /final/redirect 返回 302,跳转到 /final/v1
7. 给 /final/header 注入请求头 x-final-lab: ok
8. 用 Hubble 观察实际落到哪个 Pod
9. 用 curl 循环统计灰度比例
11.2 解题¶
部署:
kubectl -n gw-lab apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: final-gw
spec:
gatewayClassName: cilium
listeners:
- name: web
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: final-route
spec:
parentRefs:
- name: final-gw
rules:
- matches:
- path:
type: PathPrefix
value: /final/v1
backendRefs:
- name: echo-1
port: 8080
- matches:
- path:
type: PathPrefix
value: /final/canary
backendRefs:
- name: echo-1
port: 8080
weight: 90
- name: echo-2
port: 8090
weight: 10
- matches:
- path:
type: PathPrefix
value: /final/rewrite
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /echo
backendRefs:
- name: echo-1
port: 8080
- matches:
- path:
type: PathPrefix
value: /final/redirect
filters:
- type: RequestRedirect
requestRedirect:
path:
type: ReplaceFullPath
replaceFullPath: /final/v1
statusCode: 302
- matches:
- path:
type: PathPrefix
value: /final/header
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-final-lab
value: ok
backendRefs:
- name: echo-1
port: 8080
EOF
取地址:
FINAL_GATEWAY="$(kubectl -n gw-lab get gateway final-gw -o jsonpath='{.status.addresses[0].value}')"
echo "${FINAL_GATEWAY}"
检查状态:
验证 /final/v1:
验证灰度:
rm -f /tmp/final-canary.txt
for i in $(seq 1 300); do
curl -sS --max-time 5 "http://${FINAL_GATEWAY}/final/canary" >> /tmp/final-canary.txt
done
echo "echo-1 count:"
grep -c 'Hostname: echo-1' /tmp/final-canary.txt || true
echo "echo-2 count:"
grep -c 'Hostname: echo-2' /tmp/final-canary.txt || true
验证 rewrite:
curl -sS --max-time 5 "http://${FINAL_GATEWAY}/final/rewrite/demo" \
| grep -E 'real path|request_uri|path|Request Information' -A10
验证 redirect:
验证 header:
Hubble 最终观察:
综合排障:
kubectl -n gw-lab get gateway,httproute,svc,endpointslice
kubectl -n gw-lab describe gateway final-gw
kubectl -n gw-lab describe httproute final-route
kubectl -n kube-system logs deployment/cilium-operator --tail=200
kubectl -n kube-system logs daemonset/cilium --tail=200
hubble observe --namespace gw-lab --verdict DROPPED --follow
12. 清理实验¶
如果你只清理本实验资源:
kubectl delete namespace gw-app --ignore-not-found
kubectl delete namespace gw-lab --ignore-not-found
如果你只是临时启用了 Gateway API,不建议在生产直接关闭。测试环境请通过 Cilium CLI 持久化关闭:
cilium upgrade \
--namespace kube-system \
--reuse-values \
--set gatewayAPI.enabled=false \
--set envoyConfig.enabled=false \
--set l2announcements.enabled=false
不要删除 Gateway API CRD,除非确认集群中没有任何 Gateway、HTTPRoute、GRPCRoute、ReferenceGrant、TLSRoute 资源。
13. 参考资料¶
- Cilium Gateway API Support:https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/
- Cilium HTTP Example:https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/http/
- Cilium HTTP Header Modifier Examples:https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/header/
- Cilium Traffic Splitting Example:https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/splitting/
- Cilium GAMMA Support:https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gamma/
- Cilium L7-Aware Traffic Management:https://docs.cilium.io/en/stable/network/servicemesh/l7-traffic-management/
- 原目录参考文章:https://jishuzhan.net/article/1933096962320019458