Help Center/ Cloud Container Engine/ User Guide/ Networking/ Gateway API/ Using Envoy AI Gateway to Route Large Model Inference Traffic
Updated on 2026-06-16 GMT+08:00

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.

  1. 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 .

  2. Obtain and deploy the simulated Mistral model.

    1. 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
    2. Deploy the Mistral simulation service.
      kubectl apply -f mistral-inference-deploy.yaml

  3. Obtain and use an AIServiceBackend to deploy a traditional backend.

    1. 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
    2. Deploy traditional backend services.
      kubectl apply -f backend-deployment.yaml

  4. Deploy a Gateway.

    1. 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
    2. Deploy the Gateway.
      kubectl apply -f ai-gateway-config.yaml

  5. Verify the deployment.

    1. 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.
    2. 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.

  6. Test the Gateway's capability of routing requests to different models or backends.

    1. 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/completions

      If 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}, 
    2. 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/completions

      If information similar to the following is returned, the model is working:

      {"choices":[{"message":{"content":"This is a test.","role":"assistant"}}]}
    3. 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/completions

      If information similar to the following is returned, the model is working:

      {"choices":[{"message":{"role":"assistant","content":"I am the captain of my soul."}}]}