Diseño de GPUaaS para IA empresarial local

La IA está evolucionando rápidamente y los ingenieros de software ya no necesitan memorizar la sintaxis. Sin embargo, pensar como arquitecto y comprender la tecnología que permite que los sistemas funcionen de forma segura a escala es cada vez más valioso.

También quiero reflexionar sobre mi rol de ingeniero de soluciones de IA en Cisco, hace un año. Trabajo diariamente con clientes de diferentes sectores: atención médica, servicios financieros, manufactura, bufetes de abogados, y todos intentan responder en gran medida al mismo conjunto de preguntas:

¿Cuál es nuestra estrategia de IA? ¿Qué casos de uso realmente se ajustan a nuestros datos? ¿Nube, local o híbrido? ¿Cuánto costará, no sólo hoy, sino a gran escala? ¿Cómo lo aseguramos?

Estas son las verdaderas limitaciones prácticas que aparecen inmediatamente una vez que se intenta hacer operativa la IA más allá de una prueba de concepto.

Recientemente, agregamos un Cisco UCS C845A a uno de nuestros laboratorios. Tiene 2 GPU NVIDIA RTX PRO 6000 Blackwell, NVMe de 3,1 TB, ~127 núcleos de CPU asignables y 754 GB de RAM. Decidí crear una plataforma interna compartida además de esto, brindando a los equipos un entorno de autoservicio consistente para ejecutar experimentos, validar ideas y desarrollar una experiencia práctica de GPU.

Implementé la plataforma como un clúster OpenShift de un solo nodo (SNO) y coloqué una experiencia GPUaaS multiinquilino encima. Los usuarios reservan capacidad a través de una interfaz de usuario de calendario y el sistema proporciona un entorno de aprendizaje automático aislado prediseñado con PyTorch/CUDA, JupyterLab, VS Code y más. Dentro de ese entorno, los usuarios pueden ejecutar inferencias bajo demanda, iterar el entrenamiento y ajuste de modelos y crear prototipos de microservicios de grado de producción.

Esta publicación analiza la arquitectura: cómo se toman las decisiones de programación, cómo se aíslan los inquilinos y cómo se administra la plataforma. Las decisiones que se tomaron en esta plataforma de laboratorio son las mismas que enfrenta cualquier organización cuando se toma en serio la IA en producción.

Ésta es la base de la IA empresarial a escala. Arquitecturas multiagente, experimentación de autoservicio, multiinquilino seguro, computación GPU con costos predecibles: todo comienza con la correcta capa de plataforma.

Diagrama de arquitectura de plataforma de alto nivel. Imagen creada por el autor.

Configuración inicial

Antes de que exista una plataforma, hay un servidor básico y una pantalla en blanco.

Iniciando el nodo

El nodo se entrega sin sistema operativo. Cuando lo enciendes, ingresas a un shell UEFI. Para OpenShift, la instalación generalmente comienza en Red Hat Hybrid Cloud Console a través del instalador asistido. El instalador asistido maneja la configuración del clúster a través de un flujo de configuración guiado y, una vez completado, genera un ISO de descubrimiento: una imagen de arranque de RHEL CoreOS preconfigurada para su entorno. Asigne el ISO al servidor como medio virtual a través de Cisco IMC, establezca el orden de inicio y enciéndalo. El nodo llamará a la consola y usted podrá iniciar el proceso de instalación. El nodo escribe RHCOS en NVMe y arranca. En unas pocas horas tendrá un clúster en ejecución.

Este flujo de trabajo supone conectividad a Internet y extrae imágenes de los registros de Red Hat durante la instalación. Esa no siempre es una opción. Muchos de los clientes con los que trabajo operan en entornos aislados donde nada toca la Internet pública. El proceso allí es diferente: generar configuraciones de encendido localmente, descargar las imágenes de lanzamiento de OpenShift y los paquetes de operadores con anticipación, reflejar todo en un registro de Quay local y apuntar la instalación a eso. Ambos caminos te llevan al mismo lugar. La instalación asistida es mucho más sencilla. El camino sin barreras es lo que parece la producción en las industrias reguladas.

Configuración de GPU con el operador de GPU NVIDIA

Una vez instalado el Operador de GPU (sucede automáticamente usando el instalador asistido), configuré cómo se presentan las dos GPU RTX PRO 6000 Blackwell a las cargas de trabajo a través de dos ConfigMaps en el espacio de nombres nvidia-gpu-operator.

El primero, custom-mig-config, define la partición física. En este caso, es una estrategia mixta, lo que significa que la GPU 0 se divide en cuatro segmentos MIG de 1 g, 24 GB (~24 GB de memoria dedicada cada uno), la GPU 1 permanece completa para cargas de trabajo que necesitan los ~96 GB completos. La partición MIG es un verdadero aislamiento de hardware. Obtiene memoria dedicada, unidades informáticas y caché L2 por segmento. Las cargas de trabajo verán las instancias MIG como dispositivos físicos separados.

El segundo, device-plugin-config, configura la división de tiempo, lo que permite que varios pods compartan la misma división de GPU o MIG mediante un rápido cambio de contexto. Configuré 4 réplicas por GPU completa y 2 por segmento MIG. Esto es lo que permite ejecutar múltiples contenedores de inferencia uno al lado del otro dentro de una sola sesión.

Almacenamiento fundamental

El NVMe de 3,1 TB es administrado por el operador de almacenamiento LVM (lvms-vg1 StorageClass). Creé dos PVC como parte del proceso de aprovisionamiento inicial: un volumen que respalda PostgreSQL y almacenamiento persistente para el registro de imágenes interno de OpenShift.

Con el sistema operativo instalado, los requisitos previos de la red cumplidos (DNS, asignación de IP, todos los registros A requeridos) que no se tratan en este artículo, las GPU particionadas y el almacenamiento aprovisionado, el clúster está listo para la capa de aplicación.

Arquitectura del sistema

Esto nos lleva al tema principal: la arquitectura del sistema. La plataforma se divide en tres planos: programación, control y tiempo de ejecución, con la base de datos PostgreSQL como única fuente de información.

En el espacio de nombres de administración de la plataforma, hay cuatro implementaciones siempre activas:

Aplicación de portal: un contenedor único que ejecuta React UI y FastAPI backend Reconciliador (controlador): el bucle de control que converge continuamente el estado del clúster para que coincida con la base de datos PostgreSQL: estado persistente para usuarios, reservas, tokens e historial de auditoría Demonio de caché: un servicio local de nodo que prepara artefactos de modelos grandes/motores de inferencia para que los usuarios puedan comenzar rápidamente (extraer una imagen vLLM de 20 GB a través del proxy corporativo puede llevar horas)

Una nota rápida sobre el ciclo de vida de desarrollo, porque es fácil complicar el envío de sistemas Kubernetes. Escribo y pruebo código localmente, pero las imágenes se crean en el clúster utilizando artefactos de compilación de OpenShift (BuildConfigs) y se envían al registro interno. Los propios despliegues simplemente apuntan a esas imágenes.

La primera vez que se introduce un componente, aplico los manifiestos para crear la Implementación/Servicio/RBAC. Después de eso, la mayoría de los cambios simplemente crean una nueva imagen en el clúster y luego activan un reinicio para que la implementación extraiga la imagen actualizada y avance:

implementación de reinicio de implementación de oc / -n

Ese es el ciclo: confirmar → compilación en el clúster → registro interno → reiniciar/implementar.

El plano de programación

Este es el punto de entrada de cara al usuario. Los usuarios ven el grupo de recursos: GPU, CPU, memoria, eligen una ventana de tiempo, eligen el modo de asignación de GPU (más sobre esto más adelante) y envían una reserva.

Las GPU son hardware costoso con un costo real por hora, ya sea que estén en uso o no. El sistema de reservas trata el tiempo calendario y la capacidad física como una restricción combinada. De la misma manera que reservaría una sala de conferencias, excepto que esta sala tiene 96 GB de VRAM y cuesta considerablemente más por hora.

Debajo del capó, el sistema consulta las reservas superpuestas con la capacidad del grupo mediante bloqueos de aviso para evitar reservas dobles. Básicamente, se trata simplemente de sumar la capacidad reservada y restarla de la capacidad total. Cada reserva sigue un ciclo de vida: APROBADO → ACTIVO → COMPLETADO, con CANCELADO y FALLADO como estados terminales.

El servidor FastAPI en sí es intencionalmente delgado. Valida la entrada, persiste la reserva y regresa. Nunca habla con la API de Kubernetes.

El plano de control

En el corazón de la plataforma está el controlador. Está basado en Python y se ejecuta en un bucle continuo con una cadencia de 30 segundos. Puedes considerarlo como un trabajo cron en términos de tiempo, pero arquitectónicamente es un controlador estilo Kubernetes responsable de impulsar el sistema hacia un estado deseado.

La base de datos mantiene el estado deseado (reservas con ventanas de tiempo y requisitos de recursos). El reconciliador lee ese estado, lo compara con lo que realmente existe en el clúster de Kubernetes y hace converger los dos. No hay llamadas API simultáneas que se apresuren a cambiar el estado del clúster; solo un bucle determinista que realiza el conjunto mínimo de cambios necesarios para alcanzar el estado deseado. Si el reconciliador falla, se reinicia y continúa exactamente donde lo dejó, porque la fuente de verdad (estado deseado) permanece intacta en la base de datos.

Cada ciclo de conciliación evalúa cuatro inquietudes en orden:

Detenga las sesiones caducadas o canceladas y elimine el espacio de nombres (que realiza una limpieza en cascada de todos los recursos que contiene). Repare sesiones fallidas y elimine recursos huérfanos que quedaron tras un aprovisionamiento parcialmente completado. Inicie sesiones elegibles cuando llegue su ventana de reserva: aprovisione, configure y entregue el espacio de trabajo al usuario. Mantenga la base de datos venciendo los tokens antiguos y aplicando la retención del registro de auditoría.

Iniciar una sesión es una secuencia de aprovisionamiento de varios pasos, y cada paso es idempotente, lo que significa que está diseñado para volver a ejecutarse de forma segura si se interrumpe a mitad de camino:

Controlador en profundidad. Imagen creada por el autor.

El reconciliador es el único componente que se comunica con la API de Kubernetes.

La recolección de basura también se integra en el mismo circuito. A una cadencia más lenta (~5 minutos), el reconciliador busca huérfanos de espacios de nombres cruzados, como enlaces RBAC obsoletos, entradas sobrantes del contexto de seguridad de OpenShift, espacios de nombres atascados en la terminación o espacios de nombres que existen en el clúster pero que no tienen un registro de base de datos coincidente.

El supuesto de diseño en todo momento es que la falla es normal. Por ejemplo, tuvimos una falla en el suministro de energía en el nodo que hizo que el clúster se desconectara a mitad de la sesión y cuando volvió, el reconciliador reanudó su ciclo, detectó las discrepancias de estado y se reparó automáticamente sin intervención manual.

El plano de ejecución

Cuando se inicia una ventana de reserva, el usuario abre un navegador y llega a un espacio de trabajo completo de VS Code (servidor de códigos) precargado con toda la pila AI/ML y acceso a kubectl dentro de su espacio de nombres de sesión.

Captura de pantalla del espacio de trabajo. Imagen tomada por el autor.

Los motores de inferencia populares como vLLM, Ollama, TGI y Triton ya están almacenados en caché en el nodo, por lo que implementar un servidor modelo es una tarea sencilla que comienza en segundos. Hay 600 GB de almacenamiento persistente respaldado por NVMe asignado a la sesión, incluido un directorio de inicio de 20 GB para portátiles y scripts, y un caché de modelo de 300 GB.

Cada sesión es un espacio de nombres de Kubernetes completamente aislado, su propio límite de radio de explosión con recursos dedicados y visibilidad cero del entorno de cualquier otro inquilino. El reconciliador proporciona RBAC con ámbito de espacio de nombres que otorga plenos poderes de administración dentro de ese límite, lo que permite a los usuarios crear y eliminar pods, implementaciones, servicios, rutas y secretos, sea cual sea la carga de trabajo que requiera. Pero no hay acceso a nivel de clúster. Los usuarios pueden leer su propia ResourceQuota para ver su presupuesto restante, pero no pueden modificarlo.

ResourceQuota impone un límite máximo a todo. Un trabajo de entrenamiento fuera de control no puede hacer OOM en el nodo. Un contenedor fraudulento no puede llenar el NVMe. LimitRange inyecta automáticamente valores predeterminados sensatos en cada contenedor, por lo que los usuarios pueden ejecutar kubectl sin especificar solicitudes de recursos. Hay un ConfigMap proxy inyectado en el espacio de nombres para que los contenedores implementados por el usuario obtengan la salida de la red corporativa sin configuración manual.

Los usuarios implementan lo que quieren: servidores de inferencia, bases de datos, servicios personalizados y la plataforma maneja las barreras de seguridad.

Cuando finaliza la ventana de reserva, el reconciliador elimina el espacio de nombres y todo lo que contiene.

Programación de GPU

Diagrama de múltiples inquilinos de nodos. Imagen creada por el autor.

Ahora viene la parte divertida: programar la GPU y ejecutar cargas de trabajo aceleradas por hardware en un entorno multiinquilino.

MIG y corte de tiempo

Cubrimos la configuración MIG en la configuración inicial, pero vale la pena revisarla desde una perspectiva de programación. La GPU 0 está dividida en cuatro segmentos MIG de 1 g, 24 GB, cada uno con ~24 GB de memoria dedicada, suficiente para la mayoría de los modelos de parámetros de 7B a 14B. La GPU 1 permanece completa para cargas de trabajo que necesitan ~96 GB de VRAM completa para el entrenamiento de modelos, inferencia de precisión total en más de 70 mil millones de modelos o cualquier cosa que simplemente no quepa en una porción.

El sistema de reservas los rastrea como tipos de recursos distintos. Los usuarios reservan nvidia.com/gpu (completo) o nvidia.com/mig-1g.24gb (hasta cuatro porciones). La ResourceQuota para cada sesión niega el tipo opuesto. Si reservó un segmento MIG, físicamente no puede solicitar una GPU completa, incluso si una está inactiva. En un entorno MIG mixto, permitir que una sesión consuma accidentalmente el tipo de recurso incorrecto alteraría los cálculos de capacidad para cualquier otra reserva en el calendario.

En nuestra configuración, 1 GPU completa aparece como 4 recursos programables. Cada corte MIG aparece como 2.

Lo que eso significa es que un usuario reserva una GPU física y puede ejecutar hasta cuatro contenedores simultáneos acelerados por GPU dentro de su sesión: una instancia de vLLM que sirve gpt-oss, una instancia de Ollama con Mistral, un servidor TGI que ejecuta un reranker y un servicio personalizado que orquesta los tres.

Dos modos de asignación

En el momento de la reserva, los usuarios eligen cómo se distribuye inicialmente su presupuesto de GPU entre el espacio de trabajo y los contenedores implementados por el usuario.

ML interactivo: el módulo del espacio de trabajo recibe una GPU (o segmento MIG) conectado directamente. El usuario abre Jupyter, importa PyTorch y tiene acceso CUDA inmediato para capacitación, ajuste o depuración. Aún se pueden generar módulos de GPU adicionales mediante división de tiempo, pero el espacio de trabajo está consumiendo una de las ranuras virtuales.

Contenedores de inferencia: el espacio de trabajo es liviano y no tiene GPU conectada. Toda la capacidad dividida en tiempo está disponible para los contenedores implementados por el usuario. Con una reserva completa de GPU, son cuatro ranuras completas para cargas de trabajo de inferencia.

Existe una compensación real en el rendimiento con la división del tiempo: las cargas de trabajo comparten VRAM y ancho de banda de cómputo. Para desarrollar, probar y validar arquitecturas multiservicio, que es exactamente para lo que sirve esta plataforma, es la compensación correcta. Para la inferencia sensible a la latencia de producción donde cada milisegundo de p99 importa, usaría segmentos dedicados 1:1 o GPU completas.

“Tokenomía” de GPU

Una de las primeras preguntas de la introducción fue: ¿Cuánto costará, no sólo hoy, sino a gran escala? Para responder a eso, hay que empezar con cómo se ve realmente la carga de trabajo en producción.

Cómo se ven las implementaciones reales

Cuando trabajo con clientes en su arquitectura de inferencia, nadie ejecuta un único modelo detrás de un único punto final. El patrón que sigue surgiendo es una flota de modelos adaptados a la tarea. Tiene un modelo de parámetros 7B que maneja clasificación y extracción simples y se ejecuta cómodamente en un corte MIG. Un modelo 14B que realiza resúmenes y chat de propósito general. Un modelo 70B para razonamiento complejo y tareas de varios pasos, y tal vez un modelo 400B para los problemas más difíciles donde la calidad no es negociable. Las solicitudes se dirigen al modelo adecuado según la complejidad, los requisitos de latencia o las restricciones de costos. No está pagando un cómputo de clase 70B por una tarea que un 7B puede realizar.

En los sistemas multiagente, esto se vuelve más interesante. Los agentes se suscriben a un bus de mensajes y permanecen inactivos hasta que se les solicita: un patrón pub-sub en el que el contexto se comparte con el agente en el momento de la invocación y el pod ya está activo. No hay penalización por arranque en frío porque el modelo está cargado y el contenedor está en ejecución. Un agente orquestador evalúa la solicitud entrante, la enruta a un agente especializado (recuperación, generación de código, resumen, validación), recopila los resultados y sintetiza una respuesta. Cuatro o cinco modelos que colaboran en una sola solicitud de usuario, cada uno ejecutándose en su propio contenedor dentro del mismo espacio de nombres, comunicándose a través de la red interna de Kubernetes.

Las políticas de red añaden otra dimensión. No todos los agentes deberían tener acceso a todas las herramientas. Su agente de recuperación puede hablar con la base de datos de vectores. Su agente de ejecución de código puede alcanzar un tiempo de ejecución en espacio aislado. Pero el agente de resumen tampoco tiene por qué tocar: recibe contexto del orquestador y devuelve texto. Las políticas de red imponen estos límites a nivel de clúster, por lo que el acceso a las herramientas está controlado por la infraestructura, no por la lógica de la aplicación.

Este es el perfil de carga de trabajo para el que fue diseñada la plataforma. El corte MIG le permite asignar el tamaño correcto de GPU por modelo; un 7B no necesita 96 GB de VRAM. La división de tiempo permite que varios agentes compartan el mismo dispositivo físico. El aislamiento del espacio de nombres mantiene a los inquilinos separados mientras los agentes dentro de una sesión se comunican libremente. La arquitectura soporta directamente estos patrones.

Cuantificándolo

Para pasar de la arquitectura al caso de negocio, desarrollé un marco de economía de tokens que reduce el costo de la infraestructura a una única unidad comparable: el costo por millón de tokens. Cada token conlleva su parte amortizada de capital de hardware (incluida la combinación de cargas de trabajo y la redundancia), mantenimiento, energía y refrigeración. El numerador es su costo anual total. El denominador es la cantidad de tokens que realmente procesa, lo cual es enteramente una función de la utilización.

La utilización es la palanca más poderosa sobre el costo por token. No reduce lo que gasta, las facturas de hardware y energía son fijas. Lo que hace es distribuir esos costos fijos entre tokens más procesados. Una plataforma que funciona al 80% de utilización produce tokens a casi la mitad del costo unitario que una al 40%. Misma infraestructura, economía dramáticamente diferente. Es por eso que el sistema de reserva, la partición MIG y la división del tiempo son importantes más allá de la UX: existen para mantener las costosas GPU procesando tokens durante tantas horas disponibles como sea posible.

Como el marco es algebraico, también puedes resolver en la otra dirección. Dada una demanda de token conocida y un presupuesto, resuelva la infraestructura requerida y vea de inmediato si tiene un aprovisionamiento excesivo (quemando dinero en GPU inactivas), un aprovisionamiento insuficiente (solicitudes en cola y latencia degradante) o el tamaño adecuado.

Para la comparación de la nube, los proveedores ya han incluido su utilización, redundancia y gastos generales en el precio de API por token. La pregunta es: ¿a qué utilización el costo unitario local cae por debajo de esa tasa? Para una demanda empresarial constante de GPU, el tipo de tráfico de inferencia de estado estable que generan estas arquitecturas de múltiples agentes es el que gana en las instalaciones.

Sin embargo, para pruebas, demostraciones y pruebas de concepto, la nube es más barata.

Los equipos de ingeniería a menudo necesitan justificar el gasto financiero con cifras claras y defendibles. El marco de la tokenómica cierra esa brecha.

Conclusión

Al comienzo de esta publicación, enumeré las preguntas que escucho constantemente de los clientes: estrategia de IA, casos de uso, nube versus local, costo, seguridad. En última instancia, todos requieren lo mismo: una capa de plataforma que pueda programar recursos de GPU, aislar a los inquilinos y brindar a los equipos una ruta de autoservicio desde el experimento hasta la producción sin esperar a la infraestructura.

Eso es lo que recorrió esta publicación. No es un producto ni un servicio administrado, sino una arquitectura basada en Kubernetes, PostgreSQL, Python y el operador de GPU NVIDIA, que se ejecuta en un único Cisco UCS C845A con dos GPU NVIDIA RTX PRO 6000 Blackwell en nuestro laboratorio. Es un punto de partida práctico que aborda la programación, el arrendamiento múltiple, el modelado de costos y las realidades operativas del día 2 para mantener confiable la infraestructura de GPU.

Escale esto a múltiples Cisco AI Pods y el plano de programación, el patrón de conciliación y el modelo de aislamiento se transfieren directamente. La base es la misma.

Si está trabajando en estas mismas decisiones: cómo programar GPU, cómo aislar a los inquilinos, cómo construir el caso de negocio para la infraestructura de IA local, agradecería la conversación.