Cuando sirves un modelo de IA en varias GPUs, un balanceador de carga normal no sabe qué Pod tiene cada conversación ni cuánto pesa cada petición. Aquí verás cómo el Inference Gateway, la extensión de Gateway API para modelos, elige el Pod mirando su KV cache y su carga, y cómo llm-d separa la lectura del mensaje y la generación de la respuesta en GPUs distintas.
Vamos a ver cómo se reparten las peticiones cuando tienes un modelo de IA en varias GPUs, y por qué no basta con un balanceador de carga de toda la vida. Empezamos por el problema.
Por qué un balanceador normal no sirve para modelos de IA
Una sola GPU atiende a pocas personas a la vez. Así que en producción pones varias copias del modelo: varios Pods de vLLM, cada uno con su GPU.
¿Y cuál es el problema? Hay dos.
El KV cache se queda en un solo Pod
El primero es el KV cache, que es la memoria donde el modelo guarda lo que ya ha calculado de la conversación, para no repetirlo en cada palabra nueva. Lo expliqué a fondo en la lección de vLLM.
Cuando un Pod procesa tu primer mensaje, guarda en su memoria el KV cache de esa conversación. Si tu segundo mensaje cae en otro Pod, ese Pod no tiene nada y tiene que recalcularlo todo desde cero. O sea, el trabajo que ya pagaste se pierde.
No todas las peticiones pesan lo mismo
El segundo problema es la carga. Imagina cien personas recibiendo su respuesta palabra a palabra, sin problemas. Y llega una petición nueva con un documento larguísimo. Procesar ese documento de golpe exige mucha más GPU que generar una palabra más, así que le quita tiempo a las cien que ya estaban conversando, y todas notan el frenazo.
Un balanceador normal no ve ninguna de estas dos cosas. No sabe qué Pod tiene qué conversación, y no distingue una petición ligera de una pesada.
Inference Gateway: Gateway API para modelos de IA
Gateway API es el sucesor de Ingress en Kubernetes, y sirve para repartir el tráfico web entre tus servicios. Tiene una extensión para modelos de IA que se llama Gateway API Inference Extension, y a su forma de funcionar se le suele llamar Inference Gateway.
¿Y qué cambia respecto a una ruta normal? Cambia el destino de la ruta y cambia quién decide a qué Pod va cada petición.
La ruta apunta a un InferencePool
La ruta ya no apunta a un Service normal, sino a un InferencePool. Así queda una HTTPRoute que manda el tráfico a un pool llamado qwen-pool:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
spec:
rules:
- backendRefs:
- group: inference.networking.k8s.io
kind: InferencePool
name: qwen-pool
Un InferencePool es un grupo de Pods que sirven el mismo modelo. Se eligen por etiquetas, igual que en un Service. La diferencia es que Kubernetes sabe que dentro hay modelos de lenguaje, no una web cualquiera.
El Endpoint Picker decide a qué Pod va cada petición
Delante del pool hay un componente que se llama Endpoint Picker, y es el que decide a qué Pod va cada petición. El proxy del Gateway recibe la petición, pero no decide nada: le pregunta al Endpoint Picker a qué Pod la tiene que mandar.
¿Y cómo lo decide? Mirando métricas de cada Pod en tiempo real: cuántas peticiones tiene en cola, cuánto KV cache tiene ocupado y qué variantes del modelo tiene cargadas.
En resumen, ahora el balanceador sabe cómo está cada Pod antes de mandarle una petición. Y esto ya no es un experimento: la extensión llegó a su versión 1, y el objeto InferencePool ya es una API estable.
Cómo elige el Endpoint Picker el Pod
El Endpoint Picker sigue cuatro pasos, siempre en el mismo orden:
- Descubrir. Mira todos los Pods del pool y junta el estado de cada uno.
- Filtrar. Descarta los que están saturados o los que no sirven el modelo que pediste.
- Puntuar. A los que quedan les pone una nota. Si un Pod ya tiene en memoria el principio de tu conversación, gana puntos. A esto se le llama enrutar por prefijo.
- Reenviar. El proxy manda la petición al Pod ganador.
Y ojo, porque el Pod que ya tiene tu conversación no gana siempre. Si ese Pod está saturado, el Endpoint Picker deja de lado la memoria y elige por carga. No tiene sentido mandarte a una cola larga solo por reutilizar el cache.
Un ejemplo con dos Pods
Tienes dos Pods, A y B. Llega tu primer mensaje y nadie tiene tu conversación, así que el Endpoint Picker elige por carga y te manda al A. El Pod A procesa tu mensaje y guarda el KV cache.
Mandas el segundo mensaje. Ahora el Endpoint Picker sabe que tu conversación está en el A, así que te manda otra vez al A. Y el A no recalcula nada: sigue desde donde se quedó. Si mientras tanto llega otra persona con una conversación nueva, va al B, que está libre.
O sea, cada conversación se queda en el Pod que ya la conoce, y el tráfico nuevo va adonde hay sitio.
llm-d: separar prefill y decode en GPUs distintas
Con esto se resuelve el primer problema, el del KV cache. ¿Y el segundo, el de las peticiones pesadas? Aquí entra llm-d.
llm-d es un proyecto que lanzaron en mayo de 2025 Red Hat, Google Cloud, IBM Research, NVIDIA y CoreWeave. En marzo de 2026 entró en la CNCF como proyecto Sandbox.
Usa todo lo que acabamos de ver. De hecho, se apoya en el Inference Gateway. Pero añade una idea más.
Generar una respuesta tiene dos fases. La primera es leer tu mensaje entero, y se llama prefill: es pesada y usa mucho cálculo de golpe. La segunda es generar la respuesta palabra a palabra, y se llama decode: es ligera, pero larga.
Y el problema de antes era justo este: las dos fases compiten por la misma GPU. La solución de llm-d es separarlas, de forma que unos Pods solo hacen prefill y otros solo hacen decode.
El documento larguísimo va a un Pod de prefill. Ese Pod calcula el KV cache y se lo pasa a un Pod de decode, que genera la respuesta sin que nadie le quite tiempo.
¿Y cómo se pasa el KV cache de una GPU a otra? Con una librería de NVIDIA que se llama NIXL, que mueve esos datos entre GPUs, incluso entre nodos distintos. Así que hay dos capas separadas: el Endpoint Picker decide adónde va cada petición, y NIXL mueve los datos.
Lo que hemos visto
- Un balanceador normal no sabe qué Pod tiene el KV cache de cada conversación ni distingue una petición ligera de una pesada.
- Un InferencePool agrupa los Pods que sirven tu modelo, y una HTTPRoute de Gateway API puede apuntar a él en lugar de a un Service.
- El Endpoint Picker reparte las peticiones mirando la cola, el KV cache ocupado y las variantes del modelo cargadas en cada Pod.
- llm-d además separa la lectura del mensaje (prefill) y la generación de la respuesta (decode) en GPUs distintas, y usa NIXL para pasar el KV cache de una a otra.
- Todo funciona encima de Gateway API, la misma API que usas para tu tráfico web.