如何在Google Kubernetes Engine中通过网关与负载均衡实现流量拆分
流量拆分至两个后端Pod的配置解决方案
问题背景
需要将集群中的流量均匀拆分至两个后端Pod,目前已完成集群内Pod及内部服务部署,其他部分运行正常,但无法实现gRPC-Client(Node.js后端)与Redis Pub(Golang后端)的流量均匀分配。尝试过Ingress、网关、Nginx Service等配置均未奏效,需可行的实现方案。
期望实现行为
- 前端通过
http://some-ip:3000/访问API(IP无需静态,端口可调整) - 客户端API请求流量平均分配至gRPC-client后端与Redis-pub后端
现有后端Pod配置
gRPC后端配置
#gRPC apiVersion: apps/v1 kind: Deployment metadata: name: grpc-deployment labels: app: grpc namespace: so1 spec: selector: matchLabels: app: grpc replicas: 1 template: metadata: labels: app: grpc spec: containers: - name: grpc-server image: 'gcr.io/so1-proyecto-383722/grpc_server:latest' ports: - containerPort: 50051 - name: grpc-client image: 'gcr.io/so1-proyecto-383722/grpc_client:latest' ports: - containerPort: 50061 --- #gRPC Server Service apiVersion: v1 kind: Service metadata: name: grpc-service labels: app: grpc namespace: so1 spec: selector: app: grpc type: ClusterIP ports: - name: grpc port: 50051 targetPort: 50051 --- #gRPC Client Service (For Traffict Split) apiVersion: v1 kind: Service metadata: name: input-grpc-service labels: app: input-service namespace: so1 spec: selector: app: grpc type: ClusterIP ports: - name: grpc port: 3000 #3000 targetPort: 50061
Redis后端配置
#Redis Pub Sub apiVersion: apps/v1 kind: Deployment metadata: name: redis-pub-sub-deployment labels: app: redis-pub-sub namespace: so1 spec: selector: matchLabels: app: redis-pub-sub replicas: 1 template: metadata: labels: app: redis-pub-sub spec: containers: - name: redis-pub image: 'gcr.io/so1-proyecto-383722/redispub:latest' ports: - containerPort: 11000 - name: redis-sub image: 'gcr.io/so1-proyecto-383722/redissub:latest' --- #Redis Pub Service (For Traffict Split) apiVersion: v1 kind: Service metadata: name: input-redis-service labels: app: input-service namespace: so1 spec: selector: app: redis-pub-sub type: ClusterIP ports: - name: redis-pub-sub port: 3000 #3000 targetPort: 11000
可行实现方案
方案1:使用Kubernetes Gateway API(原生流量拆分)
首先确保集群已安装Gateway API CRDs,然后创建以下资源:
1. 创建GatewayClass和Gateway(暴露端口3000)
apiVersion: gateway.networking.k8s.io/v1beta1 kind: GatewayClass metadata: name: example-gateway-class spec: controllerName: gateway.example.com/controller # 根据实际使用的控制器调整,如nginx.org/gateway-controller --- apiVersion: gateway.networking.k8s.io/v1beta1 kind: Gateway metadata: name: traffic-split-gateway namespace: so1 spec: gatewayClassName: example-gateway-class listeners: - name: http port: 3000 protocol: HTTP allowedRoutes: namespaces: from: Same
2. 创建HTTPRoute和TrafficSplit实现50/50流量拆分
apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: api-route namespace: so1 spec: parentRefs: - name: traffic-split-gateway rules: - backendRefs: - name: input-service-split kind: Service port: 3000 --- apiVersion: split.smi-spec.io/v1alpha2 kind: TrafficSplit metadata: name: input-service-split namespace: so1 spec: service: input-service-split # 虚拟服务名称 backends: - service: input-grpc-service weight: 50 - service: input-redis-service weight: 50
注意:需要先安装SMI(Service Mesh Interface)的TrafficSplit CRD,部分网关控制器支持直接在HTTPRoute的backendRefs中配置weight字段实现分流。
方案2:使用Nginx Ingress Controller实现流量拆分
如果集群已部署Nginx Ingress Controller,可通过Ingress注解实现流量拆分:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: traffic-split-ingress namespace: so1 annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "50" spec: ingressClassName: nginx rules: - http: paths: - path: / pathType: Prefix backend: service: name: input-grpc-service port: number: 3000 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: traffic-split-ingress-canary namespace: so1 annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "50" spec: ingressClassName: nginx rules: - http: paths: - path: / pathType: Prefix backend: service: name: input-redis-service port: number: 3000
这种方式通过两个Ingress资源配合canary权重实现50/50分流,需确保Nginx Ingress Controller版本支持canary权重功能。
方案3:自定义简易流量转发服务(适合快速验证)
如果不想依赖复杂的网关或服务网格,可以写一个简单的转发服务,以50%概率将请求转发到两个后端,再将该服务暴露为NodePort或LoadBalancer:
示例Node.js转发服务(server.js):
const express = require('express'); const httpProxy = require('http-proxy'); const app = express(); const proxy = httpProxy.createProxyServer({}); const backendServices = [ 'http://input-grpc-service.so1.svc.cluster.local:3000', 'http://input-redis-service.so1.svc.cluster.local:3000' ]; app.all('*', (req, res) => { // 随机选择一个后端 const target = backendServices[Math.floor(Math.random() * backendServices.length)]; proxy.web(req, res, { target }); }); app.listen(3000, () => { console.log('Traffic forwarder running on port 3000'); });
将该服务打包成镜像后,部署为Deployment和NodePort Service:
apiVersion: apps/v1 kind: Deployment metadata: name: traffic-forwarder namespace: so1 spec: replicas: 1 selector: matchLabels: app: traffic-forwarder template: metadata: labels: app: traffic-forwarder spec: containers: - name: forwarder image: your-registry/traffic-forwarder:latest ports: - containerPort: 3000 --- apiVersion: v1 kind: Service metadata: name: traffic-forwarder-service namespace: so1 spec: selector: app: traffic-forwarder type: NodePort ports: - port: 3000 targetPort: 3000 nodePort: 30000 # 可选,指定端口
前端即可通过NodePort的IP和对应端口访问,实现流量随机均分。
内容的提问来源于stack exchange,提问作者Brian Matus
相关产品推荐
相关产品推荐

