Using Envoy AI Gateway to Route Large Model Inference Traffic
Envoy AI Gateway is a cloud-native traffic gateway add-on designed for large language models (LLMs) and AI applications. Built on the high-performance Envoy proxy, it serves as a data plane component for AI traffic, providing unified traffic routing, load balancing, and traffic governance for AI inference services in Kubernetes clusters.
With this add-on, you can seamlessly proxy and manage API requests to heterogeneous large models (such as OpenAI, ERNIE Bot, Qwen, and locally deployed open-source models), improving the reliability and governance efficiency of AI application architectures.
This section provides an example of using the AI Gateway to route traffic based on model names.
How It Works
Envoy AI Gateway uses a control plane–data plane architecture:
- Control plane:
- Includes the Envoy Gateway Controller and AI Gateway Controller.
- Listens for AI Gateway resources (such as AIGatewayRoute) submitted by users and generates configurations.
- Communicates with Envoy via the Extension Server protocol, fine-tunes xDS configuration before delivery, and injects AI-specific routing logic.
- Data plane:
- The core component is the Envoy proxy.
- Typically deployed in sidecar mode or as an independent gateway.
- The built-in AI Gateway ExtProc (External Processing) handles AI-specific service logic (such as KV cache routing and multi-model load balancing).
Prerequisites
- A cluster of v1.32 or later is available.
- Envoy Gateway is installed in the current cluster with AI Inference Gateway enabled.
Procedure
If the node cannot access the Internet, replace the node image with an appropriate custom image.
- Obtain and deploy the simulated vLLM model (Llama3-8b).
# vLLM simulation backend wget https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/v1.0.1/config/manifests/vllm/sim-deployment.yaml # InferenceObjective wget https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.0.1/config/manifests/inferenceobjective.yaml # InferencePool resources wget https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/v1.0.1/config/manifests/inferencepool-resources.yaml # Apply all resources. kubectl apply -f .
- Obtain and deploy the simulated Mistral model.
- Create mistral-inference-deploy.yaml.
apiVersion: v1 kind: Service metadata: name: mistral-upstream namespace: default spec: selector: app: mistral-upstream ports: - protocol: TCP port: 8080 targetPort: 8080 # The headless service allows the IP addresses of the pods to be resolved via the Service DNS. clusterIP: None --- apiVersion: apps/v1 kind: Deployment metadata: name: mistral-upstream namespace: default spec: replicas: 3 selector: matchLabels: app: mistral-upstream template: metadata: labels: app: mistral-upstream spec: containers: - name: testupstream image: docker.io/envoyproxy/ai-gateway-testupstream:latest imagePullPolicy: IfNotPresent ports: - containerPort: 8080 env: - name: TESTUPSTREAM_ID value: test readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 1 periodSeconds: 1 --- apiVersion: inference.networking.k8s.io/v1 kind: InferencePool metadata: name: mistral namespace: default spec: targetPorts: - number: 8080 selector: matchLabels: app: mistral-upstream endpointPickerRef: name: mistral-epp port: number: 9002 --- apiVersion: inference.networking.x-k8s.io/v1alpha2 kind: InferenceObjective metadata: name: mistral namespace: default spec: priority: 10 poolRef: # Bind the InferenceObjective to the InferencePool. name: mistral --- apiVersion: v1 kind: Service metadata: name: mistral-epp namespace: default spec: selector: app: mistral-epp ports: - protocol: TCP port: 9002 targetPort: 9002 appProtocol: http2 type: ClusterIP --- apiVersion: v1 kind: ServiceAccount metadata: name: mistral-epp namespace: default --- apiVersion: apps/v1 kind: Deployment metadata: name: mistral-epp namespace: default labels: app: mistral-epp spec: replicas: 1 selector: matchLabels: app: mistral-epp template: metadata: labels: app: mistral-epp spec: serviceAccountName: mistral-epp # Conservatively, this timeout should mirror the longest grace period of the pods within the pool terminationGracePeriodSeconds: 130 containers: - name: epp image: registry.k8s.io/gateway-api-inference-extension/epp:v1.0.1 imagePullPolicy: IfNotPresent args: - --pool-name - "mistral" - "--pool-namespace" - "default" - --v - "4" - --zap-encoder - "json" - --grpc-port - "9002" - --grpc-health-port - "9003" - "--config-file" - "/config/default-plugins.yaml" ports: - containerPort: 9002 - containerPort: 9003 - name: metrics containerPort: 9090 livenessProbe: grpc: port: 9003 service: inference-extension initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: grpc: port: 9003 service: inference-extension initialDelaySeconds: 5 periodSeconds: 10 volumeMounts: - name: plugins-config-volume mountPath: "/config" volumes: - name: plugins-config-volume configMap: name: plugins-config --- apiVersion: v1 kind: ConfigMap metadata: name: plugins-config namespace: default data: default-plugins.yaml: | apiVersion: inference.networking.x-k8s.io/v1alpha1 kind: EndpointPickerConfig plugins: - type: queue-scorer - type: kv-cache-utilization-scorer - type: prefix-cache-scorer schedulingProfiles: - name: default plugins: - pluginRef: queue-scorer - pluginRef: kv-cache-utilization-scorer - pluginRef: prefix-cache-scorer --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pod-read namespace: default rules: - apiGroups: ["inference.networking.x-k8s.io"] resources: ["inferenceobjectives", "inferencepools"] verbs: ["get", "watch", "list"] - apiGroups: ["inference.networking.k8s.io"] resources: ["inferencepools"] verbs: ["get", "watch", "list"] - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pod-read-binding namespace: default subjects: - kind: ServiceAccount name: mistral-epp namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: pod-read --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auth-reviewer rules: - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auth-reviewer-binding subjects: - kind: ServiceAccount name: mistral-epp namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: auth-reviewer - Deploy the Mistral simulation service.
kubectl apply -f mistral-inference-deploy.yaml
- Create mistral-inference-deploy.yaml.
- Obtain and use an AIServiceBackend to deploy a traditional backend.
- Create backend-deployment.yaml.
apiVersion: aigateway.envoyproxy.io/v1alpha1 kind: AIServiceBackend metadata: name: envoy-ai-gateway-basic-testupstream namespace: default spec: schema: name: OpenAI backendRef: name: envoy-ai-gateway-basic-testupstream kind: Backend group: gateway.envoyproxy.io --- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: Backend metadata: name: envoy-ai-gateway-basic-testupstream namespace: default spec: endpoints: - fqdn: hostname: envoy-ai-gateway-basic-testupstream.default.svc.cluster.local port: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: envoy-ai-gateway-basic-testupstream namespace: default spec: replicas: 1 selector: matchLabels: app: envoy-ai-gateway-basic-testupstream template: metadata: labels: app: envoy-ai-gateway-basic-testupstream spec: containers: - name: testupstream image: docker.io/envoyproxy/ai-gateway-testupstream:latest imagePullPolicy: IfNotPresent ports: - containerPort: 8080 env: - name: TESTUPSTREAM_ID value: test readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 1 periodSeconds: 1 --- apiVersion: v1 kind: Service metadata: name: envoy-ai-gateway-basic-testupstream namespace: default spec: selector: app: envoy-ai-gateway-basic-testupstream ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP - Deploy traditional backend services.
kubectl apply -f backend-deployment.yaml
- Create backend-deployment.yaml.
- Deploy a Gateway.
- Create ai-gateway-config.yaml.
apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: name: nodeport-config namespace: envoy-gateway-system spec: provider: type: Kubernetes kubernetes: envoyService: type: NodePort envoyDeployment: container: image: docker.io/envoyproxy/envoy:distroless-dev --- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: inference-pool-with-aigwroute spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: group: gateway.envoyproxy.io kind: EnvoyProxy name: nodeport-config namespace: envoy-gateway-system --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: inference-pool-with-aigwroute namespace: default spec: gatewayClassName: inference-pool-with-aigwroute listeners: - name: http protocol: HTTP port: 80 --- apiVersion: aigateway.envoyproxy.io/v1alpha1 kind: AIGatewayRoute metadata: name: inference-pool-with-aigwroute namespace: default spec: parentRefs: - name: inference-pool-with-aigwroute kind: Gateway group: gateway.networking.k8s.io rules: # Route for vLLM Llama model via InferencePool - matches: - headers: - type: Exact name: x-ai-eg-model value: meta-llama/Llama-3.1-8B-Instruct backendRefs: - group: inference.networking.k8s.io kind: InferencePool name: vllm-llama3-8b-instruct # Route for Mistral model via InferencePool - matches: - headers: - type: Exact name: x-ai-eg-model value: mistral:latest backendRefs: - group: inference.networking.k8s.io kind: InferencePool name: mistral # Route for traditional backend (non-InferencePool) - matches: - headers: - type: Exact name: x-ai-eg-model value: some-cool-self-hosted-model backendRefs: - name: envoy-ai-gateway-basic-testupstream - Deploy the Gateway.
kubectl apply -f ai-gateway-config.yaml
- Create ai-gateway-config.yaml.
- Verify the deployment.
- Access the cluster console. In the navigation pane, choose Workloads. In the right pane, click the Deployments tab and check whether all workloads are Running.
- On the Services tab, verify that the required Services have been created.
On this page, obtain and record the access address and NodePort of the corresponding Service, and combine them in the format [access-address]:[NodePort]. In all subsequent tests, replace $GATEWAY_IP in the configuration with this address.

- Test the Gateway's capability of routing requests to different models or backends.
- Test the Llama-3 model route.
Verify route forwarding from the Gateway to Llama-3.
curl -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-3.1-8B-Instruct", "messages": [ { "role": "user", "content": "Hi. Say this is a test" } ] }' \ http://$GATEWAY_IP/v1/chat/completionsIf information similar to the following is returned, the model is working:
{"choices":[{"finish_reason":"stop","index":0,"message":{"content":"The temperature there is twenty-five degrees centigrade. Give a man a fish and you feed him for a day; Teach a man to fish",role":"assistant"}}],"created":1767755896,"do_remote_decode":false,"do_remote_prefill":false,"id":"chatcmp-561ca69e-9716-411f-9656-7a96d9******","model":"meta-llama/llama-3.1-8B-Instruct","object":"chat.completion","remote_block_id":"","remote_engine_id":"","remote_host":"","remote_port":0,"usage":{"completion_tokens":28,"prompt_tokens":7,"total_tokens":35}, - Test the Mistral model route.
Verify route forwarding from the Gateway to Mistral.
curl -H "Content-Type: application/json" \ -d '{ "model": "mistral:latest", "messages": [ { "role": "user", "content": "Hi. Say this is a test" } ] }' \ http://$GATEWAY_IP/v1/chat/completionsIf information similar to the following is returned, the model is working:
{"choices":[{"message":{"content":"This is a test.","role":"assistant"}}]} - Test the common backend load balancer route.
Verify route forwarding from the Gateway to the custom backend load balancer.
curl -H "Content-Type: application/json" \ -d '{ "model": "some-cool-self-hosted-model", "messages": [ { "role": "user", "content": "Hi. Say this is a test" } ] }' \ http://$GATEWAY_IP/v1/chat/completionsIf information similar to the following is returned, the model is working:
{"choices":[{"message":{"role":"assistant","content":"I am the captain of my soul."}}]}
- Test the Llama-3 model route.
Feedback
Was this page helpful?
Provide feedbackThank you very much for your feedback. We will continue working to improve the documentation.See the reply and handling status in My Cloud VOC.
For any further questions, feel free to contact us through the chatbot.
Chatbot