Gateway API 实战:在单节点 Kubernetes 上使用 Envoy Gateway 跑通 HTTPRoute、Header 匹配和金丝雀分流
一、前言
本文会在一个单节点 Kubernetes 测试环境中安装 Envoy Gateway,然后通过 Gateway API 完成几组 HTTP 路由实验:
1. 安装 Envoy Gateway 2. 创建 GatewayClass 3. 部署 app-v1 / app-v2 两个测试服务 4. 创建 Gateway 和 HTTPRoute 5. 验证基础路由:/ -> app-v1 6. 验证 Path 路由:/api -> app-v1,/web -> app-v2 7. 验证 Header 匹配:带 x-env: canary 的请求 -> app-v2 8. 验证 backendRefs.weight 金丝雀分流:app-v1 90%,app-v2 10%二、实验环境与整体链路
本文实验环境是一个单节点 Kubernetes 测试集群。实验中使用的主要资源如下:
Gateway Controller:Envoy Gateway Envoy Gateway 安装命名空间:envoy-gateway-system 测试业务命名空间:gateway-demo GatewayClass:eg Gateway:app-gateway 测试域名:app.example.local 后端服务: - app-v1 - app-v2由于本文是单节点 Kubernetes 测试环境,没有云厂商 LoadBalancer,也没有安装 MetalLB,所以 Envoy Gateway 创建出来的LoadBalancer类型 Service 的EXTERNAL-IP会是<pending>。
因此本文使用kubectl port-forward的方式在本机验证 Gateway API 的转发链路。
最终请求链路如下:
curl -> kubectl port-forward -> Envoy Gateway 数据面 Service -> Envoy Proxy 数据面 Pod -> Gateway -> HTTPRoute -> Service -> Pod三、安装 Envoy Gateway
3.1 拉取 Helm Chart
首先拉取 Envoy Gateway 的 Helm Chart:
helm pull oci://docker.io/envoyproxy/gateway-helm --version v1.8.1如果网络可以正常访问 Docker Hub,这一步会在当前目录生成类似下面的文件:
gateway-helm-v1.8.1.tgz如果测试机无法访问 Docker Hub,也可以在能访问外网的机器上下载后再上传到测试机。
3.2 使用 Helm 安装 Envoy Gateway
执行安装命令:
helm install envoygateway ./gateway-helm-v1.8.1.tgz \ -n envoy-gateway-system \ --create-namespace这里需要注意两个名字:
envoygateway 是 Helm release 名称。 envoy-gateway-system 是 Envoy Gateway 安装所在的命名空间。Helm release 名称和后面创建的 GatewayClass 名称不是一回事。
也就是说,这里的envoygateway只是 Helm 管理这次安装的 release 名字;后面我们创建的GatewayClass eg是 Gateway API 里的资源名字。
3.3 验证 Envoy Gateway Pod
执行:
kubectl -n envoy-gateway-system get pod实验输出如下:
NAME READY STATUS RESTARTS AGE envoy-gateway-6f954cd9dd-49zm7 1/1 Running 0 26s envoygateway-gateway-helm-certgen-z9xzq 0/1 Completed 0 30s这里有两个对象需要理解:
envoy-gateway-xxx Envoy Gateway Controller,状态 Running,说明控制平面正常运行。 envoygateway-gateway-helm-certgen-xxx Helm 安装过程中用于生成证书的 Job,Completed 是正常状态。只要envoy-gateway-xxx是1/1 Running,说明 Envoy Gateway 控制平面已经正常起来了。
3.4 验证 Gateway API CRD
执行:
kubectl get crd | grep gateway.networking.k8s.io实验输出如下:
backendtlspolicies.gateway.networking.k8s.io gatewayclasses.gateway.networking.k8s.io gateways.gateway.networking.k8s.io grpcroutes.gateway.networking.k8s.io httproutes.gateway.networking.k8s.io listenersets.gateway.networking.k8s.io referencegrants.gateway.networking.k8s.io tcproutes.gateway.networking.k8s.io tlsroutes.gateway.networking.k8s.io udproutes.gateway.networking.k8s.io这说明 Gateway API 相关 CRD 已经安装到集群中。
对于 Gateway API 来说,CRD 非常关键。只有 CRD 存在,Kubernetes 才认识下面这些资源:
GatewayClass Gateway HTTPRoute ReferenceGrant GRPCRoute TCPRoute TLSRoute UDPRoute本文主要使用这三个核心资源:
GatewayClass Gateway HTTPRoute四、创建 GatewayClass
4.1 GatewayClass 的作用
GatewayClass是集群级资源,用来表示一类 Gateway 由哪个 Gateway Controller 处理。
可以这样理解:
GatewayClass = 选择使用哪种 Gateway Controller本文使用 Envoy Gateway,所以 GatewayClass 的controllerName写成:
gateway.envoyproxy.io/gatewayclass-controller这是 Envoy Gateway 对应的 controllerName。
4.2 创建 GatewayClass YAML
创建文件:
cat > 00-gatewayclass.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller EOF应用:
kubectl apply -f 00-gatewayclass.yaml检查:
kubectl get gatewayclass实验输出:
NAME CONTROLLER ACCEPTED AGE eg gateway.envoyproxy.io/gatewayclass-controller True 5s这里重点看:
ACCEPTED=True这说明 Envoy Gateway Controller 已经接受了这个 GatewayClass。后续 Gateway 里写:
gatewayClassName: eg就表示这个 Gateway 交给 Envoy Gateway 处理。
五、部署 app-v1 / app-v2 测试服务
为了隔离实验资源,先创建一个测试命名空间。后续 Deployment、Service、Gateway、HTTPRoute 都放在这个命名空间中。
kubectl create ns gateway-demo为了测试不同路由规则,这里部署两个简单 HTTP 服务:
app-v1 返回:hello from app-v1 app-v2 返回:hello from app-v25.1 创建测试应用 YAML
创建文件:
cat > 01-apps.yaml <<'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: app-v1 namespace: gateway-demo spec: replicas: 1 selector: matchLabels: app: app-v1 template: metadata: labels: app: app-v1 spec: tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" containers: - name: app image: hashicorp/http-echo:1.0 args: - "-text=hello from app-v1" ports: - containerPort: 5678 --- apiVersion: v1 kind: Service metadata: name: app-v1 namespace: gateway-demo spec: selector: app: app-v1 ports: - name: http port: 8080 targetPort: 5678 --- apiVersion: apps/v1 kind: Deployment metadata: name: app-v2 namespace: gateway-demo spec: replicas: 1 selector: matchLabels: app: app-v2 template: metadata: labels: app: app-v2 spec: tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" containers: - name: app image: hashicorp/http-echo:1.0 args: - "-text=hello from app-v2" ports: - containerPort: 5678 --- apiVersion: v1 kind: Service metadata: name: app-v2 namespace: gateway-demo spec: selector: app: app-v2 ports: - name: http port: 8080 targetPort: 5678 EOF5.2 应用测试服务
执行:
kubectl apply -f 01-apps.yaml检查 Deployment、Pod、Service:
kubectl -n gateway-demo get deploy,pod,svc实验输出:
NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/app-v1 1/1 1 1 15s deployment.apps/app-v2 1/1 1 1 15s NAME READY STATUS RESTARTS AGE pod/app-v1-6dbdb7dc7-947vw 1/1 Running 0 15s pod/app-v2-6f5444c457-7l25x 1/1 Running 0 15s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/app-v1 ClusterIP 10.103.180.185 <none> 8080/TCP 15s service/app-v2 ClusterIP 10.96.61.62 <none> 8080/TCP 15s继续检查后端端点:
kubectl -n gateway-demo get endpoints实验输出:
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice NAME ENDPOINTS AGE app-v1 10.244.0.252:5678 15s app-v2 10.244.0.253:5678 15s六、创建 Gateway
6.1 Gateway 的作用
Gateway用来描述具体的入口配置,例如:
使用哪个 GatewayClass 监听哪个端口 使用什么协议 匹配哪个 hostname 允许哪些 Route 绑定本文创建一个名为app-gateway的 Gateway,监听 HTTP 80 端口,并匹配app.example.local。
6.2 创建 Gateway YAML
创建文件:
cat > 02-gateway.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: app-gateway namespace: gateway-demo spec: gatewayClassName: eg listeners: - name: http port: 80 protocol: HTTP hostname: "app.example.local" allowedRoutes: namespaces: from: Same EOF应用:
kubectl apply -f 02-gateway.yaml6.3 Gateway 字段解释
gatewayClassName: eg表示这个 Gateway 使用前面创建的 GatewayClasseg。
也就是交给 Envoy Gateway Controller 处理。
listeners: - name: http定义一个 listener,名字叫http。后面的 HTTPRoute 可以通过:
sectionName: http精确绑定到这个 listener。
port: 80 protocol: HTTP表示这个 listener 处理 HTTP 80 端口流量。
hostname: "app.example.local"表示这个 listener 只匹配 Host 为app.example.local的请求。
allowedRoutes: namespaces: from: Same表示只允许和 Gateway 同 namespace 的 Route 绑定。因为这个 Gateway 在gateway-demo命名空间,所以只有gateway-demo命名空间里的 HTTPRoute 可以绑定到它。
6.4 检查 Gateway
执行:
kubectl -n gateway-demo get gateway实验输出:
NAME CLASS ADDRESS PROGRAMMED AGE app-gateway eg False 2m1s这里可以看到:
ADDRESS 为空 PROGRAMMED=False这不一定表示 Gateway YAML 写错。继续查看详情:
kubectl -n gateway-demo describe gateway app-gateway关键状态如下:
Reason: Accepted Status: True Type: Accepted Reason: AddressNotAssigned Status: False Type: Programmed这说明:
Gateway 已经被 Envoy Gateway 接受。 但是 Gateway 没有分配到外部地址。由于本文是单节点 Kubernetes,没有云厂商 LoadBalancer,也没有 MetalLB,所以 Envoy Gateway 创建出来的 LoadBalancer Service 无法获得 EXTERNAL-IP。
因此这里的Programmed=False是因为:
Reason: AddressNotAssigned Message: No addresses have been assigned to the Gateway这不是 Gateway YAML 错误。再看 listener 状态:
Listeners: Attached Routes: 0 Programmed=True Accepted=True ResolvedRefs=True这说明:
Gateway 的 listener 配置已经成功翻译并发送到数据面。 只是当前还没有 HTTPRoute 绑定到这个 listener。七、创建基础 HTTPRoute:/ 转发到 app-v1
7.1 HTTPRoute 的作用
HTTPRoute用来描述 HTTP 请求如何匹配,以及匹配后转发到哪个后端 Service。
可以简单理解为:
Gateway 负责入口。 HTTPRoute 负责路由规则。 Service 负责后端访问入口。 Pod 才是真正处理请求的应用实例。本文先创建一个最简单的 HTTPRoute:
app.example.local/ -> app-v1:80807.2 创建基础 HTTPRoute YAML
创建文件:
cat > 03-route-basic.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: app-route-basic namespace: gateway-demo spec: parentRefs: - name: app-gateway sectionName: http hostnames: - "app.example.local" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: app-v1 port: 8080 EOF应用:
kubectl apply -f 03-route-basic.yaml7.3 HTTPRoute 字段解释
parentRefs: - name: app-gateway sectionName: http表示这条 HTTPRoute 绑定到:
Gateway app-gateway 的 http listener这里的sectionName: http对应 Gateway 中 listener 的名字:
listeners: - name: httphostnames: - "app.example.local"表示这条 HTTPRoute 只匹配 Host 为app.example.local的请求。
matches: - path: type: PathPrefix value: /表示匹配所有以/开头的路径。
backendRefs: - name: app-v1 port: 8080表示匹配成功后,转发到app-v1这个 Service 的 8080 端口。
7.4 检查 HTTPRoute
执行:
kubectl -n gateway-demo get httproute实验输出:
NAME HOSTNAMES AGE app-route-basic ["app.example.local"] 24s继续查看详情:
kubectl -n gateway-demo describe httproute app-route-basic重点看:
Reason: Accepted Status: True Type: Accepted Reason: ResolvedRefs Status: True Type: ResolvedRefs这说明:
Accepted=True HTTPRoute 已经成功绑定到 Gateway。 ResolvedRefs=True HTTPRoute 引用的后端 Service 已经成功解析。也就是说,app-route-basic已经成功绑定到app-gateway,并且能够找到后端app-v1:8080。
7.5 查看 Gateway 上绑定的 Route 数量
执行:
kubectl -n gateway-demo describe gateway app-gateway | grep Attached实验输出:
Attached Routes: 1说明app-route-basic已经成功挂到app-gateway的httplistener 上。
八、理解 Envoy Gateway 控制面和数据面
创建 Gateway 之后,再查看 Envoy Gateway 命名空间中的 Pod:
kubectl -n envoy-gateway-system get pod实验输出:
NAME READY STATUS RESTARTS AGE envoy-gateway-6f954cd9dd-49zm7 1/1 Running 0 68m envoy-gateway-demo-app-gateway-c2617110-5df694555c-smmfb 2/2 Running 0 83s这里很多初学者容易迷惑:为什么多了一个envoy-gateway-demo-app-gateway开头的 Pod?
这不是重复安装了 Envoy Gateway,而是控制面和数据面的区别。
8.1envoy-gateway-xxx是控制面
envoy-gateway-6f954cd9dd-49zm7这个 Pod 是 Helm 安装时创建的 Envoy Gateway Controller。它的职责是:
监听 GatewayClass 监听 Gateway 监听 HTTPRoute 监听 Service / EndpointSlice 把 Gateway API 资源翻译成 Envoy 配置 创建和管理 Envoy 数据面 Deployment / Service它本身一般不直接承接业务 HTTP 请求。
8.2envoy-gateway-demo-app-gateway-xxx是数据面
envoy-gateway-demo-app-gateway-c2617110-5df694555c-smmfb这个 Pod 是 Envoy Gateway 根据我们创建的Gateway app-gateway自动创建出来的数据面 Envoy Proxy Pod。可以拆开理解这个名字:
envoy gateway-demo app-gateway c2617110 随机 Pod 后缀其中:
gateway-demo 是 Gateway 所在 namespace app-gateway 是 Gateway 名字所以看到这个名字,就可以判断:
这是 gateway-demo 命名空间里的 app-gateway 对应的数据面 Envoy Pod。真正处理业务 HTTP 请求的是这个数据面 Envoy Pod。
8.3 是否每创建一个 Gateway 都会创建一个新 Pod
默认情况下,Envoy Gateway 通常会为每个 Gateway 创建一套独立的 Envoy Proxy 数据面资源。
也就是说,创建一个 Gateway,Envoy Gateway Controller 监听到它后,默认会创建一套对应的数据面资源,例如:
Deployment Pod Service不过 Envoy Gateway 也支持合并模式,可以把多个 Gateway 的 listener 合并到同一套 Envoy Proxy fleet 中。本文没有配置合并模式,所以使用的是默认模式。
可以简单记住:
默认模式: 一个 Gateway -> 一套独立 Envoy 数据面 合并模式: 多个 Gateway -> 可以合并到一套 Envoy 数据面8.4 控制面 Service 和数据面 Service 的区别
执行:
kubectl -n envoy-gateway-system get svc可以看到类似:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) envoy-gateway ClusterIP 10.97.106.78 <none> 18000/TCP,18001/TCP,18002/TCP,19001/TCP,9443/TCP envoy-gateway-demo-app-gateway-c2617110 LoadBalancer 10.99.157.94 <pending> 80:31738/TCP这里也有两个 Service:
envoy-gateway 控制面 Service,给 Envoy Gateway Controller 自己使用。 envoy-gateway-demo-app-gateway-c2617110 数据面 Service,给外部流量进入 Gateway 使用。业务流量应该打到数据面 Service,而不是控制面 Service。所以后面 port-forward 的对象是:
service/envoy-gateway-demo-app-gateway-c2617110而不是:
service/envoy-gateway九、使用 port-forward 验证基础路由
9.1 为什么需要 port-forward
由于本文是单节点 Kubernetes,没有云厂商 LoadBalancer,也没有安装 MetalLB,所以数据面 Service 的EXTERNAL-IP是:
<pending>这表示外部地址没有分配成功。为了在本机验证 Gateway API 转发链路,可以使用kubectl port-forward。port-forward可以理解成:
在本机端口和 Kubernetes 集群内部某个 Pod/Service 端口之间建立一条临时转发通道。9.2 执行 port-forward
先设置变量:
export ENVOY_SERVICE=envoy-gateway-demo-app-gateway-c2617110执行端口转发:
kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80这条命令的含义是:
把当前机器的 127.0.0.1:8888 转发到 envoy-gateway-system 命名空间下 envoy-gateway-demo-app-gateway-c2617110 这个 Service 的 80 端口。格式可以理解为:
本地端口:目标端口所以:
8888:80表示:
本机 8888 -> 集群内目标 Service/Pod 的 80该命令会一直占用当前终端。不要关闭这个终端。
9.3 curl 验证基础路由
重新开一个终端,执行:
curl -s -H "Host: app.example.local" http://127.0.0.1:8888/实验输出:
hello from app-v1这说明请求已经成功到达app-v1。这里必须带:
-H "Host: app.example.local"原因是 Gateway 和 HTTPRoute 都配置了:
hostname: "app.example.local" hostnames: - "app.example.local"如果直接访问:
curl http://127.0.0.1:8888/请求里的 Host 会是:
127.0.0.1:8888它不匹配app.example.local,路由就可能不会命中。
9.4 基础路由链路
基础路由成功后,请求链路如下:
curl -H "Host: app.example.local" http://127.0.0.1:8888/ | v kubectl port-forward | v Envoy Gateway 数据面 Service | v Envoy 数据面 Pod | | Envoy 根据 Gateway listener 和 HTTPRoute 规则匹配请求 v Service app-v1:8080 | v app-v1 Pod:5678 | v hello from app-v1十、实验一:Path 路由
基础路由已经跑通后,开始测试 Path 路由。目标:
/api -> app-v1 /web -> app-v2为了避免多个 HTTPRoute 同时存在导致测试结果不清晰,先删除基础 Route:
kubectl -n gateway-demo delete httproute app-route-basic10.1 创建 Path 路由 YAML
创建文件:
cat > 04-route-path.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: app-route-path namespace: gateway-demo spec: parentRefs: - name: app-gateway sectionName: http hostnames: - "app.example.local" rules: - matches: - path: type: PathPrefix value: /api backendRefs: - name: app-v1 port: 8080 - matches: - path: type: PathPrefix value: /web backendRefs: - name: app-v2 port: 8080 EOF应用:
kubectl apply -f 04-route-path.yaml检查:
kubectl -n gateway-demo get httproute kubectl -n gateway-demo describe httproute app-route-path10.2 测试 Path 路由
测试/api:
curl -s -H "Host: app.example.local" http://127.0.0.1:8888/api输出:
hello from app-v1测试/web:
curl -s -H "Host: app.example.local" http://127.0.0.1:8888/web输出:
hello from app-v2说明:
/api 成功转发到 app-v1 /web 成功转发到 app-v2这里使用的是:
type: PathPrefix表示按路径前缀匹配。例如:
/api /api/ /api/v1都属于/api前缀。
十一、实验二:Header 匹配
接下来测试基于请求头的匹配。目标:
普通请求 -> app-v1 带 x-env: canary 的请求 -> app-v2这类能力可以用于测试版本访问、灰度验证等场景。先删除上一条 Path Route:
kubectl -n gateway-demo delete httproute app-route-path11.1 创建 Header 匹配 YAML
创建文件:
cat > 05-route-header.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: app-route-header namespace: gateway-demo spec: parentRefs: - name: app-gateway sectionName: http hostnames: - "app.example.local" rules: - matches: - path: type: PathPrefix value: / headers: - type: Exact name: x-env value: canary backendRefs: - name: app-v2 port: 8080 - matches: - path: type: PathPrefix value: / backendRefs: - name: app-v1 port: 8080 EOF这里把带 Header 的规则写在前面,把普通默认规则写在后面,便于理解和排查。带 x-env: canary 的请求会匹配第一条规则,普通请求会匹配后面的默认规则。
应用:
kubectl apply -f 05-route-header.yaml检查:
kubectl -n gateway-demo get httproute kubectl -n gateway-demo describe httproute app-route-header11.2 测试普通请求
执行:
curl -s -H "Host: app.example.local" http://127.0.0.1:8888/输出:
hello from app-v1普通请求没有带x-env: canary,所以命中第二条规则,转发到app-v1。
11.3 测试带 Header 的请求
执行:
curl -s -H "Host: app.example.local" \ -H "x-env: canary" \ http://127.0.0.1:8888/输出:
hello from app-v2这说明带有:
x-env: canary的请求命中了第一条规则,并被转发到app-v2。这里需要注意:
同一个 matches 中的 path 和 headers 是共同匹配条件。也就是说,请求需要同时满足:
PathPrefix / Header x-env=canary才会命中第一条规则。
十二、实验三:金丝雀分流
最后测试基于权重的流量分配。目标:
app-v1 90% app-v2 10%先删除 Header Route:
kubectl -n gateway-demo delete httproute app-route-header12.1 创建金丝雀分流 YAML
创建文件:
cat > 06-route-canary.yaml <<'EOF' apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: app-route-canary namespace: gateway-demo spec: parentRefs: - name: app-gateway sectionName: http hostnames: - "app.example.local" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: app-v1 port: 8080 weight: 90 - name: app-v2 port: 8080 weight: 10 EOF应用:
kubectl apply -f 06-route-canary.yaml检查:
kubectl -n gateway-demo get httproute kubectl -n gateway-demo describe httproute app-route-canary12.2 测试金丝雀分流
执行 50 次请求:
for i in {1..50}; do curl -s -H "Host: app.example.local" http://127.0.0.1:8888/ done | sort | uniq -c实验输出:
45 hello from app-v1 5 hello from app-v2这说明流量大致按照 90/10 分到了app-v1和app-v2。这里要注意,weight是相对权重,不是必须加起来等于 100。例如:
app-v1 weight=90 app-v2 weight=10和:
app-v1 weight=9 app-v2 weight=1表达的比例都近似是:
app-v1 90% app-v2 10%另外,请求次数较少时,结果不一定严格等于 90/10。如果想让统计结果更稳定,可以把请求次数增加到 200 次:
for i in {1..200}; do curl -s -H "Host: app.example.local" http://127.0.0.1:8888/ done | sort | uniq -c十三、常见问题总结
13.1 为什么 curl 必须加 Host header
因为 Gateway 里配置了:
hostname: "app.example.local"HTTPRoute 里配置了:
hostnames: - "app.example.local"所以 curl 测试时要带:
-H "Host: app.example.local"否则请求 Host 是:
127.0.0.1:8888不会匹配app.example.local。
13.2 为什么 port-forward 到数据面 Service,不是 envoy-gateway Service
Envoy Gateway 安装后会有控制面 Service:
envoy-gateway创建 Gateway 后又会出现数据面 Service:
envoy-gateway-demo-app-gateway-c2617110它们的作用不同:
envoy-gateway 是控制面 Service,给 Envoy Gateway Controller 自己使用。 envoy-gateway-demo-app-gateway-c2617110 是数据面 Service,给外部流量进入 Gateway 使用。业务流量应该进入数据面 Service,而不是控制面 Service。所以正确的 port-forward 是:
kubectl -n envoy-gateway-system port-forward service/envoy-gateway-demo-app-gateway-c2617110 8888:80十四、总结
本文在单节点 Kubernetes 测试环境中完成了 Envoy Gateway 和 Gateway API 的基础实战。
完成的内容包括:
1. 使用 Helm 安装 Envoy Gateway 2. 检查 Gateway API CRD 3. 创建 GatewayClass 4. 部署 app-v1 / app-v2 两个测试服务 5. 创建 Gateway 6. 创建基础 HTTPRoute 7. 使用 port-forward 本机访问 Envoy Gateway 数据面 8. 验证基础路由:/ -> app-v1 9. 验证 Path 路由:/api -> app-v1,/web -> app-v2 10. 验证 Header 匹配:x-env: canary -> app-v2 11. 验证金丝雀分流:app-v1 90%,app-v2 10%通过这次实验,可以把 Gateway API 的核心链路串起来:
GatewayClass -> Gateway -> HTTPRoute -> Service -> Pod也可以把 Envoy Gateway 的控制面和数据面关系理清楚:
envoy-gateway-xxx -> 控制面 Controller,负责监听 Gateway API 资源并管理 Envoy 配置 envoy-gateway-demo-app-gateway-xxx -> 数据面 Envoy Proxy,负责真正接收和转发业务流量最终请求链路如下:
真实流量链路: curl -> port-forward -> Envoy 数据面 Service -> Envoy 数据面 Pod -> Service -> Pod 配置匹配逻辑: Gateway listener -> HTTPRoute host/path/header 匹配 -> backendRefs 选择后端 Service这篇文章只是 Gateway API 的普通 HTTP 服务实验。后续可以继续把后端app-v1/app-v2替换成 vLLM 模型服务,通过 Gateway API 暴露/v1/chat/completions接口。
