Introducción
Los equipos de desarrollo de GPU enfrentan un dilema arquitectónico costoso: dedicar un control plane completo por cada equipo resulta en una sobrecarga de recursos innecesaria, mientras que compartir un único clúster monolítico debilita el aislamiento de seguridad y la autonomía operativa. Además, la asignación tradicional de GPU en Kubernetes suele ser binaria (tarjeta completa o nada), desperdiciando memoria y cómputo cuando las cargas de trabajo solo requieren una fracción del dispositivo.
Esta guía demuestra cómo resolver ambos problemas combinando Kamaji (para virtualizar control planes ligeros) y HAMi (Heterogeneous AI Computing Virtualization Middleware) para la compartición de GPU. El objetivo es construir una plataforma multi-tenant reproducible en una única máquina Linux sin GPU física, utilizando el plugin simulado de HAMi. Al finalizar, tendrás dos inquilinos aislados (tenant-a y tenant-b) capaces de solicitar GPU fraccionada, con validación de que el tráfico de admisión atraviesa correctamente el túnel Konnectivity de Kamaji.
Qué es y para qué sirve
La solución se basa en una arquitectura de dos capas. La capa de infraestructura utiliza Kind (Kubernetes in Docker) para alojar un clúster de gestión que ejecuta Kamaji. Kamaji permite crear «Tenant Control Planes» como pods dentro del clúster de gestión, eliminando la necesidad de nodos dedicados para cada API server de cliente. Esto reduce drásticamente el consumo de CPU y RAM.
La capa de virtualización de hardware utiliza HAMi. En entornos reales, HAMi instala un webhook y un scheduler que interceptan las solicitudes de nvidia.com/gpu y las subdividen en gpumem (memoria) y gpucores (núcleos de cómputo). Para este ejercicio de validación lógica sin hardware, utilizamos el mock device plugin de HAMi. Este plugin no requiere drivers NVIDIA; en su lugar, lee anotaciones de nodo para simular tarjetas GPU y registrar capacidades falsas. La integración crítica que verificamos aquí es el flujo de admisión: cuando un pod se crea en el tenant, el API server (gestionado por Kamaji) debe llamar al webhook de HAMi (que corre en los nodos trabajadores del tenant) a través del túnel Konnectivity.
Prerequisitos
Antes de iniciar, asegurate de cumplir con los siguientes requisitos. La configuración está probada para consumos mínimos, pero el uso de contenedores de control plane puede picar CPU durante el arranque.
- Sistema Operativo: Linux (Ubuntu 22.04/24.04 recomendado).
- Recursos: 4 vCPU, 8 GB de RAM libre, 20 GB de disco.
- Software:
* Docker Engine 24.0+.
* Docker Compose (opcional, pero útil para limpieza).
* kind v0.20+.
* kubectl v1.28+.
* helm v3.13+.
- Permisos: Usuario con permisos para ejecutar docker y kubectl contra el clúster de gestión.
Guía paso a paso
Paso 1: Preparación de la red y clúster de gestión
Primero, iniciamos el clúster Kind. Es crucial identificar el rango de IPv4 de la red de Docker para configurar MetalLB correctamente, evitando conflictos con IPv6 que suelen romper la asignación de IPs externas.
# Crear el archivo de configuración kind-config.yaml
cat <
# Instalar cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
# Instalar MetalLB con el rango obtenido
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb
# Esperar a que los pods estén listos
kubectl wait –for=condition=ready pod -l app=metallb -n metallb-system –timeout=180s
# Crear el pool de IPs (ajusta el range según SUBNET detectado) Kamaji actúa como el orquestador de control planes. Lo instalamos en el clúster de gestión. helm repo add kamaji https://clastix.github.io/charts Verifica que el pod de control plane de Kamaji esté corriendo: kubectl get pods -n kamaji-system Definimos dos tenants. Cada uno tendrá su propio API server, pero compartirán el datastore etcd gestionado por Kamaji (aislado lógicamente). # tenant-a.yaml Aplica los manifestos: kubectl apply -f tenant-a.yaml Una vez que los pods tenant-a-control-plane y tenant-b-control-plane estén Running, extrae los kubeconfigs: KAMAJI_POD=$(kubectl get pod -l app.kubernetes.io/name=kamaji -n kamaji-system -o jsonpath='{.items[0].metadata.name}’) Kamaji no gestiona nodos workers automáticamente. Debemos crear contenedores kindest/node manualmente y unirlos al tenant correspondiente. # Generar token y hash para tenant-a # Iniciar contenedor worker para tenant-a # Obtener IP del worker # Ejecutar join dentro del contenedor (requiere acceso root en el contenedor kind) Nota: El IP del API server del tenant se puede obtener describiendo el servicio kubernetes en el namespace del tenant en el clúster de gestión, o usando el endpoint interno de Kamaji. Repite esto para tenant-b. Luego, instala CNI (Calico) en cada tenant usando sus respectivos kubeconfigs. # Ejemplo para tenant-a Aquí es donde ocurre la magia. Instalamos HAMi en los tenants. No etiquetes los nodos con gpu=on o etiquetas que disparen el plugin real de NVIDIA, ya que fallará por falta de drivers. Edita la configuración de HAMi para usar el mock plugin. En el values.yaml de HAMi, asegurate de que el dispositivo plugin sea el mock. Anotamos el nodo worker del tenant para simular una GPU: kubectl –kubeconfig tenant-a.kubeconfig patch node $(docker inspect tenant-a-worker -f ‘{{.Name}}’ | cut -c2-) -p ‘{«metadata»:{«annotations»:{«hami.io/node-mock-gpu»:»[{\»device_uuid\»:\»GPU-MOCK-A0\»,\»device_type\»:\»NVIDIA A100\»,\»device_memory\»:8192,\»device_cores\»:100}]»}}}’ Verifica que el nodo reporte la capacidad: kubectl –kubeconfig tenant-a.kubeconfig describe node Errores comunes y soluciones: Seguridad: Esta configuración valida la lógica de scheduling y aislamiento de API. En producción, asegúrate de que las políticas de NetworkPolicy en el clúster de gestión impidan que los tenants se comuniquen entre sí directamente a nivel de red, confiando únicamente en el aislamiento lógico de los control planes. La combinación de Kamaji y HAMi permite ofrecer GPU-as-a-Service de forma multi-tenant sin sacrificar el aislamiento ni la eficiencia de recursos. Kamaji elimina el costo del control plane por equipo, mientras que HAMi permite granularidad en la asignación de GPU. La validación en hardware simulado demuestra que el punto de integración más frágil —la llamada del webhook de HAMi a través del túnel Konnectivity de Kamaji— funciona correctamente. Una vez validada la lógica en este entorno de bajo costo, la migración a hardware real implica simplemente reemplazar el mock plugin por el plugin de NVIDIA de HAMi. https://www.cncf.io/blog/2026/10/23/multi-tenant-gpu-kubernetes-on-simulated-hardware-with-kamaji-and-hami/
cat <
helm repo update
helm install kamaji kamaji/kamaji \
–namespace kamaji-system –create-namespace \
–set etcd.image.tag=v3.5.9
# Resultado esperado: kamaji-control-plane-… RunningPaso 3: Creación de los Tenant Control Planes
apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
metadata:
name: tenant-a
spec:
kubernetesVersion: v1.29.0
controlPlane:
deployment:
replicas: 1
networkProfile:
serviceCIDR: 10.96.0.0/16
podCIDR: 10.244.0.0/16
—
# tenant-b.yaml
apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
metadata:
name: tenant-b
spec:
kubernetesVersion: v1.29.0
controlPlane:
deployment:
replicas: 1
networkProfile:
serviceCIDR: 10.97.0.0/16
podCIDR: 10.245.0.0/16
kubectl apply -f tenant-b.yaml
kubectl exec -n kamaji-system $KAMAJI_POD — cat /etc/kamaji/tenant-a/admin.conf > tenant-a.kubeconfig
kubectl exec -n kamaji-system $KAMAJI_POD — cat /etc/kamaji/tenant-b/admin.conf > tenant-b.kubeconfigPaso 4: Unión de Nodos Trabajadores (Workers)
TENANT_A_KUBECONFIG=tenant-a.kubeconfig
TOKEN=$(kubectl –kubeconfig $TENANT_A_KUBECONFIG token create –print-join-command | cut -d ‘ ‘ -f 2)
HASH=$(kubectl –kubeconfig $TENANT_A_KUBECONFIG token list -o jsonpath='{.items[0].tokenHash}’)
docker run -d –name tenant-a-worker –network kind \
kindest/node:v1.29.0
WORKER_IP=$(docker inspect tenant-a-worker -f ‘{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}’)
docker exec tenant-a-worker kubeadm join
kubectl –kubeconfig tenant-a.kubeconfig apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
# Asegúrate de ajustar la ipPool si es necesarioPaso 5: Configuración de HAMi con GPUs Simuladas
# Deberías ver gpumem: 8192 y gpucores: 100Consideraciones y buenas prácticas
Conclusión
Fuentes
