CUDA, cuDNN y NVIDIA Container Toolkit: una instalación sensata para un servidor de IA

Tiene un servidor recién montado en rack con cuatro u ocho GPU, Ubuntu 22.04 o 24.04, el controlador NVIDIA fijado (según L01), y nvidia-smi devolviendo cifras razonables. Ese es el punto de partida. Por encima se apila un conjunto incómodo —CUDA, cuDNN, el container toolkit, los runtimes, las imágenes NGC— que la mayoría de las guías despacha de pasada. Este artículo recorre el camino desde el controlador hasta el punto en el que docker run --gpus all enciende todas las tarjetas y un contenedor vLLM sirve tokens.

Con criterio sobre qué camino tomar y honesto sobre cuál de ellos duele.

La tríada controlador / runtime / toolkit

Bajo el paraguas «CUDA» se distribuyen tres cosas que la gente confunde constantemente:

Componente Dónde reside Qué hace Quién lo instala
Controlador NVIDIA Módulo del kernel + espacio de usuario Se comunica con la GPU. Es el dueño del dispositivo. Fija el estado PCIe, la potencia y las frecuencias. SO / apt / DKMS
Runtime de CUDA Bibliotecas .so en espacio de usuario Implementa la API de CUDA contra la que enlaza su aplicación. El paquete de la aplicación o el sistema
Toolkit de CUDA nvcc, cabeceras, ejemplos Compila CUDA C++ a PTX/SASS. Necesario para compilar, no para ejecutar. apt o imagen NGC

Lo que importa en tiempo de ejecución es el controlador. Un controlador admite una versión máxima del runtime de CUDA; el runtime debe ser esa o anterior. El toolkit solo hace falta si compila usted mismo código CUDA: en el 95 % de las instalaciones no necesita nvcc en el host.

Por eso funcionan los contenedores: la imagen incorpora su propio runtime de CUDA y el host solo necesita el controlador. El controlador del host es lo único que tiene que estar bien. Todo lo demás es portable.

┌─────────────────────────────────────────────────┐
│ Container: PyTorch 2.9 + CUDA 13.0 runtime      │  ← shipped in image
│ Container: vLLM + CUDA 13.0 runtime             │  ← shipped in image
├─────────────────────────────────────────────────┤
│ Host: NVIDIA driver 570.x                       │  ← only thing host needs
│ Host: nvidia-container-toolkit                  │  ← bridges driver → container
└─────────────────────────────────────────────────┘

Esta imagen marca la diferencia entre una tarde de trabajo y tres días de infierno de dependencias.

CUDA 12.x o 13.x: cuál elegir

Ambas están en uso activo en mayo de 2026. La matriz honesta para las cargas de trabajo que ejecutan los servidores Kentino:

Pila CUDA 12.4–12.8 CUDA 13.0+
PyTorch estable 2.5 / 2.6 (12.4–12.6) 2.9 / 2.10 / 2.11 (13.0+)
Wheels estables de vLLM Se siguen publicando wheels para 12.8 13.0 es el wheel predeterminado de PyPI desde la v0.20
llama.cpp Funciona en ambas Funciona en ambas
TensorRT-LLM Fijado a la imagen NGC de PyTorch (25.12 → 13.0) Nativo
Blackwell (5090, RTX Pro 6000, B70) Requiere 12.8+ como mínimo El mejor soporte
Ada (4090, L40, L4) Totalmente compatible Totalmente compatible

La opción predeterminada de Kentino es CUDA 13.0 en los equipos nuevos. Todas las GPU de la gama (5090, 4090, RTX Pro 6000 Blackwell, L40, L4) son compatibles, vLLM ya publica wheels 13.0 por defecto en PyPI y la imagen vllm/vllm-openai usa 13.0, PyTorch 2.11 (contra la que compila vLLM) está construido para 13.0 y las novedades (FlashAttention 3 en Blackwell, kernels de entrenamiento FP8) apuntan primero a 13.x.

Quédese en 12.8 solo si: reproduce un benchmark fijado, un SDK de un fabricante no ha avanzado o trabaja exclusivamente con tarjetas anteriores a Blackwell. Nada anterior a 12.4 en instalaciones nuevas: son fallos corregidos aguas arriba hace dos años.

Instalar el controlador y CUDA de forma limpia

Ubuntu 24.04 LTS (22.04 es idéntico). El camino limpio es el repositorio apt de CUDA de NVIDIA:

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update

# Driver + CUDA runtime libs. No nvcc — only what apps need to run.
sudo apt install -y cuda-drivers-570 cuda-runtime-13-0

# Optional full toolkit (nvcc, headers). Only if you compile on the host.
# sudo apt install -y cuda-toolkit-13-0

sudo reboot

Tras reiniciar, nvidia-smi debería indicar el controlador 570.x y CUDA 13.0. Trampas que conviene evitar:

  • No instale el metapaquete cuda salvo que quiera absolutamente todo. Arrastra el toolkit, los ejemplos, GDS, Nsight y fija paquetes que no quería. Elija cuda-runtime-13-0 o cuda-toolkit-13-0 de forma explícita.
  • Fije el controlador para que unattended-upgrades no pueda cambiar el módulo del kernel: sudo apt-mark hold cuda-drivers cuda-drivers-570 nvidia-driver-570 nvidia-dkms-570 libnvidia-compute-570.
  • Una sola rama de controlador a la vez. Mezclar paquetes 550 y 570 produce un sistema que arranca pero en el que nvidia-smi devuelve un error críptico de versión incompatible. La solución es apt purge '*nvidia*' '*cuda*' y empezar de cero; mejor no llegar a ese punto.

cuDNN: ¿apt o tarball?

cuDNN es la biblioteca de primitivas de aprendizaje profundo: convolución, atención, RNN. PyTorch / TensorFlow / JAX dependen de ella. A partir de la 9.x hay dos vías de instalación.

apt (recomendado):

sudo apt install -y libcudnn9-cuda-13 libcudnn9-dev-cuda-13

Se instala en /usr/lib/x86_64-linux-gnu/, lo recoge el enlazador dinámico y se integra con el gestor de paquetes, de modo que las actualizaciones de seguridad llegan solas. Solo puede instalarse una versión de cuDNN 9 para una versión de CUDA a la vez: nada de libcudnn9-cuda-12 y libcudnn9-cuda-13 en paralelo mediante apt.

Tarball:

# Fetch from the redist manifest at developer.download.nvidia.com/compute/cudnn/redist/
tar -xf cudnn-linux-x86_64-9.5.0.x_cuda13-archive.tar.xz
sudo cp -r cudnn-linux-x86_64-*/include/* /usr/local/cuda/include/
sudo cp -r cudnn-linux-x86_64-*/lib/* /usr/local/cuda/lib64/

El tarball tiene sentido para varias versiones de cuDNN en un mismo host, paquetes para entornos aislados o fijar microversiones. Para todo lo demás: apt. La documentación de NVIDIA recomienda los paquetes de la distribución siempre que sea posible; el tarball no es un instalador, es un archivo de redistribución.

Respuesta honesta: si usa contenedores NGC, no instale cuDNN en el host en absoluto. Ya viene en la imagen.

Contenedores frente a bare-metal: cuándo usar cada uno

La decisión más importante de toda la pila, y sin respuesta universal. El equilibrio, tal como se ve en equipos Kentino reales:

Dimensión Bare-metal Contenedor
Rendimiento máximo 100 % (referencia) 99–100 % (despreciable)
Tiempo de puesta en marcha Horas, y después semanas de deriva Minutos; la imagen es la especificación
Reproducibilidad Mala: el estado del host influye Excelente: la imagen está congelada
Multiinquilino Incómodo Nativo
Varias versiones de CUDA Costoso, una cada vez Trivial, una imagen por versión
Depurar rendimiento Más fácil (menos capas) Más difícil (cgroup, espacios de nombres)
Impacto de actualizar el controlador Hay que volver a probarlo todo Solo hay que volver a probar el host

La diferencia de rendimiento de CUDA entre contenedor y bare-metal es despreciable en los kernels modernos: un porcentaje de un solo dígito en el peor caso, normalmente ruido. Quien afirme lo contrario está midiendo almacenamiento o red, no cómputo de GPU.

Bare-metal: perseguir el último 2–3 % para un artículo, depurar kernels con cuda-gdb / compute-sanitizer / Nsight cuando la capa del contenedor estorba, o un único proceso que es dueño de la máquina durante años.

Contenedores (la opción predeterminada de Kentino): equipos multiusuario, PyTorch 2.5 y 2.11 en paralelo, cambiar de framework de inferencia (vLLM, SGLang, TensorRT-LLM) sin reconstruir el host, o un despliegue que puede docker pull en un servidor nuevo y tener en marcha en cinco minutos.

Para un laboratorio de robótica o un grupo de investigación: contenedores. Para un banco de pruebas con una sola carga de trabajo, bare-metal está bien.

nvidia-container-toolkit: qué es y cómo configurarlo

El toolkit es el pegamento que da a un contenedor acceso a la GPU del host. Sin él, nvidia-smi falla dentro de un contenedor. Con él, el contenedor ve el controlador, los dispositivos y un stub de runtime inyectado.

Instalación:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# Smoke test
docker run --rm --gpus all nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smi

Ese último comando debería mostrar la salida de nvidia-smi del host desde dentro del contenedor. Si lo hace, la pila está bien conectada.

Podman

Podman usa CDI:

sudo nvidia-ctk runtime configure --runtime=podman
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

podman run --rm --device=nvidia.com/gpu=all \
  nvcr.io/nvidia/pytorch:25.12-py3 nvidia-smi

Podman sin root + CDI es la configuración más limpia para equipos multiusuario en los que no quiere meter a los usuarios en el grupo docker (que equivale a root).

CDI frente al runtime clásico

Dos modos de exponer las GPU:

  • Hook de runtime nvidia clásico: lo que --gpus all lleva años usando. Docker invoca un hook que monta las bibliotecas del controlador y los dispositivos dentro del contenedor en tiempo de ejecución.
  • CDI (Container Device Interface): una especificación abierta para declarar el acceso a dispositivos. nvidia-ctk cdi generate escribe un archivo YAML que enumera las GPU como dispositivos con nombre (nvidia.com/gpu=0, nvidia.com/gpu=all); cualquier runtime compatible con CDI los consume.

Situación a mediados de 2026:

Modo Docker Podman Kubernetes (GPU Operator) Sin root
Hook de runtime Predeterminado, funciona Funciona Obsoleto Problemático
CDI Compatible Predeterminado Predeterminado desde la v25.10 Limpio

Para una instalación Docker en un solo servidor, el modo clásico sigue funcionando bien. Para cualquier cosa que toque Kubernetes, modo sin root o multiusuario, pase a CDI: es donde está invirtiendo NVIDIA. La migración no es invasiva: instale el toolkit, genere la especificación CDI y cambie --gpus all por --device=nvidia.com/gpu=all. Los dos modos pueden convivir.

Paso de GPU al contenedor: qué hace en realidad --gpus all

Cuando ejecuta docker run --gpus all my-image el hook del runtime lee NVIDIA_VISIBLE_DEVICES / NVIDIA_DRIVER_CAPABILITIES, monta /dev/nvidia* dentro del contenedor con permisos de cgroup, enlaza los archivos .so del controlador del host y establece LD_LIBRARY_PATH para que el enlazador los encuentre.

Lo que usted controla:

docker run --gpus all ...                                     # all GPUs
docker run --gpus '"device=0,1"' ...                          # by index
docker run --gpus '"device=GPU-abc123..."' ...                # by UUID
docker run --gpus all -e NVIDIA_DRIVER_CAPABILITIES=compute,utility ...
docker run --device=nvidia.com/gpu=0 --device=nvidia.com/gpu=1 ...  # CDI

Para un servidor de 8 GPU que ejecute vLLM con paralelismo tensorial, use --gpus all. Para un equipo multiinquilino en el que los usuarios reciben porciones de 4 GPU, fije por UUID: la fijación por índice se rompe si recablea.

Contenedores NGC: cuándo usarlos

El catálogo NGC de NVIDIA (nvcr.io) publica imágenes para PyTorch, TensorRT, TensorRT-LLM, Triton y otros. Son grandes (5–15 GB), pero están probadas y se distribuyen con combinaciones de cuDNN / NCCL / runtime de CUDA validadas por NVIDIA. Mayo de 2026:

Imagen Contiene
nvcr.io/nvidia/pytorch:25.12-py3 PyTorch 2.9.1 + CUDA 13.0 + cuDNN 9 + NCCL
nvcr.io/nvidia/tritonserver:25.12-py3 Triton + backends de Python / ONNX / TensorRT / vLLM
nvcr.io/nvidia/tensorrt-llm/release:0.x Compilación de TensorRT-LLM, motores optimizados, NCCL
nvcr.io/nvidia/tensorrt:25.08-py3 TensorRT C++ / Python, analizador ONNX

Las etiquetas son <year>.<month>-py3, publicadas cada mes. No son exactamente la versión original —están parcheadas y recompiladas—, pero la siguen de cerca.

Argumento honesto a favor de NGC: si ejecuta TensorRT-LLM o Triton, use la imagen NGC. El trabajo de encajar cuDNN, NCCL, TensorRT y CUDA ya está hecho. Para PyTorch o vLLM originales, las imágenes públicas (pytorch/pytorch:..., vllm/vllm-openai:...) son más pequeñas y se actualizan antes; NGC es excesivo.

MIG: por qué no se aplica a la gama de Kentino

MIG divide una sola GPU en hasta siete porciones aisladas, cada una con su propia memoria, cómputo y dominio de fallo. Resulta útil para inferencia multiinquilino: cinco usuarios reciben 1/7 de una H100 con aislamiento estricto.

MIG no está disponible en tarjetas de consumo (RTX 5090, 4090) ni en las Blackwell de estación de trabajo (RTX Pro 6000). Está limitado a los modelos de centro de datos: H100, H200, A100, B100, B200, GB200. Kentino no comercializa GPU de centro de datos en formato SXM, así que MIG no está presente en ningún equipo Kentino.

El sustituto en tarjetas de consumo o de estación de trabajo es MPS (Multi-Process Service): varios procesos comparten el planificador de cómputo de la GPU. No ofrece el aislamiento de MIG (un proceso defectuoso puede tumbar el dispositivo por falta de memoria), pero para inferencia multiinquilino de confianza funciona y no cuesta nada. Si realmente necesita MIG, necesita la línea de modelos de centro de datos a través de otro integrador.

Resolución de problemas: los errores que afectan a todo el mundo

CUDA error: no kernel image is available for execution on the device (error 209). El runtime de CUDA del contenedor no tiene kernels para la capacidad de cómputo de su GPU; suele ser una imagen de CUDA 12.4 sobre una tarjeta Blackwell (sm_120) que necesita 12.8+. Solución: una imagen más reciente, o compilar con el TORCH_CUDA_ARCH_LIST.

CUDA error: forward compatibility was attempted on non supported HW (error 100). El controlador del host es más antiguo de lo que pide el runtime del contenedor. Solución: actualice el controlador del host, e instálelo desde el repositorio de NVIDIA, no desde la versión obsoleta empaquetada por Ubuntu.

Failed to initialize NVML: Driver/library version mismatch. El controlador se actualizó mediante apt sin reiniciar; el módulo del kernel es antiguo y el espacio de usuario es nuevo. Solución: reiniciar. (Si no puede, haga rmmod de los módulos nvidia y vuelva a cargarlos con modprobe, pero reiniciar es más seguro.)

nvidia-container-cli: initialization error: nvml error: driver not loaded. El toolkit está instalado, pero el módulo del kernel del controlador no está cargado. Suele ocurrir en una instalación nueva en la que nvidia-smi ya falla en el propio host. Arregle el host y después el contenedor.

OCI runtime exec failed: ... no such file or directory en --gpus all. El hook del runtime no está registrado en Docker. Vuelva a ejecutar sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker. Si no se soluciona, revise /etc/docker/daemon.json en busca de un bloque "runtimes".

Compilar desde el código fuente frente a usar binarios precompilados

PyTorch, vLLM, llama.cpp, FlashAttention: todos tienen wheels e imágenes precompiladas. Compile desde el código fuente solo cuando necesite una función aún no publicada, trabaje con una capacidad de cómputo a la que no apuntan los wheels oficiales o compile contra CUDA / cuDNN personalizados. Un PyTorch compilado desde fuente son 30–90 minutos en EPYC sin ninguna ganancia de rendimiento, salvo que pase opciones de CMake personalizadas. Las compilaciones desde fuente son la respuesta a un problema concreto, no la opción por defecto.

Qué hacer a continuación

Para un servidor Kentino montado desde cero:

  1. Instale Ubuntu LTS y fije el controlador según L01.
  2. Instale cuda-runtime-13-0 (omita el toolkit completo salvo que vaya a compilar).
  3. Instale libcudnn9-cuda-13 mediante apt, u omítalo si trabaja totalmente con contenedores.
  4. Instale nvidia-container-toolkit, configure Docker y haga una prueba rápida con docker run --rm --gpus all nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smi.
  5. Descargue la imagen de su carga de trabajo: vllm/vllm-openai:latest, nvcr.io/nvidia/pytorch:25.12-py3, o nvcr.io/nvidia/tritonserver:25.12-py3.
  6. Ejecute con --gpus all, o migre ya a CDI si tiene a la vista entornos multiusuario, sin root o Kubernetes.
  7. apt-mark hold el controlador y no ejecute nunca apt-get dist-upgrade en un servidor que funciona.

L03 trata el ajuste del kernel, L04 trata la elección de sistema de archivos para el almacenamiento de modelos y el rendimiento de los puntos de control, y L05 trata la pila de monitorización (Prometheus, Grafana, DCGM-exporter). Si hoy nvidia-smi se ejecuta dentro de un contenedor en su equipo, ya ha recorrido el 80 % del camino.


Esto forma parte de la Kentino Wiki, una serie de referencia sobre cómputo de IA, robótica y los sistemas que los conectan. Comentarios y correcciones son bienvenidos en info@kentino.com.