Una guía práctica con Docker, GPU NVIDIA y una API compatible con OpenAI
Hola a tod@s,
Hoy voy a hablar de inteligencia artificial aplicada a infraestructura. En esta ocasión vamos a desplegar un LLM con vLLM, uno de los motores de inferencia más utilizados cuando necesitamos servir modelos con buen rendimiento y exponerlos mediante una API compatible con OpenAI.
Después de comentarle a un compañero de la comunidad que iba mi proximo artículo me hizo la siguiente pregunta:
¿Por qué utilizar modelos de IA locales?
Ejecutar modelos de inteligencia artificial dentro nuestra organización nos permite mantener el control sobre la información, evitar que nuestro código fuente, documentación o configuraciones corporativas se envíen a servicios externos y garantizar que los datos permanezcan bajo la gobernanza de la empresa. Además, nos proporciona una plataforma propia para desarrollo asistido por IA, con costes predecibles, independencia de proveedores externos y capacidad de adaptación a las necesidades específicas de la organización. En mi caso, hoy voy a utilizar el Dell Pro Max GB10 nos permite alojar modelos avanzados como Qwen3-Coder, ofreciéndonos una experiencia similar a GitHub Copilot mientras se preservan la privacidad, la seguridad y la soberanía de los datos. La integración se realiza mediante vLLM, que expone una API compatible con OpenAI para ser consumida por herramientas de desarrollo y automatización y otra cosa tener en cuenta que si necesitamos esto para una organización grande ya iríamos a otra tecnologia, otra plataforma que hablaremos más adelante.
La idea del artículo es muy práctica. Partiremos de equipo Dell Pro Max GB10 con Linux con una GPU NVIDIA, ejecutaremos vLLM dentro de Docker, descargaremos un modelo desde Hugging Face y comprobaremos que podemos enviar una conversación al endpoint /v1/chat/completions.
Aunque la referencia utiliza DGX Spark, el flujo base resulta útil en otros servidores Linux con GPU NVIDIA. Lo que cambia entre plataformas es la capacidad de memoria, el soporte del modelo y algunos parámetros de lanzamiento.
Importante: probad primero este procedimiento en laboratorio. La publicación de un modelo implica consumo de GPU, descarga de varios gigabytes, aceptación de licencias y exposición de un servicio HTTP que debe protegerse antes de utilizarlo en producción.
Qué vamos a conseguir
- Comprobar que Docker puede acceder a la GPU NVIDIA.
- Preparar las variables del modelo, la imagen y la longitud máxima de contexto.
- Arrancar vLLM en segundo plano y publicar el puerto 8000.
- Validar el estado del servicio y revisar sus logs.
- Consumir una API compatible con OpenAI mediante curl.
- Aplicar recomendaciones básicas de seguridad, capacidad y operación.
Qué es vLLM y por qué utilizarlo
vLLM es un motor de inferencia y serving para modelos de lenguaje. Su objetivo no es entrenar el modelo, sino cargar sus pesos, gestionar la memoria disponible y atender peticiones de generación de texto de forma eficiente. Para un equipo de sistemas, podemos verlo como la capa que convierte un modelo almacenado en un servicio consumible por aplicaciones.
Dos características resultan especialmente interesantes: el procesamiento continuo de solicitudes para aprovechar mejor la GPU y la API compatible con OpenAI, que simplifica la integración con clientes y aplicaciones que ya entienden ese formato.
| Componente | Función |
| Hugging Face Hub | Repositorio de Modelo de IA. |
| Docker | Aísla dependencias y facilita repetir el despliegue. |
| NVIDIA Container Toolkit | Permite que el contenedor utilice la GPU del host. |
| vLLM | Carga el modelo y atiende solicitudes de inferencia. |
| API compatible con OpenAI | Ofrece endpoints conocidos para chat y generación. |
Arquitectura del laboratorio

En el ejemplo publicaremos el puerto 8000 del contenedor. La caché de Hugging Face se monta desde el host para no descargar de nuevo el modelo cada vez que recreemos el contenedor.
Requisitos previos
- Host Linux compatible y actualizado. El flujo containerizado de la guía de NVIDIA no aplica actualmente a Windows nativo ni a WSL.
- GPU NVIDIA con memoria suficiente para el modelo seleccionado.
- Driver NVIDIA funcional y comando nvidia-smi operativo.
- Docker Engine y NVIDIA Container Toolkit configurados.
- Espacio en disco para la imagen, la caché y los pesos del modelo.
- Cuenta y token de Hugging Face si el modelo es privado o requiere aceptar condiciones de acceso.
- Conectividad saliente hacia el registro de contenedores y Hugging Face Hub.
Decisión crítica: el modelo debe caber en la memoria disponible teniendo en cuenta pesos, KV cache y sobrecarga del runtime. No seleccionéis un modelo solo por el número de parámetros.
Paso 1. Validar Docker y el acceso a la GPU
Abrimos una terminal en el host Linux y comprobamos primero el driver:
nvidia-smi
A continuación validamos Docker:
docker ps
Si aparece un error de permisos y nuestra política nos permite administrar Docker sin sudo, añadimos el usuario al grupo docker y actualizamos la sesión de grupo:
sudo usermod -aG docker $USER
newgrp docker
docker ps
Seguridad: pertenecer al grupo docker concede privilegios elevados sobre el host. Limitad esta pertenencia a cuentas administrativas y revisadla periódicamente.
Por último, podemos realizar una comprobación específica del runtime NVIDIA con una imagen CUDA disponible en vuestro registro autorizado. La versión de la imagen debe ser compatible con el driver instalado:
docker run –rm –gpus all <imagen-cuda-autorizada> nvidia-smi
Paso 2. Elegir el modelo y preparar variables
Antes de arrancar el servicio debemos conocer el identificador del modelo en Hugging Face y revisar la configuración recomendada para nuestro hardware. Para modelos gated o privados necesitaremos un token con los permisos mínimos necesarios.
export HF_TOKEN=»hf_reemplazar_por_un_token_seguro»
export MODEL_HANDLE=»<organizacion/modelo>»
export VLLM_IMAGE=»vllm/vllm-openai:latest»
export MAX_MODEL_LEN=8192
Descargamos la imagen de vLLM:
docker pull «$VLLM_IMAGE»
Sobre la versión latest: es cómoda para laboratorio, pero en producción conviene fijar una etiqueta o digest validado. Así evitamos cambios inesperados al recrear el servicio.
Sobre MAX_MODEL_LEN: este valor representa el máximo de tokens de prompt más salida por solicitud. Un contexto mayor reserva más memoria para la KV cache. Empezad con un valor alineado con vuestro caso de uso y aumentadlo después de medir.
Paso 3. Arrancar el servidor vLLM
Lanzamos el contenedor en segundo plano. El siguiente comando es una base razonable para un modelo que cabe en una sola GPU o en las GPU visibles del nodo:
docker run -d \
–name vllm-server \
–gpus all \
–ipc host \
–ulimit memlock=-1 \
–ulimit stack=67108864 \
–entrypoint «» \
-p 8000:8000 \
-e HF_TOKEN=»$HF_TOKEN» \
-v «$HOME/.cache/huggingface/hub:/root/.cache/huggingface/hub» \
«$VLLM_IMAGE» \
vllm serve «$MODEL_HANDLE» \
–max-model-len «$MAX_MODEL_LEN» \
–gpu-memory-utilization 0.8
| Parámetro | Qué controla | Recomendación inicial |
| –gpus all | GPU disponibles para el contenedor. | Restringir dispositivos si el host es compartido. |
| –ipc host | Memoria compartida del host. | Útil para cargas de IA. Evaluar el aislamiento requerido. |
| -p 8000:8000 | Publicación del servicio HTTP. | En producción, enlazar a red privada o a un proxy. |
| –max-model-len | Contexto máximo por solicitud. | No sobredimensionar sin medir memoria. |
| –gpu-memory-utilization 0.8 | Fracción de memoria GPU que puede usar vLLM. | Deja margen inicial para estabilizar el laboratorio. |
| Montaje de caché | Persistencia de descargas del Hub. | Proteger el directorio y controlar su crecimiento. |
En un equipo dedicado puede ser posible aumentar gpu-memory-utilization hacia 0.9 o 0.95, pero no existe un valor universal. Debemos vigilar errores de memoria, concurrencia y tamaño de contexto. En DGX Spark existe memoria unificada y la presión de memoria requiere atención específica. En DGX Station, NVIDIA indica consideraciones particulares para seleccionar la GPU GB300.
Paso 4. Supervisar el arranque
La primera ejecución puede tardar más porque debe descargar el modelo. Seguimos los logs del contenedor:
docker logs -f vllm-server
Buscamos el progreso de descarga, la carga de pesos y el mensaje Application startup complete. También podemos esperar al endpoint de salud:
timeout 900 bash -c ‘until curl -sf http://localhost:8000/health >/dev/null 2>&1; do sleep 10; done’ \
|| { echo ‘El servidor no ha arrancado’; docker logs vllm-server | tail -50; exit 1; }
Resultado esperado: curl debe devolver código HTTP satisfactorio y el contenedor debe permanecer en estado Up.
docker ps –filter name=vllm-server
curl -i http://localhost:8000/health
Paso 5. Probar la API compatible con OpenAI
Una vez operativo el servicio, enviamos una conversación de prueba. El nombre del modelo debe coincidir con MODEL_HANDLE:
curl http://localhost:8000/v1/chat/completions \
-H ‘Content-Type: application/json’ \
-d ‘{
«model»: «‘»$MODEL_HANDLE»‘»,
«messages»: [
{«role»: «system», «content»: «Responde en español y de forma concisa.»},
{«role»: «user», «content»: «Explica qué es la virtualización.»}
],
«max_tokens»: 512,
«temperature»: 0.2
}’
La respuesta debería incluir un array choices y el texto generado dentro de message.content. Si el modelo utiliza razonamiento interno o un parser específico, una cuota de max_tokens demasiado baja puede terminar con finish_reason igual a length y sin respuesta final útil.
Paso 6. Integrarlo desde Python
Una ventaja de la compatibilidad con OpenAI es que podemos reutilizar clientes existentes apuntando base_url al servidor local. El valor de api_key puede ser un texto de laboratorio si no hemos configurado autenticación delante del servicio:
from openai import OpenAI
client = OpenAI(
base_url=»http://servidor-vllm:8000/v1″,
api_key=»laboratorio»
)
respuesta = client.chat.completions.create(
model=»<organizacion/modelo>»,
messages=[
{«role»: «user», «content»: «Resume las ventajas de Azure Local.»}
],
max_tokens=300,
temperature=0.2
)
print(respuesta.choices[0].message.content)
Producción: no expongáis vLLM directamente a Internet sin autenticación, TLS, control de acceso, límites de consumo y registro de actividad. Situadlo detrás de un reverse proxy, API gateway o balanceador adecuado.
Validar capacidad y rendimiento
Que una petición funcione no significa que el servicio esté dimensionado. Debemos medir al menos latencia hasta el primer token, velocidad de generación, concurrencia, uso de memoria GPU y tasa de errores. También conviene probar el tamaño de prompt real y no solo una pregunta corta.
| Métrica | Por qué importa | Qué observar |
| TTFT | Impacta en la percepción de respuesta. | Aumentos con prompts largos o cola. |
| Tokens por segundo | Mide la velocidad de generación. | Variación por modelo y concurrencia. |
| Concurrencia | Determina usuarios simultáneos. | Colas, latencia y saturación. |
| Memoria GPU | Limita modelo, contexto y KV cache. | OOM y reinicios del contenedor. |
| Errores HTTP | Revela fallos operativos. | 429, 5xx y timeouts. |
| Disco de caché | Puede crecer con varios modelos. | Capacidad y limpieza controlada. |
Errores habituales y cómo resolverlos
| Síntoma | Causa probable | Qué revisar |
| permission denied al usar Docker | El usuario no tiene acceso al socket. | Grupo docker, sesión actual y política de privilegios. |
| could not select device driver | Runtime NVIDIA ausente o mal configurado. | Driver, Container Toolkit y prueba nvidia-smi en contenedor. |
| 401 o 403 al descargar | Token inválido o licencia no aceptada. | Permisos del token y acceso al modelo en Hugging Face. |
| CUDA out of memory | Modelo, contexto o concurrencia excesivos. | Reducir MAX_MODEL_LEN, utilización o elegir otro modelo. |
| El contenedor se detiene | Error durante la carga o parámetros incompatibles. | docker logs vllm-server y receta del modelo. |
| finish_reason: length | Presupuesto de salida insuficiente. | Aumentar max_tokens o reducir el prompt. |
| Respuesta lenta en la primera petición | Carga inicial, compilación o cachés frías. | Separar warm-up de las métricas estables. |
| Puerto 8000 inaccesible | Firewall, bind, red o contenedor no operativo. | curl local, docker ps, reglas y publicación de puertos. |
Recomendaciones de hardening
- Fijar versiones de la imagen y validar actualizaciones antes de desplegarlas.
- No incluir HF_TOKEN en scripts, repositorios, imágenes ni historiales compartidos. Utilizar un gestor de secretos.
- Publicar la API únicamente en redes autorizadas y añadir TLS y autenticación.
- Aplicar cuotas, límites de concurrencia, tamaño máximo de entrada y timeouts.
- Ejecutar el servicio con la mínima superficie de privilegios compatible con el modelo.
- Registrar accesos y errores sin almacenar prompts sensibles de forma indiscriminada.
- Revisar licencias del modelo, condiciones de uso y requisitos de atribución.
- Separar entornos de laboratorio y producción, y disponer de rollback.
- Monitorizar GPU, memoria, disco, latencia y disponibilidad.
- Evaluar filtros, guardrails y políticas de datos según el caso de uso.
Operación diaria
Estos comandos cubren las acciones más habituales durante el laboratorio:
docker ps –filter name=vllm-server
docker logs –tail 100 vllm-server
docker stats vllm-server
docker restart vllm-server
docker stop vllm-server
docker rm vllm-server
Eliminar el contenedor no borra la caché montada desde el host. Para liberar espacio debemos identificar primero la ruta exacta del modelo y aplicar el procedimiento de limpieza aprobado. Evitad comandos recursivos sin validar el destino.
Checklist antes de pasar a producción
- Modelo y licencia aprobados.
- Imagen fijada por versión o digest.
- Capacidad validada con prompts y concurrencias reales.
- TLS, autenticación y autorización configurados.
- Token y secretos fuera de la línea de comandos y del repositorio.
- Monitorización, alertas y logs definidos.
- Límites de consumo y protección frente a abuso.
- Política de datos, retención y privacidad revisada.
- Procedimiento de actualización, rollback y recuperación probada.
- Documentación operativa y responsables identificados.
Para finalizar
Con vLLM podemos convertir un modelo de Hugging Face en un servicio de inferencia accesible mediante una API compatible con OpenAI. El flujo básico es sencillo: validamos Docker y la GPU, elegimos el modelo, arrancamos el contenedor, esperamos al endpoint de salud y enviamos una petición de chat.
La parte importante empieza después de la primera respuesta. En un entorno real debemos dimensionar memoria y concurrencia, proteger el endpoint, controlar secretos, respetar la licencia del modelo y monitorizar el servicio. Con estas bases, vLLM se convierte en una pieza muy útil para laboratorios de IA, asistentes internos y aplicaciones que requieren inferencia privada o cercana a los datos.
Ya sabemos cómo desplegar y servir un LLM con vLLM.
Nos vemos en el siguiente artículo.
Saludos.
