Classic L7 load balancing assumes requests are cheap, short, and interchangeable. LLM traffic is none of those: requests run for seconds to minutes, cost varies 100x with token counts, and a replica's usefulness depends on its current KV-cache contents.
Tier: Advanced · Workflow area: Platform-for-AI: serving & gateways
The Gateway API Inference Extension teaches Kubernetes ingress about all of this — routing to model-server pools via an Endpoint Picker that reads live serving metrics. This playbook stands it up in front of the vLLM service from pb-43.
Skip this when you have one replica of one model — a plain Service suffices until you scale out — or when your traffic is mostly to external providers (that's pb-45's LiteLLM layer, which can sit behind or beside this).
GatewayClass, Gateway, HTTPRoute.Install the extension CRDs and your gateway. Follow your chosen implementation's install (Helm for kgateway/Envoy AI Gateway). Confirm the CRDs: kubectl get crd | grep inference — the v1 API centers on InferencePool (early alpha releases also had an InferenceModel CRD; it did not survive to v1, so treat older blog YAML with suspicion and match your installed bundle's API version).
Define the InferencePool. An InferencePool selects your model-server pods and names the Endpoint Picker (EPP) that will choose among them:
apiVersion: inference.networking.k8s.io/v1
kind: InferencePool
metadata: {name: qwen-pool}
spec:
targetPorts:
- number: 8000
selector:
matchLabels:
app: vllm-qwen
endpointPickerRef:
name: qwen-pool-epp
The EPP (deployed alongside, per your implementation's chart) scrapes vLLM metrics — queue depth, KV-cache utilization, loaded LoRA adapters — and picks the endpoint per request instead of blind round-robin. Field names vary slightly across bundle versions; validate with kubectl explain inferencepool.spec.
Route to the pool, not a Service. The HTTPRoute's backend is the pool:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: llm-route}
spec:
parentRefs: [{name: ai-gateway}]
rules:
- matches: [{path: {type: PathPrefix, value: /v1}}]
backendRefs:
- group: inference.networking.k8s.io
kind: InferencePool
name: qwen-pool
Prove it's smarter than round-robin. Send a skewed load: a few long-context requests (big prefill) mixed with many short ones. Watch per-pod vllm:num_requests_waiting — with the EPP, queue depth should stay roughly even and no pod should starve behind a long request. Then repeat against a plain Service and compare P95 TTFT. Keep the numbers; that delta is the business case.
Add policy in layers. Once basic routing works: use your implementation's support for priority/criticality (protect the interactive workload when batch traffic spikes), model-version traffic splitting via weighted rules for canary model rollout (pairs with pb-52's eval gates), and body-based model-name routing if one gateway fronts several pools.
Wire it into the platform path. North-south: clients → LiteLLM gateway (auth, keys, budgets — pb-45) → inference gateway (scheduling-aware routing) → vLLM pools. Each layer does one job; don't make one layer do both.
The EPP optimizes placement, not admission — quotas, auth, and spend limits still belong at the LiteLLM/API-gateway layer above it.
kubectl get inferencepool shows the pool programmed; gateway status conditions are Accepted/Programmed.InferenceModel or old group names (inference.networking.x-k8s.io) won't apply cleanly. Trust kubectl explain over blog posts — including AI-assistant output trained on them.For the lab on a laptop, kind + kgateway + two CPU-mode vLLM replicas is enough to demonstrate queue-aware routing — the effect is visible even without GPUs.