La mayoría de los servicios de inferencia usan solo una parte de una GPU, y una tarjeta entera suele ser más de lo que necesita un notebook, un trabajo de batch scoring o un modelo pequeño. Compartir una tarjeta entre cargas de trabajo parece sencillo, pero Kubernetes no lo hace por defecto, y las distintas formas de hacerlo hacen promesas muy diferentes. Este artículo explica esas promesas, qué garantiza una porción compartida en Kube-DC y qué no, y qué aprendimos ejecutando un LLM real en una de ellas.
Kubernetes reparte GPU enteras por defecto
El modelo estándar de device plugin de Kubernetes comunica las GPU al scheduler como números enteros. Una solicitud de nvidia.com/gpu: 1 significa la tarjeta entera, y todos los demás pods esperan. El scheduler no tiene noción de cuota de memoria, así que compartir una tarjeta requiere maquinaria adicional, y hay tres enfoques habituales.
| Enfoque | Límite de memoria | Aislamiento | ¿Funciona en una V100? |
|---|---|---|---|
| MIG (particionado por hardware) | Aplicado por hardware | Aislamiento de memoria y de fallos en la capa de hardware | No: requiere Ampere o posterior (A100, H100, A30, H200), y la V100 es una tarjeta más antigua, de la generación Volta |
| Time-slicing | Ninguno: las réplicas comparten la memoria de la tarjeta | Sin aislamiento de memoria ni de fallos entre réplicas, según la documentación de NVIDIA | Sí |
| Software slicing (HAMi) | Aplicado por software: se rechazan las asignaciones que superan la porción | Aislamiento por software mediante interceptación de la API de CUDA; no es aislamiento por hardware y el cómputo se limita según el criterio best-effort | Sí |
HAMi, un proyecto de la Cloud Native Computing Foundation, funciona cargando en cada contenedor una biblioteca que intercepta las llamadas de CUDA. El contenedor ve solo su porción de memoria, una asignación que superaría la porción devuelve un error de memoria agotada y los lanzamientos de kernels se limitan hacia la cuota de cómputo solicitada. La propia documentación de HAMi es cuidadosa con los límites: las aplicaciones que eluden la biblioteca CUDA, como Docker-in-Docker o las llamadas directas al controlador, no están cubiertas, y el aislamiento es best-effort en comparación con MIG.
Qué garantiza una porción compartida en Kube-DC
Los paquetes GPU de Kube-DC usan software slicing en tarjetas NVIDIA V100, y la documentación de la plataforma indica con precisión las garantías. Una porción es una fracción fija de un modelo de GPU; solicitas el producto en lugar de ajustar libremente memoria y cómputo. En una V100 de 32 GB, nuestros paquetes ofrecen el 25 % (8 GiB), el 50 % (16 GiB) y el 100 % (32 GiB).
- La porción de memoria se aplica. Dentro del contenedor, nvidia-smi informa del tamaño de la porción, no de la tarjeta física, y la porción es un límite rígido.
- La cuota de cómputo es cooperativa. La plataforma orienta cada carga de trabajo hacia su cuota en régimen estable, pero en el arranque y en algunas rutas de las bibliotecas CUDA puede superarse brevemente. No es un rendimiento garantizado.
- La cuota es un derecho, no una reserva. Tener cuota no reserva una porción física. Cuando todas las GPU compatibles están ocupadas, una carga de trabajo válida espera en el estado Pending hasta que se libera una porción.
- Las porciones son para contenedores. La capacidad compartida no puede conectarse a una máquina virtual.
Cómo es usar una
Desde Kubernetes 1.34, las API de Dynamic Resource Allocation (DRA) en resource.k8s.io/v1 están disponibles con carácter general y Kube-DC las usa: una carga de trabajo hace referencia a un ResourceClaimTemplate que apunta a la DeviceClass de tu producto, y la plataforma valida la solicitud cuando la aplicas. La cuota de tu proyecto muestra cuántas porciones de cada producto puedes tener a la vez:
kubectl get resourcequota -n <project-namespace> -o yaml | grep deviceclass
Una vez que tu pod está en ejecución, confirma la porción desde dentro del contenedor. En el producto de 8 GiB, la memoria total es de 8192 MiB aunque la tarjeta tenga 32 GB:
nvidia-smi --query-gpu=name,memory.total --format=csv,noheader
# Tesla V100-PCIE-32GB, 8192 MiB
El manifest completo, para copiar y editar, está en la documentación de Kube-DC, y no lo reproducimos aquí deliberadamente, porque debe ajustarse exactamente al contrato publicado de la plataforma. En nuestra propia prueba, dos intentos fallaron por ese motivo antes de que el tercero tuviera éxito:
- Una simple solicitud nvidia.com/gpu en un pod en bruto fue rechazada por una admission policy, porque la capacidad de GPU compartida solo se ofrece mediante el patrón basado en claims.
- Una claim template DRA escrita a mano fue rechazada por una segunda admission policy, porque las etiquetas, los nombres y los valores de capacidad fijos deben seguir la forma documentada.
- El manifest documentado fue aceptado y después el pod no consiguió arrancar por un motivo no relacionado: la cuota de CPU compartida del proyecto estaba casi totalmente usada por un Managed Cluster y una máquina virtual. Un pod GPU sigue necesitando CPU y memoria de tu paquete, así que comprueba ese margen antes de desplegar.
Ejecutar un LLM en una porción: qué aprendimos
En nuestra prueba de extremo a extremo de agosto de 2026 ejecutamos Ollama en una porción de 8 GiB sirviendo llama3.2:3b y después le conectamos un agente de IA, primero desde una máquina virtual del mismo proyecto y luego desde un servidor fuera del centro de datos. Los logs de Ollama mostraban la tarjeta como una Tesla V100 con compute capability 7.0 y un total de 8,0 GiB, exactamente la porción. Cuatro detalles nos costaron tiempo, y cada uno se aplica a cualquiera que ejecute un modelo en una porción.
1. Comprueba que tu software sigue siendo compatible con la generación de GPU
La V100 es una tarjeta de la generación Volta y el software de GPU actual ha empezado a dejarla atrás. En nuestra prueba, la última imagen de Ollama omitió la GPU, porque su compilación de CUDA no incluía la compute capability 7.0. Fijar ollama/ollama:0.24.0 lo solucionó. Esto coincide con los informes del origen: una incidencia de Ollama describe la v0.30.0 fallando en una Tesla V100 con el error «device kernel image is invalid», mientras que la v0.24.0 y las anteriores funcionaban, y un mantenedor atribuyó después uno de estos fallos a kernels CUDA comprimidos que requieren el controlador NVIDIA 550 o posterior. Fija la versión de tu imagen y prueba las actualizaciones de forma deliberada.
2. El límite de memoria del pod es independiente de la porción de GPU
Nuestra primera carga del modelo fue terminada con el código de salida 137 (OOMKilled). La causa fue el propio límite de memoria del contenedor, de 512 MiB, muy insuficiente para la sobrecarga de Ollama en el lado del host, y no tenía nada que ver con la memoria de la GPU. Dimensiona el request y el limit de RAM del contenedor con independencia de la porción fija. Usamos 2 GiB y 4 GiB:
containers:
- name: ollama
image: ollama/ollama:0.24.0
env:
- name: OLLAMA_CONTEXT_LENGTH
value: "16384"
resources:
requests:
memory: 2Gi
limits:
memory: 4Gi
3. La ventana de contexto predeterminada puede truncar en silencio el prompt de un agente
El agente que conectamos envía un prompt de sistema y definiciones de herramientas de unos 14.700 tokens. El log del servidor de Ollama mostraba que la entrada se recortaba a su límite de 4.096 tokens, lo que producía respuestas confusas y poco fiables que parecían un problema de calidad del modelo. Aumentar la longitud de contexto con la variable anterior resolvió la causa. Recuerda que un contexto más largo usa más memoria de tu porción, así que dimensiona juntos el modelo y el contexto.
4. Los modelos viven en el pod a menos que les des un volumen
Con el almacenamiento temporal predeterminado, cada reinicio del pod, incluido el causado por cambiar una variable de entorno, borraba los modelos descargados. Pon el directorio de modelos en un volumen persistente para cualquier cosa que vaya más allá de una demo. Ten en cuenta también que los roles de tenant en Kube-DC no pueden usar kubectl exec, así que descargamos los modelos con Jobs puntuales que llaman a la API HTTP de Ollama.
Cuándo una porción compartida es la herramienta adecuada
La documentación de Kube-DC indica el caso de uso adecuado: inferencia de modelos, notebooks, pequeñas ejecuciones de entrenamiento y batch scoring, en los que un trabajo necesita aceleración por GPU pero no una tarjeta entera. Encaja mal cuando necesitas un límite de aislamiento rígido, un rendimiento garantizado o un modelo mayor que la porción, y el trabajo que requiere una tarjeta entera pertenece a hardware dedicado. Como las porciones hacen cola cuando la tarjeta está ocupada, también encaja mejor con cargas de trabajo que pueden esperar a que haya capacidad que con servicios críticos para la latencia con plazos estrictos.
Cómo evaluar cualquier oferta de GPU compartida
- ¿El límite de memoria se aplica o es solo una sugerencia? El time-slicing no aplica ninguno.
- ¿La cuota de cómputo es garantizada, cooperativa o ilimitada, y qué ocurre en caso de contención?
- ¿La cuota es una reserva, o tu carga de trabajo puede quedarse esperando en Pending cuando la tarjeta está ocupada?
- ¿Qué modelo de GPU es y el software que piensas ejecutar sigue siendo compatible con esa generación?
- ¿Puedes obtener una tarjeta entera o una VM dedicada si se te queda pequeña una porción, y cómo es el aislamiento en ese caso?
- ¿Qué CPU, memoria y almacenamiento obtienes junto con la GPU, y se comparten en un pool con el resto de tus cargas de trabajo?
Fuentes
- Kube-DC: shared GPU workloads
- Kube-DC platform datasheet
- HAMi: GPU virtualization principles
- HAMi: FAQ (HAMi vGPU compared with MIG)
- NVIDIA GPU Operator: time-slicing GPUs in Kubernetes
- Kubernetes v1.34: DRA has graduated to GA
- Kubernetes: Dynamic Resource Allocation
- Ollama issue 16449: v0.30.0 fails on Tesla V100
Lecturas relacionadas
- Kubernetes managed vs. self-hosted: la realidad técnica
- Cómo evaluar a un proveedor de Managed Kubernetes en Europa
Pruébalo en Kube-DC
Nuestros paquetes GPU añaden a un pool de Kube-DC una porción de una NVIDIA V100 compartida: el 25 %, el 50 % o el 100 % de la tarjeta, con la porción de memoria aplicada. Con ella obtienes vCPU dedicadas, almacenamiento NVMe respaldado por Ceph, una IPv4 pública dedicada, almacenamiento de objetos compatible con S3 y tus propios clústeres de Kubernetes administrados por el tenant, con soporte 24/7 por correo y tickets y un objetivo de resolución de 4 horas. Hemos ejecutado de extremo a extremo un LLM real y un agente de IA, y cada paso de este artículo es uno que puedes repetir.
Descubre Kube-DC Kubernetes